1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20// common.c -- misc functions used in client and server
21
22#include <ctype.h>
23
24#ifdef SERVERONLY
25#include "qwsvdef.h"
26#else
27#include "quakedef.h"
28#endif
29
30#define MAX_NUM_ARGVS	50
31#define NUM_SAFE_ARGVS	6
32
33usercmd_t nullcmd; // guarenteed to be zero
34
35static char	*largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
36static char	*argvdummy = " ";
37
38static char	*safeargvs[NUM_SAFE_ARGVS] =
39	{"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse"};
40
41cvar_t	registered = CVAR2("registered","0");
42
43qboolean	com_modified;	// set true if using non-id files
44
45int		static_registered = 1;	// only for startup check, then set
46
47qboolean		msg_suppress_1 = 0;
48
49void COM_InitFilesystem (void);
50void COM_Path_f (void);
51
52
53// if a packfile directory differs from this, it is assumed to be hacked
54#define	PAK0_COUNT		339
55#define	PAK0_CRC		52883
56
57qboolean		standard_quake = true, rogue, hipnotic;
58
59char	gamedirfile[MAX_OSPATH];
60
61// this graphic needs to be in the pak file to use registered features
62unsigned short pop[] =
63{
64 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
65,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000
66,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000
67,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600
68,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563
69,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564
70,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564
71,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563
72,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500
73,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200
74,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000
75,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000
76,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000
77,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000
78,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000
79,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000
80};
81
82/*
83
84
85All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources.
86
87The "base directory" is the path to the directory holding the quake.exe and all game directories.  The sys_* files pass this to host_init in quakeparms_t->basedir.  This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory.  The base directory is
88only used during filesystem initialization.
89
90The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to.  This can be overridden with the "-game" command line parameter.  The game directory can never be changed while quake is executing.  This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't.
91
92The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines.  If there is a cache directory
93specified, when a file is found by the normal search path, it will be mirrored
94into the cache directory, then opened there.
95
96*/
97
98//============================================================================
99
100
101// ClearLink is used for new headnodes
102void ClearLink (link_t *l)
103{
104	l->prev = l->next = l;
105}
106
107void RemoveLink (link_t *l)
108{
109	l->next->prev = l->prev;
110	l->prev->next = l->next;
111}
112
113void InsertLinkBefore (link_t *l, link_t *before)
114{
115	l->next = before;
116	l->prev = before->prev;
117	l->prev->next = l;
118	l->next->prev = l;
119}
120void InsertLinkAfter (link_t *l, link_t *after)
121{
122	l->next = after->next;
123	l->prev = after;
124	l->prev->next = l;
125	l->next->prev = l;
126}
127
128/*
129============================================================================
130
131					LIBRARY REPLACEMENT FUNCTIONS
132
133============================================================================
134*/
135
136#if 0
137void Q_memset (void *dest, int fill, int count)
138{
139	int		i;
140
141	if ( (((long)dest | count) & 3) == 0)
142	{
143		count >>= 2;
144		fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
145		for (i=0 ; i<count ; i++)
146			((int *)dest)[i] = fill;
147	}
148	else
149		for (i=0 ; i<count ; i++)
150			((byte *)dest)[i] = fill;
151}
152
153void Q_memcpy (void *dest, void *src, int count)
154{
155	int		i;
156
157	if (( ( (long)dest | (long)src | count) & 3) == 0 )
158	{
159		count>>=2;
160		for (i=0 ; i<count ; i++)
161			((int *)dest)[i] = ((int *)src)[i];
162	}
163	else
164		for (i=0 ; i<count ; i++)
165			((byte *)dest)[i] = ((byte *)src)[i];
166}
167
168int Q_memcmp (void *m1, void *m2, int count)
169{
170	while(count)
171	{
172		count--;
173		if (((byte *)m1)[count] != ((byte *)m2)[count])
174			return -1;
175	}
176	return 0;
177}
178
179void Q_strcpy (char *dest, char *src)
180{
181	while (*src)
182	{
183		*dest++ = *src++;
184	}
185	*dest++ = 0;
186}
187
188void Q_strncpy (char *dest, char *src, int count)
189{
190	while (*src && count--)
191	{
192		*dest++ = *src++;
193	}
194	if (count)
195		*dest++ = 0;
196}
197
198int Q_strlen (char *str)
199{
200	int		count;
201
202	count = 0;
203	while (str[count])
204		count++;
205
206	return count;
207}
208
209char *Q_strrchr(char *s, char c)
210{
211    int len = Q_strlen(s);
212    s += len;
213    while (len--)
214        if (*--s == c) return s;
215    return 0;
216}
217
218void Q_strcat (char *dest, char *src)
219{
220	dest += Q_strlen(dest);
221	Q_strcpy (dest, src);
222}
223
224int Q_strcmp (char *s1, char *s2)
225{
226	while (1)
227	{
228		if (*s1 != *s2)
229			return -1;		// strings not equal
230		if (!*s1)
231			return 0;		// strings are equal
232		s1++;
233		s2++;
234	}
235
236	return -1;
237}
238
239int Q_strncmp (char *s1, char *s2, int count)
240{
241	while (1)
242	{
243		if (!count--)
244			return 0;
245		if (*s1 != *s2)
246			return -1;		// strings not equal
247		if (!*s1)
248			return 0;		// strings are equal
249		s1++;
250		s2++;
251	}
252
253	return -1;
254}
255
256int Q_strncasecmp (char *s1, char *s2, int n)
257{
258	int		c1, c2;
259
260	while (1)
261	{
262		c1 = *s1++;
263		c2 = *s2++;
264
265		if (!n--)
266			return 0;		// strings are equal until end point
267
268		if (c1 != c2)
269		{
270			if (c1 >= 'a' && c1 <= 'z')
271				c1 -= ('a' - 'A');
272			if (c2 >= 'a' && c2 <= 'z')
273				c2 -= ('a' - 'A');
274			if (c1 != c2)
275				return -1;		// strings not equal
276		}
277		if (!c1)
278			return 0;		// strings are equal
279//		s1++;
280//		s2++;
281	}
282
283	return -1;
284}
285
286int Q_strcasecmp (char *s1, char *s2)
287{
288	return Q_strncasecmp (s1, s2, 99999);
289}
290
291#endif
292
293int Q_atoi (char *str)
294{
295	int		val;
296	int		sign;
297	int		c;
298
299	if (*str == '-')
300	{
301		sign = -1;
302		str++;
303	}
304	else
305		sign = 1;
306
307	val = 0;
308
309//
310// check for hex
311//
312	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
313	{
314		str += 2;
315		while (1)
316		{
317			c = *str++;
318			if (c >= '0' && c <= '9')
319				val = (val<<4) + c - '0';
320			else if (c >= 'a' && c <= 'f')
321				val = (val<<4) + c - 'a' + 10;
322			else if (c >= 'A' && c <= 'F')
323				val = (val<<4) + c - 'A' + 10;
324			else
325				return val*sign;
326		}
327	}
328
329//
330// check for character
331//
332	if (str[0] == '\'')
333	{
334		return sign * str[1];
335	}
336
337//
338// assume decimal
339//
340	while (1)
341	{
342		c = *str++;
343		if (c <'0' || c > '9')
344			return val*sign;
345		val = val*10 + c - '0';
346	}
347
348	return 0;
349}
350
351
352float Q_atof (char *str)
353{
354	double	val;
355	int		sign;
356	int		c;
357	int		decimal, total;
358
359	if (*str == '-')
360	{
361		sign = -1;
362		str++;
363	}
364	else
365		sign = 1;
366
367	val = 0;
368
369//
370// check for hex
371//
372	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
373	{
374		str += 2;
375		while (1)
376		{
377			c = *str++;
378			if (c >= '0' && c <= '9')
379				val = (val*16) + c - '0';
380			else if (c >= 'a' && c <= 'f')
381				val = (val*16) + c - 'a' + 10;
382			else if (c >= 'A' && c <= 'F')
383				val = (val*16) + c - 'A' + 10;
384			else
385				return val*sign;
386		}
387	}
388
389//
390// check for character
391//
392	if (str[0] == '\'')
393	{
394		return sign * str[1];
395	}
396
397//
398// assume decimal
399//
400	decimal = -1;
401	total = 0;
402	while (1)
403	{
404		c = *str++;
405		if (c == '.')
406		{
407			decimal = total;
408			continue;
409		}
410		if (c <'0' || c > '9')
411			break;
412		val = val*10 + c - '0';
413		total++;
414	}
415
416	if (decimal == -1)
417		return val*sign;
418	while (total > decimal)
419	{
420		val /= 10;
421		total--;
422	}
423
424	return val*sign;
425}
426
427/*
428============================================================================
429
430					BYTE ORDER FUNCTIONS
431
432============================================================================
433*/
434
435qboolean	bigendien;
436
437short	(*BigShort) (short l);
438short	(*LittleShort) (short l);
439int	(*BigLong) (int l);
440int	(*LittleLong) (int l);
441float	(*BigFloat) (float l);
442float	(*LittleFloat) (float l);
443
444short   ShortSwap (short l)
445{
446	byte    b1,b2;
447
448	b1 = l&255;
449	b2 = (l>>8)&255;
450
451	return (b1<<8) + b2;
452}
453
454short	ShortNoSwap (short l)
455{
456	return l;
457}
458
459int    LongSwap (int l)
460{
461	byte    b1,b2,b3,b4;
462
463	b1 = l&255;
464	b2 = (l>>8)&255;
465	b3 = (l>>16)&255;
466	b4 = (l>>24)&255;
467
468	return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
469}
470
471int	LongNoSwap (int l)
472{
473	return l;
474}
475
476float FloatSwap (float f)
477{
478	union
479	{
480		float	f;
481		byte	b[4];
482	} dat1, dat2;
483
484
485	dat1.f = f;
486	dat2.b[0] = dat1.b[3];
487	dat2.b[1] = dat1.b[2];
488	dat2.b[2] = dat1.b[1];
489	dat2.b[3] = dat1.b[0];
490	return dat2.f;
491}
492
493float FloatNoSwap (float f)
494{
495	return f;
496}
497
498/*
499==============================================================================
500
501			MESSAGE IO FUNCTIONS
502
503Handles byte ordering and avoids alignment errors
504==============================================================================
505*/
506
507//
508// writing functions
509//
510
511void MSG_WriteChar (sizebuf_t *sb, int c)
512{
513	byte	*buf;
514
515#ifdef PARANOID
516	if (c < -128 || c > 127)
517		Sys_Error ("MSG_WriteChar: range error");
518#endif
519
520	buf = SZ_GetSpace (sb, 1);
521	buf[0] = c;
522}
523
524void MSG_WriteByte (sizebuf_t *sb, int c)
525{
526	byte	*buf;
527
528#ifdef PARANOID
529	if (c < 0 || c > 255)
530		Sys_Error ("MSG_WriteByte: range error");
531#endif
532
533	buf = SZ_GetSpace (sb, 1);
534	buf[0] = c;
535}
536
537void MSG_WriteShort (sizebuf_t *sb, int c)
538{
539	byte	*buf;
540
541#ifdef PARANOID
542	if (c < ((short)0x8000) || c > (short)0x7fff)
543		Sys_Error ("MSG_WriteShort: range error");
544#endif
545
546	buf = SZ_GetSpace (sb, 2);
547	buf[0] = c&0xff;
548	buf[1] = c>>8;
549}
550
551void MSG_WriteLong (sizebuf_t *sb, int c)
552{
553	byte	*buf;
554
555	buf = SZ_GetSpace (sb, 4);
556	buf[0] = c&0xff;
557	buf[1] = (c>>8)&0xff;
558	buf[2] = (c>>16)&0xff;
559	buf[3] = c>>24;
560}
561
562void MSG_WriteFloat (sizebuf_t *sb, float f)
563{
564	union
565	{
566		float	f;
567		int	l;
568	} dat;
569
570
571	dat.f = f;
572	dat.l = LittleLong (dat.l);
573
574	SZ_Write (sb, &dat.l, 4);
575}
576
577void MSG_WriteString (sizebuf_t *sb, char *s)
578{
579	if (!s)
580		SZ_Write (sb, "", 1);
581	else
582		SZ_Write (sb, s, Q_strlen(s)+1);
583}
584
585void MSG_WriteCoord (sizebuf_t *sb, float f)
586{
587	MSG_WriteShort (sb, (int)(f*8));
588}
589
590void MSG_WriteAngle (sizebuf_t *sb, float f)
591{
592	MSG_WriteByte (sb, (int)(f*256/360) & 255);
593}
594
595void MSG_WriteAngle16 (sizebuf_t *sb, float f)
596{
597	MSG_WriteShort (sb, (int)(f*65536/360) & 65535);
598}
599
600void MSG_WriteDeltaUsercmd (sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd)
601{
602	int		bits;
603
604//
605// send the movement message
606//
607	bits = 0;
608	if (cmd->angles[0] != from->angles[0])
609		bits |= CM_ANGLE1;
610	if (cmd->angles[1] != from->angles[1])
611		bits |= CM_ANGLE2;
612	if (cmd->angles[2] != from->angles[2])
613		bits |= CM_ANGLE3;
614	if (cmd->forwardmove != from->forwardmove)
615		bits |= CM_FORWARD;
616	if (cmd->sidemove != from->sidemove)
617		bits |= CM_SIDE;
618	if (cmd->upmove != from->upmove)
619		bits |= CM_UP;
620	if (cmd->buttons != from->buttons)
621		bits |= CM_BUTTONS;
622	if (cmd->impulse != from->impulse)
623		bits |= CM_IMPULSE;
624
625    MSG_WriteByte (buf, bits);
626
627	if (bits & CM_ANGLE1)
628		MSG_WriteAngle16 (buf, cmd->angles[0]);
629	if (bits & CM_ANGLE2)
630		MSG_WriteAngle16 (buf, cmd->angles[1]);
631	if (bits & CM_ANGLE3)
632		MSG_WriteAngle16 (buf, cmd->angles[2]);
633
634	if (bits & CM_FORWARD)
635		MSG_WriteShort (buf, cmd->forwardmove);
636	if (bits & CM_SIDE)
637	  	MSG_WriteShort (buf, cmd->sidemove);
638	if (bits & CM_UP)
639		MSG_WriteShort (buf, cmd->upmove);
640
641 	if (bits & CM_BUTTONS)
642	  	MSG_WriteByte (buf, cmd->buttons);
643 	if (bits & CM_IMPULSE)
644	    MSG_WriteByte (buf, cmd->impulse);
645	MSG_WriteByte (buf, cmd->msec);
646}
647
648
649//
650// reading functions
651//
652int			msg_readcount;
653qboolean	msg_badread;
654
655void MSG_BeginReading (void)
656{
657	msg_readcount = 0;
658	msg_badread = false;
659}
660
661int MSG_GetReadCount(void)
662{
663	return msg_readcount;
664}
665
666// returns -1 and sets msg_badread if no more characters are available
667int MSG_ReadChar (void)
668{
669	int	c;
670
671	if (msg_readcount+1 > net_message.cursize)
672	{
673		msg_badread = true;
674		return -1;
675	}
676
677	c = (signed char)net_message.data[msg_readcount];
678	msg_readcount++;
679
680	return c;
681}
682
683int MSG_ReadByte (void)
684{
685	int	c;
686
687	if (msg_readcount+1 > net_message.cursize)
688	{
689		msg_badread = true;
690		return -1;
691	}
692
693	c = (unsigned char)net_message.data[msg_readcount];
694	msg_readcount++;
695
696	return c;
697}
698
699int MSG_ReadShort (void)
700{
701	int	c;
702
703	if (msg_readcount+2 > net_message.cursize)
704	{
705		msg_badread = true;
706		return -1;
707	}
708
709	c = (short)(net_message.data[msg_readcount]
710	+ (net_message.data[msg_readcount+1]<<8));
711
712	msg_readcount += 2;
713
714	return c;
715}
716
717int MSG_ReadLong (void)
718{
719	int	c;
720
721	if (msg_readcount+4 > net_message.cursize)
722	{
723		msg_badread = true;
724		return -1;
725	}
726
727	c = net_message.data[msg_readcount]
728	+ (net_message.data[msg_readcount+1]<<8)
729	+ (net_message.data[msg_readcount+2]<<16)
730	+ (net_message.data[msg_readcount+3]<<24);
731
732	msg_readcount += 4;
733
734	return c;
735}
736
737float MSG_ReadFloat (void)
738{
739	union
740	{
741		byte	b[4];
742		float	f;
743		int	l;
744	} dat;
745
746	dat.b[0] =	net_message.data[msg_readcount];
747	dat.b[1] =	net_message.data[msg_readcount+1];
748	dat.b[2] =	net_message.data[msg_readcount+2];
749	dat.b[3] =	net_message.data[msg_readcount+3];
750	msg_readcount += 4;
751
752	dat.l = LittleLong (dat.l);
753
754	return dat.f;
755}
756
757char *MSG_ReadString (void)
758{
759	static char	string[2048];
760	int		l,c;
761
762	l = 0;
763	do
764	{
765		c = MSG_ReadChar ();
766		if (c == -1 || c == 0)
767			break;
768		string[l] = c;
769		l++;
770	} while (l < (int) sizeof(string)-1);
771
772	string[l] = 0;
773
774	return string;
775}
776
777char *MSG_ReadStringLine (void)
778{
779	static char	string[2048];
780	int		l,c;
781
782	l = 0;
783	do
784	{
785		c = MSG_ReadChar ();
786		if (c == -1 || c == 0 || c == '\n')
787			break;
788		string[l] = c;
789		l++;
790	} while (l < (int) sizeof(string)-1);
791
792	string[l] = 0;
793
794	return string;
795}
796
797float MSG_ReadCoord (void)
798{
799	return MSG_ReadShort() * (1.0/8);
800}
801
802float MSG_ReadAngle (void)
803{
804	return MSG_ReadChar() * (360.0/256);
805}
806
807float MSG_ReadAngle16 (void)
808{
809	return MSG_ReadShort() * (360.0/65536);
810}
811
812void MSG_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move)
813{
814	int bits;
815
816	memcpy (move, from, sizeof(*move));
817
818	bits = MSG_ReadByte ();
819
820// read current angles
821	if (bits & CM_ANGLE1)
822		move->angles[0] = MSG_ReadAngle16 ();
823	if (bits & CM_ANGLE2)
824		move->angles[1] = MSG_ReadAngle16 ();
825	if (bits & CM_ANGLE3)
826		move->angles[2] = MSG_ReadAngle16 ();
827
828// read movement
829	if (bits & CM_FORWARD)
830		move->forwardmove = MSG_ReadShort ();
831	if (bits & CM_SIDE)
832		move->sidemove = MSG_ReadShort ();
833	if (bits & CM_UP)
834		move->upmove = MSG_ReadShort ();
835
836// read buttons
837	if (bits & CM_BUTTONS)
838		move->buttons = MSG_ReadByte ();
839
840	if (bits & CM_IMPULSE)
841		move->impulse = MSG_ReadByte ();
842
843// read time to run command
844	move->msec = MSG_ReadByte ();
845}
846
847
848//===========================================================================
849
850void SZ_Clear (sizebuf_t *buf)
851{
852	buf->cursize = 0;
853	buf->overflowed = false;
854}
855
856void *SZ_GetSpace (sizebuf_t *buf, int length)
857{
858	void	*data;
859
860	if (buf->cursize + length > buf->maxsize)
861	{
862		if (!buf->allowoverflow)
863			Sys_Error ("SZ_GetSpace: overflow without allowoverflow set (%d)", buf->maxsize);
864
865		if (length > buf->maxsize)
866			Sys_Error ("SZ_GetSpace: %i is > full buffer size", length);
867
868		Sys_Printf ("SZ_GetSpace: overflow\n");	// because Con_Printf may be redirected
869		SZ_Clear (buf);
870		buf->overflowed = true;
871	}
872
873	data = buf->data + buf->cursize;
874	buf->cursize += length;
875
876	return data;
877}
878
879void SZ_Write (sizebuf_t *buf, void *data, int length)
880{
881	Q_memcpy (SZ_GetSpace(buf,length),data,length);
882}
883
884void SZ_Print (sizebuf_t *buf, char *data)
885{
886	int		len;
887
888	len = Q_strlen(data)+1;
889
890	if (!buf->cursize || buf->data[buf->cursize-1])
891		Q_memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
892	else
893		Q_memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
894}
895
896
897//============================================================================
898
899
900/*
901============
902COM_SkipPath
903============
904*/
905char *COM_SkipPath (char *pathname)
906{
907	char	*last;
908
909	last = pathname;
910	while (*pathname)
911	{
912		if (*pathname=='/')
913			last = pathname+1;
914		pathname++;
915	}
916	return last;
917}
918
919/*
920============
921COM_StripExtension
922============
923*/
924void COM_StripExtension (char *in, char *out)
925{
926	while (*in && *in != '.')
927		*out++ = *in++;
928	*out = 0;
929}
930
931/*
932============
933COM_FileExtension
934============
935*/
936char *COM_FileExtension (char *in)
937{
938	static char exten[8];
939	int		i;
940
941	while (*in && *in != '.')
942		in++;
943	if (!*in)
944		return "";
945	in++;
946	for (i=0 ; i<7 && *in ; i++,in++)
947		exten[i] = *in;
948	exten[i] = 0;
949	return exten;
950}
951
952/*
953============
954COM_FileBase
955============
956*/
957void COM_FileBase (char *in, char *out)
958{
959	char *s, *s2;
960
961	s = in + strlen(in) - 1;
962
963	while (s != in && *s != '.')
964		s--;
965
966	for (s2 = s ; *s2 && *s2 != '/' ; s2--)
967	;
968
969	if (s-s2 < 2)
970		strcpy (out,"?model?");
971	else
972	{
973		s--;
974		strncpy (out,s2+1, s-s2);
975		out[s-s2] = 0;
976	}
977}
978
979
980/*
981==================
982COM_DefaultExtension
983==================
984*/
985void COM_DefaultExtension (char *path, char *extension)
986{
987	char    *src;
988//
989// if path doesn't have a .EXT, append extension
990// (extension should include the .)
991//
992	src = path + strlen(path) - 1;
993
994	while (*src != '/' && src != path)
995	{
996		if (*src == '.')
997			return;                 // it has an extension
998		src--;
999	}
1000
1001	strcat (path, extension);
1002}
1003
1004//============================================================================
1005
1006char		com_token[1024];
1007int		com_argc;
1008char	**com_argv;
1009
1010
1011/*
1012==============
1013COM_Parse
1014
1015Parse a token out of a string
1016==============
1017*/
1018char *COM_Parse (char *data)
1019{
1020	int		c;
1021	int		len;
1022
1023	len = 0;
1024	com_token[0] = 0;
1025
1026	if (!data)
1027		return NULL;
1028
1029// skip whitespace
1030skipwhite:
1031	while ( (c = *data) <= ' ')
1032	{
1033		if (c == 0)
1034			return NULL;			// end of file;
1035		data++;
1036	}
1037
1038// skip // comments
1039	if (c=='/' && data[1] == '/')
1040	{
1041		while (*data && *data != '\n')
1042			data++;
1043		goto skipwhite;
1044	}
1045
1046
1047// handle quoted strings specially
1048	if (c == '\"')
1049	{
1050		data++;
1051		while (1)
1052		{
1053			c = *data++;
1054			if (c=='\"' || !c)
1055			{
1056				com_token[len] = 0;
1057				return data;
1058			}
1059			com_token[len] = c;
1060			len++;
1061		}
1062	}
1063
1064// parse a regular word
1065	do
1066	{
1067		com_token[len] = c;
1068		data++;
1069		len++;
1070		c = *data;
1071	} while (c>32);
1072
1073	com_token[len] = 0;
1074	return data;
1075}
1076
1077
1078/*
1079================
1080COM_CheckParm
1081
1082Returns the position (1 to argc-1) in the program's argument list
1083where the given parameter apears, or 0 if not present
1084================
1085*/
1086int COM_CheckParm (char *parm)
1087{
1088	int		i;
1089
1090	for (i=1 ; i<com_argc ; i++)
1091	{
1092		if (!com_argv[i])
1093			continue;		// NEXTSTEP sometimes clears appkit vars.
1094		if (!Q_strcmp (parm,com_argv[i]))
1095			return i;
1096	}
1097
1098	return 0;
1099}
1100
1101/*
1102================
1103COM_CheckRegistered
1104
1105Looks for the pop.txt file and verifies it.
1106Sets the "registered" cvar.
1107Immediately exits out if an alternate game was attempted to be started without
1108being registered.
1109================
1110*/
1111void COM_CheckRegistered (void)
1112{
1113	FILE		*h;
1114	unsigned short	check[128];
1115	int			i;
1116
1117	COM_FOpenFile("gfx/pop.lmp", &h);
1118	static_registered = 0;
1119
1120	if (!h)
1121	{
1122		Con_Printf ("Playing shareware version.\n");
1123#if 0
1124#ifndef SERVERONLY
1125// FIXME DEBUG -- only temporary
1126		if (com_modified)
1127			Sys_Error ("You must have the registered version to play QuakeWorld");
1128#endif
1129#endif
1130		return;
1131	}
1132
1133	fread (check, 1, sizeof(check), h);
1134	fclose (h);
1135
1136	for (i=0 ; i<128 ; i++)
1137		if (pop[i] != (unsigned short)BigShort (check[i]))
1138			Sys_Error ("Corrupted data file.");
1139
1140	Cvar_Set ("registered", "1");
1141	static_registered = 1;
1142	Con_Printf ("Playing registered version.\n");
1143}
1144
1145
1146
1147/*
1148================
1149COM_InitArgv
1150================
1151*/
1152void COM_InitArgv (int argc, char **argv)
1153{
1154	qboolean	safe;
1155	int			i;
1156
1157	safe = false;
1158
1159	for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1160		 com_argc++)
1161	{
1162		largv[com_argc] = argv[com_argc];
1163		if (!Q_strcmp ("-safe", argv[com_argc]))
1164			safe = true;
1165	}
1166
1167	if (safe)
1168	{
1169	// force all the safe-mode switches. Note that we reserved extra space in
1170	// case we need to add these, so we don't need an overflow check
1171		for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1172		{
1173			largv[com_argc] = safeargvs[i];
1174			com_argc++;
1175		}
1176	}
1177
1178	largv[com_argc] = argvdummy;
1179	com_argv = largv;
1180}
1181
1182/*
1183================
1184COM_AddParm
1185
1186Adds the given string at the end of the current argument list
1187================
1188*/
1189void COM_AddParm (char *parm)
1190{
1191	largv[com_argc++] = parm;
1192}
1193
1194
1195/*
1196================
1197COM_Init
1198================
1199*/
1200void COM_Init (void)
1201{
1202	byte	swaptest[2] = {1,0};
1203
1204// set the byte swapping variables in a portable manner
1205	if ( *(short *)swaptest == 1)
1206	{
1207		bigendien = false;
1208		BigShort = ShortSwap;
1209		LittleShort = ShortNoSwap;
1210		BigLong = LongSwap;
1211		LittleLong = LongNoSwap;
1212		BigFloat = FloatSwap;
1213		LittleFloat = FloatNoSwap;
1214	}
1215	else
1216	{
1217		bigendien = true;
1218		BigShort = ShortNoSwap;
1219		LittleShort = ShortSwap;
1220		BigLong = LongNoSwap;
1221		LittleLong = LongSwap;
1222		BigFloat = FloatNoSwap;
1223		LittleFloat = FloatSwap;
1224	}
1225
1226	Cvar_RegisterVariable (&registered);
1227	Cmd_AddCommand ("path", COM_Path_f);
1228
1229	COM_InitFilesystem ();
1230	COM_CheckRegistered ();
1231}
1232
1233
1234/*
1235============
1236va
1237
1238does a varargs printf into a temp buffer, so I don't need to have
1239varargs versions of all text functions.
1240FIXME: make this buffer size safe someday
1241============
1242*/
1243char	*va(char *format, ...)
1244{
1245	va_list		argptr;
1246	static char		string[1024];
1247
1248	va_start (argptr, format);
1249	vsprintf (string, format,argptr);
1250	va_end (argptr);
1251
1252	return string;
1253}
1254
1255
1256/// just for debugging
1257int	memsearch (byte *start, int count, int search)
1258{
1259	int		i;
1260
1261	for (i=0 ; i<count ; i++)
1262		if (start[i] == search)
1263			return i;
1264	return -1;
1265}
1266
1267/*
1268=============================================================================
1269
1270QUAKE FILESYSTEM
1271
1272=============================================================================
1273*/
1274
1275int	com_filesize;
1276
1277
1278//
1279// in memory
1280//
1281
1282typedef struct
1283{
1284	char	name[MAX_QPATH];
1285	int		filepos, filelen;
1286} packfile_t;
1287
1288typedef struct pack_s
1289{
1290	char	filename[MAX_OSPATH];
1291	FILE	*handle;
1292	int		numfiles;
1293	packfile_t	*files;
1294} pack_t;
1295
1296//
1297// on disk
1298//
1299typedef struct
1300{
1301	char	name[56];
1302	int		filepos, filelen;
1303} dpackfile_t;
1304
1305typedef struct
1306{
1307	char	id[4];
1308	int		dirofs;
1309	int		dirlen;
1310} dpackheader_t;
1311
1312#define	MAX_FILES_IN_PACK	2048
1313
1314char	com_gamedir[MAX_OSPATH];
1315char	com_basedir[MAX_OSPATH];
1316
1317typedef struct searchpath_s
1318{
1319	char	filename[MAX_OSPATH];
1320	pack_t	*pack;		// only one of filename / pack will be used
1321	struct searchpath_s *next;
1322} searchpath_t;
1323
1324searchpath_t	*com_searchpaths;
1325searchpath_t	*com_base_searchpaths;	// without gamedirs
1326
1327/*
1328================
1329COM_filelength
1330================
1331*/
1332int COM_filelength (FILE *f)
1333{
1334	int		pos;
1335	int		end;
1336
1337	pos = ftell (f);
1338	fseek (f, 0, SEEK_END);
1339	end = ftell (f);
1340	fseek (f, pos, SEEK_SET);
1341
1342	return end;
1343}
1344
1345int COM_FileOpenRead (char *path, FILE **hndl)
1346{
1347	FILE	*f;
1348
1349	f = fopen(path, "rb");
1350	if (!f)
1351	{
1352		*hndl = NULL;
1353		return -1;
1354	}
1355	*hndl = f;
1356
1357	return COM_filelength(f);
1358}
1359
1360/*
1361============
1362COM_Path_f
1363
1364============
1365*/
1366void COM_Path_f (void)
1367{
1368	searchpath_t	*s;
1369
1370	Con_Printf ("Current search path:\n");
1371	for (s=com_searchpaths ; s ; s=s->next)
1372	{
1373		if (s == com_base_searchpaths)
1374			Con_Printf ("----------\n");
1375		if (s->pack)
1376			Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1377		else
1378			Con_Printf ("%s\n", s->filename);
1379	}
1380}
1381
1382/*
1383============
1384COM_WriteFile
1385
1386The filename will be prefixed by the current game directory
1387============
1388*/
1389void COM_WriteFile (char *filename, void *data, int len)
1390{
1391	FILE	*f;
1392	char	name[MAX_OSPATH];
1393
1394	sprintf (name, "%s/%s", com_gamedir, filename);
1395
1396	f = fopen (name, "wb");
1397	if (!f) {
1398		Sys_mkdir(com_gamedir);
1399		f = fopen (name, "wb");
1400		if (!f)
1401			Sys_Error ("Error opening %s", filename);
1402	}
1403
1404	Sys_Printf ("COM_WriteFile: %s\n", name);
1405	fwrite (data, 1, len, f);
1406	fclose (f);
1407}
1408
1409
1410/*
1411============
1412COM_CreatePath
1413
1414Only used for CopyFile and download
1415============
1416*/
1417void	COM_CreatePath (char *path)
1418{
1419	char	*ofs;
1420
1421	for (ofs = path+1 ; *ofs ; ofs++)
1422	{
1423		if (*ofs == '/')
1424		{	// create the directory
1425			*ofs = 0;
1426			Sys_mkdir (path);
1427			*ofs = '/';
1428		}
1429	}
1430}
1431
1432
1433/*
1434===========
1435COM_CopyFile
1436
1437Copies a file over from the net to the local cache, creating any directories
1438needed.  This is for the convenience of developers using ISDN from home.
1439===========
1440*/
1441void COM_CopyFile (char *netpath, char *cachepath)
1442{
1443	FILE	*in, *out;
1444	int		remaining, count;
1445	char	buf[4096];
1446
1447	remaining = COM_FileOpenRead (netpath, &in);
1448	COM_CreatePath (cachepath);	// create directories up to the cache file
1449	out = fopen(cachepath, "wb");
1450	if (!out)
1451		Sys_Error ("Error opening %s", cachepath);
1452
1453	while (remaining)
1454	{
1455		if (remaining < (int) sizeof(buf))
1456			count = remaining;
1457		else
1458			count = sizeof(buf);
1459		fread (buf, 1, count, in);
1460		fwrite (buf, 1, count, out);
1461		remaining -= count;
1462	}
1463
1464	fclose (in);
1465	fclose (out);
1466}
1467
1468/*
1469===========
1470COM_FindFile
1471
1472Finds the file in the search path.
1473Sets com_filesize and one of handle or file
1474===========
1475*/
1476int file_from_pak; // global indicating file came from pack file ZOID
1477
1478int COM_FOpenFile (char *filename, FILE **file)
1479{
1480	searchpath_t	*search;
1481	char		netpath[MAX_OSPATH];
1482	pack_t		*pak;
1483	int			i;
1484	int			findtime;
1485
1486	file_from_pak = 0;
1487
1488//
1489// search through the path, one element at a time
1490//
1491	for (search = com_searchpaths ; search ; search = search->next)
1492	{
1493	// is the element a pak file?
1494		if (search->pack)
1495		{
1496		// look through all the pak file elements
1497			pak = search->pack;
1498			for (i=0 ; i<pak->numfiles ; i++)
1499				if (!strcmp (pak->files[i].name, filename))
1500				{	// found it!
1501					Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename);
1502				// open a new file on the pakfile
1503					*file = fopen (pak->filename, "rb");
1504					if (!*file)
1505						Sys_Error ("Couldn't reopen %s", pak->filename);
1506					fseek (*file, pak->files[i].filepos, SEEK_SET);
1507					com_filesize = pak->files[i].filelen;
1508					file_from_pak = 1;
1509					return com_filesize;
1510				}
1511		}
1512		else
1513		{
1514	// check a file in the directory tree
1515			if (!static_registered)
1516			{	// if not a registered version, don't ever go beyond base
1517				if ( strchr (filename, '/') || strchr (filename,'\\'))
1518					continue;
1519			}
1520
1521			sprintf (netpath, "%s/%s",search->filename, filename);
1522
1523			findtime = Sys_FileTime (netpath);
1524			if (findtime == -1)
1525				continue;
1526
1527			Sys_Printf ("FindFile: %s\n",netpath);
1528
1529			*file = fopen (netpath, "rb");
1530			return COM_filelength (*file);
1531		}
1532
1533	}
1534
1535	Sys_Printf ("FindFile: can't find %s\n", filename);
1536
1537	*file = NULL;
1538	com_filesize = -1;
1539	return -1;
1540}
1541
1542/*
1543============
1544COM_LoadFile
1545
1546Filename are reletive to the quake directory.
1547Allways appends a 0 byte to the loaded data.
1548============
1549*/
1550cache_user_t *loadcache;
1551byte	*loadbuf;
1552int		loadsize;
1553byte *COM_LoadFile (char *path, int usehunk)
1554{
1555	FILE	*h;
1556	byte	*buf;
1557	char	base[32];
1558	int		len;
1559
1560	buf = NULL;	// quiet compiler warning
1561
1562// look for it in the filesystem or pack files
1563	len = com_filesize = COM_FOpenFile (path, &h);
1564	if (!h)
1565		return NULL;
1566
1567// extract the filename base name for hunk tag
1568	COM_FileBase (path, base);
1569
1570	if (usehunk == 1)
1571		buf = Hunk_AllocName (len+1, base);
1572	else if (usehunk == 2)
1573		buf = Hunk_TempAlloc (len+1);
1574	else if (usehunk == 0)
1575		buf = Z_Malloc (len+1);
1576	else if (usehunk == 3)
1577		buf = Cache_Alloc (loadcache, len+1, base);
1578	else if (usehunk == 4)
1579	{
1580		if (len+1 > loadsize)
1581			buf = Hunk_TempAlloc (len+1);
1582		else
1583			buf = loadbuf;
1584	}
1585	else
1586		Sys_Error ("COM_LoadFile: bad usehunk");
1587
1588	if (!buf)
1589		Sys_Error ("COM_LoadFile: not enough space for %s", path);
1590
1591	((byte *)buf)[len] = 0;
1592#ifndef SERVERONLY
1593	Draw_BeginDisc ();
1594#endif
1595	fread (buf, 1, len, h);
1596	fclose (h);
1597#ifndef SERVERONLY
1598	Draw_EndDisc ();
1599#endif
1600
1601	return buf;
1602}
1603
1604byte *COM_LoadHunkFile (char *path)
1605{
1606	return COM_LoadFile (path, 1);
1607}
1608
1609byte *COM_LoadTempFile (char *path)
1610{
1611	return COM_LoadFile (path, 2);
1612}
1613
1614void COM_LoadCacheFile (char *path, struct cache_user_s *cu)
1615{
1616	loadcache = cu;
1617	COM_LoadFile (path, 3);
1618}
1619
1620// uses temp hunk if larger than bufsize
1621byte *COM_LoadStackFile (char *path, void *buffer, int bufsize)
1622{
1623	byte	*buf;
1624
1625	loadbuf = (byte *)buffer;
1626	loadsize = bufsize;
1627	buf = COM_LoadFile (path, 4);
1628
1629	return buf;
1630}
1631
1632/*
1633=================
1634COM_LoadPackFile
1635
1636Takes an explicit (not game tree related) path to a pak file.
1637
1638Loads the header and directory, adding the files at the beginning
1639of the list so they override previous pack files.
1640=================
1641*/
1642pack_t *COM_LoadPackFile (char *packfile)
1643{
1644	dpackheader_t	header;
1645	int				i;
1646	packfile_t		*newfiles;
1647	int				numpackfiles;
1648	pack_t			*pack;
1649	FILE			*packhandle;
1650	dpackfile_t		info[MAX_FILES_IN_PACK];
1651	unsigned short		crc;
1652
1653	if (COM_FileOpenRead (packfile, &packhandle) == -1)
1654		return NULL;
1655
1656	fread (&header, 1, sizeof(header), packhandle);
1657	if (header.id[0] != 'P' || header.id[1] != 'A'
1658	|| header.id[2] != 'C' || header.id[3] != 'K')
1659		Sys_Error ("%s is not a packfile", packfile);
1660	header.dirofs = LittleLong (header.dirofs);
1661	header.dirlen = LittleLong (header.dirlen);
1662
1663	numpackfiles = header.dirlen / sizeof(dpackfile_t);
1664
1665	if (numpackfiles > MAX_FILES_IN_PACK)
1666		Sys_Error ("%s has %i files", packfile, numpackfiles);
1667
1668	if (numpackfiles != PAK0_COUNT)
1669		com_modified = true;	// not the original file
1670
1671	newfiles = Z_Malloc (numpackfiles * sizeof(packfile_t));
1672
1673	fseek (packhandle, header.dirofs, SEEK_SET);
1674	fread (&info, 1, header.dirlen, packhandle);
1675
1676// crc the directory to check for modifications
1677	crc = CRC_Block((byte *)info, header.dirlen);
1678
1679//	CRC_Init (&crc);
1680//	for (i=0 ; i<header.dirlen ; i++)
1681//		CRC_ProcessByte (&crc, ((byte *)info)[i]);
1682	if (crc != PAK0_CRC)
1683		com_modified = true;
1684
1685// parse the directory
1686	for (i=0 ; i<numpackfiles ; i++)
1687	{
1688		strcpy (newfiles[i].name, info[i].name);
1689		newfiles[i].filepos = LittleLong(info[i].filepos);
1690		newfiles[i].filelen = LittleLong(info[i].filelen);
1691	}
1692
1693	pack = Z_Malloc (sizeof (pack_t));
1694	strcpy (pack->filename, packfile);
1695	pack->handle = packhandle;
1696	pack->numfiles = numpackfiles;
1697	pack->files = newfiles;
1698
1699	Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1700	return pack;
1701}
1702
1703
1704/*
1705================
1706COM_AddGameDirectory
1707
1708Sets com_gamedir, adds the directory to the head of the path,
1709then loads and adds pak1.pak pak2.pak ...
1710================
1711*/
1712void COM_AddGameDirectory (char *dir)
1713{
1714	int				i;
1715	searchpath_t	*search;
1716	pack_t			*pak;
1717	char			pakfile[MAX_OSPATH];
1718	char			*p;
1719
1720	if ((p = strrchr(dir, '/')) != NULL)
1721		strcpy(gamedirfile, ++p);
1722	else
1723		strcpy(gamedirfile, p);
1724	strcpy (com_gamedir, dir);
1725
1726//
1727// add the directory to the search path
1728//
1729	search = Hunk_Alloc (sizeof(searchpath_t));
1730	strcpy (search->filename, dir);
1731	search->next = com_searchpaths;
1732	com_searchpaths = search;
1733
1734//
1735// add any pak files in the format pak0.pak pak1.pak, ...
1736//
1737	for (i=0 ; ; i++)
1738	{
1739		sprintf (pakfile, "%s/pak%i.pak", dir, i);
1740		pak = COM_LoadPackFile (pakfile);
1741		if (!pak)
1742			break;
1743		search = Hunk_Alloc (sizeof(searchpath_t));
1744		search->pack = pak;
1745		search->next = com_searchpaths;
1746		com_searchpaths = search;
1747	}
1748
1749}
1750
1751/*
1752================
1753COM_Gamedir
1754
1755Sets the gamedir and path to a different directory.
1756================
1757*/
1758void COM_Gamedir (char *dir)
1759{
1760	searchpath_t	*search, *next;
1761	int				i;
1762	pack_t			*pak;
1763	char			pakfile[MAX_OSPATH];
1764
1765	if (strstr(dir, "..") || strstr(dir, "/")
1766		|| strstr(dir, "\\") || strstr(dir, ":") )
1767	{
1768		Con_Printf ("Gamedir should be a single filename, not a path\n");
1769		return;
1770	}
1771
1772	if (!strcmp(gamedirfile, dir))
1773		return;		// still the same
1774	strcpy (gamedirfile, dir);
1775
1776	//
1777	// free up any current game dir info
1778	//
1779	while (com_searchpaths != com_base_searchpaths)
1780	{
1781		if (com_searchpaths->pack)
1782		{
1783			fclose (com_searchpaths->pack->handle);
1784			Z_Free (com_searchpaths->pack->files);
1785			Z_Free (com_searchpaths->pack);
1786		}
1787		next = com_searchpaths->next;
1788		Z_Free (com_searchpaths);
1789		com_searchpaths = next;
1790	}
1791
1792	//
1793	// flush all data, so it will be forced to reload
1794	//
1795	Cache_Flush ();
1796
1797	if (!strcmp(dir,"id1") || !strcmp(dir, "qw"))
1798		return;
1799
1800	sprintf (com_gamedir, "%s/%s", com_basedir, dir);
1801
1802	//
1803	// add the directory to the search path
1804	//
1805	search = Z_Malloc (sizeof(searchpath_t));
1806	strcpy (search->filename, com_gamedir);
1807	search->next = com_searchpaths;
1808	com_searchpaths = search;
1809
1810	//
1811	// add any pak files in the format pak0.pak pak1.pak, ...
1812	//
1813	for (i=0 ; ; i++)
1814	{
1815		sprintf (pakfile, "%s/pak%i.pak", com_gamedir, i);
1816		pak = COM_LoadPackFile (pakfile);
1817		if (!pak)
1818			break;
1819		search = Z_Malloc (sizeof(searchpath_t));
1820		search->pack = pak;
1821		search->next = com_searchpaths;
1822		com_searchpaths = search;
1823	}
1824}
1825
1826/*
1827================
1828COM_InitFilesystem
1829================
1830*/
1831void COM_InitFilesystem (void)
1832{
1833	int		i;
1834
1835//
1836// -basedir <path>
1837// Overrides the system supplied base directory (under id1)
1838//
1839	i = COM_CheckParm ("-basedir");
1840	if (i && i < com_argc-1)
1841		strcpy (com_basedir, com_argv[i+1]);
1842	else
1843		strcpy (com_basedir, host_parms.basedir);
1844
1845//
1846// start up with id1 by default
1847//
1848	COM_AddGameDirectory (va("%s/id1", com_basedir) );
1849	COM_AddGameDirectory (va("%s/qw", com_basedir) );
1850
1851	// any set gamedirs will be freed up to here
1852	com_base_searchpaths = com_searchpaths;
1853}
1854
1855
1856
1857/*
1858=====================================================================
1859
1860  INFO STRINGS
1861
1862=====================================================================
1863*/
1864
1865/*
1866===============
1867Info_ValueForKey
1868
1869Searches the string for the given
1870key and returns the associated value, or an empty string.
1871===============
1872*/
1873char *Info_ValueForKey (char *s, char *key)
1874{
1875	char	pkey[512];
1876	static	char value[4][512];	// use two buffers so compares
1877								// work without stomping on each other
1878	static	int	valueindex;
1879	char	*o;
1880
1881	valueindex = (valueindex + 1) % 4;
1882	if (*s == '\\')
1883		s++;
1884	while (1)
1885	{
1886		o = pkey;
1887		while (*s != '\\')
1888		{
1889			if (!*s)
1890				return "";
1891			*o++ = *s++;
1892		}
1893		*o = 0;
1894		s++;
1895
1896		o = value[valueindex];
1897
1898		while (*s != '\\' && *s)
1899		{
1900			if (!*s)
1901				return "";
1902			*o++ = *s++;
1903		}
1904		*o = 0;
1905
1906		if (!strcmp (key, pkey) )
1907			return value[valueindex];
1908
1909		if (!*s)
1910			return "";
1911		s++;
1912	}
1913}
1914
1915void Info_RemoveKey (char *s, char *key)
1916{
1917	char	*start;
1918	char	pkey[512];
1919	char	value[512];
1920	char	*o;
1921
1922	if (strstr (key, "\\"))
1923	{
1924		Con_Printf ("Can't use a key with a \\\n");
1925		return;
1926	}
1927
1928	while (1)
1929	{
1930		start = s;
1931		if (*s == '\\')
1932			s++;
1933		o = pkey;
1934		while (*s != '\\')
1935		{
1936			if (!*s)
1937				return;
1938			*o++ = *s++;
1939		}
1940		*o = 0;
1941		s++;
1942
1943		o = value;
1944		while (*s != '\\' && *s)
1945		{
1946			if (!*s)
1947				return;
1948			*o++ = *s++;
1949		}
1950		*o = 0;
1951
1952		if (!strcmp (key, pkey) )
1953		{
1954			strcpy (start, s);	// remove this part
1955			return;
1956		}
1957
1958		if (!*s)
1959			return;
1960	}
1961
1962}
1963
1964void Info_RemovePrefixedKeys (char *start, char prefix)
1965{
1966	char	*s;
1967	char	pkey[512];
1968	char	value[512];
1969	char	*o;
1970
1971	s = start;
1972
1973	while (1)
1974	{
1975		if (*s == '\\')
1976			s++;
1977		o = pkey;
1978		while (*s != '\\')
1979		{
1980			if (!*s)
1981				return;
1982			*o++ = *s++;
1983		}
1984		*o = 0;
1985		s++;
1986
1987		o = value;
1988		while (*s != '\\' && *s)
1989		{
1990			if (!*s)
1991				return;
1992			*o++ = *s++;
1993		}
1994		*o = 0;
1995
1996		if (pkey[0] == prefix)
1997		{
1998			Info_RemoveKey (start, pkey);
1999			s = start;
2000		}
2001
2002		if (!*s)
2003			return;
2004	}
2005
2006}
2007
2008
2009void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize)
2010{
2011	char	new[1024], *v;
2012	int		c;
2013#ifdef SERVERONLY
2014	extern cvar_t sv_highchars;
2015#endif
2016
2017	if (strstr (key, "\\") || strstr (value, "\\") )
2018	{
2019		Con_Printf ("Can't use keys or values with a \\\n");
2020		return;
2021	}
2022
2023	if (strstr (key, "\"") || strstr (value, "\"") )
2024	{
2025		Con_Printf ("Can't use keys or values with a \"\n");
2026		return;
2027	}
2028
2029	if (strlen(key) > 63 || strlen(value) > 63)
2030	{
2031		Con_Printf ("Keys and values must be < 64 characters.\n");
2032		return;
2033	}
2034
2035	// this next line is kinda trippy
2036	if (*(v = Info_ValueForKey(s, key))) {
2037		// key exists, make sure we have enough room for new value, if we don't,
2038		// don't change it!
2039		if ((int) (strlen(value) - strlen(v) + strlen(s)) > maxsize) {
2040			Con_Printf ("Info string length exceeded\n");
2041			return;
2042		}
2043	}
2044	Info_RemoveKey (s, key);
2045	if (!value || !strlen(value))
2046		return;
2047
2048	sprintf (new, "\\%s\\%s", key, value);
2049
2050	if ((int)(strlen(new) + strlen(s)) > maxsize)
2051	{
2052		Con_Printf ("Info string length exceeded\n");
2053		return;
2054	}
2055
2056	// only copy ascii values
2057	s += strlen(s);
2058	v = new;
2059	while (*v)
2060	{
2061		c = (unsigned char)*v++;
2062#ifndef SERVERONLY
2063		// client only allows highbits on name
2064		if (stricmp(key, "name") != 0) {
2065			c &= 127;
2066			if (c < 32 || c > 127)
2067				continue;
2068			// auto lowercase team
2069			if (stricmp(key, "team") == 0)
2070				c = tolower(c);
2071		}
2072#else
2073		if (!sv_highchars.value) {
2074			c &= 127;
2075			if (c < 32 || c > 127)
2076				continue;
2077		}
2078#endif
2079//		c &= 127;		// strip high bits
2080		if (c > 13) // && c < 127)
2081			*s++ = c;
2082	}
2083	*s = 0;
2084}
2085
2086void Info_SetValueForKey (char *s, char *key, char *value, int maxsize)
2087{
2088	if (key[0] == '*')
2089	{
2090		Con_Printf ("Can't set * keys\n");
2091		return;
2092	}
2093
2094	Info_SetValueForStarKey (s, key, value, maxsize);
2095}
2096
2097void Info_Print (char *s)
2098{
2099	char	key[512];
2100	char	value[512];
2101	char	*o;
2102	int		l;
2103
2104	if (*s == '\\')
2105		s++;
2106	while (*s)
2107	{
2108		o = key;
2109		while (*s && *s != '\\')
2110			*o++ = *s++;
2111
2112		l = o - key;
2113		if (l < 20)
2114		{
2115			memset (o, ' ', 20-l);
2116			key[20] = 0;
2117		}
2118		else
2119			*o = 0;
2120		Con_Printf ("%s", key);
2121
2122		if (!*s)
2123		{
2124			Con_Printf ("MISSING VALUE\n");
2125			return;
2126		}
2127
2128		o = value;
2129		s++;
2130		while (*s && *s != '\\')
2131			*o++ = *s++;
2132		*o = 0;
2133
2134		if (*s)
2135			s++;
2136		Con_Printf ("%s\n", value);
2137	}
2138}
2139
2140static byte chktbl[1024 + 4] = {
21410x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
21420x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
21430x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
21440xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
21450x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
21460x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
21470x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
21480x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
21490xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
21500xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
21510xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
21520x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
21530xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
21540x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
21550xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
21560x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
21570xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
21580xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
21590x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
21600xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
21610x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
21620xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
21630x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
21640xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
21650xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
21660x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
21670xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
21680x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
21690xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
21700x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
21710xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
21720x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
2173
2174// map checksum goes here
21750x00,0x00,0x00,0x00
2176};
2177
2178static byte chkbuf[16 + 60 + 4];
2179
2180static unsigned last_mapchecksum = 0;
2181
2182#if 0
2183/*
2184====================
2185COM_BlockSequenceCheckByte
2186
2187For proxy protecting
2188====================
2189*/
2190byte	COM_BlockSequenceCheckByte (byte *base, int length, int sequence, unsigned mapchecksum)
2191{
2192	int		checksum;
2193	byte	*p;
2194
2195	if (last_mapchecksum != mapchecksum) {
2196		last_mapchecksum = mapchecksum;
2197		chktbl[1024] = (mapchecksum & 0xff000000) >> 24;
2198		chktbl[1025] = (mapchecksum & 0x00ff0000) >> 16;
2199		chktbl[1026] = (mapchecksum & 0x0000ff00) >> 8;
2200		chktbl[1027] = (mapchecksum & 0x000000ff);
2201
2202		Com_BlockFullChecksum (chktbl, sizeof(chktbl), chkbuf);
2203	}
2204
2205	p = chktbl + (sequence % (sizeof(chktbl) - 8));
2206
2207	if (length > 60)
2208		length = 60;
2209	memcpy (chkbuf + 16, base, length);
2210
2211	length += 16;
2212
2213	chkbuf[length] = (sequence & 0xff) ^ p[0];
2214	chkbuf[length+1] = p[1];
2215	chkbuf[length+2] = ((sequence>>8) & 0xff) ^ p[2];
2216	chkbuf[length+3] = p[3];
2217
2218	length += 4;
2219
2220	checksum = LittleLong(Com_BlockChecksum (chkbuf, length));
2221
2222	checksum &= 0xff;
2223
2224	return checksum;
2225}
2226#endif
2227
2228/*
2229====================
2230COM_BlockSequenceCRCByte
2231
2232For proxy protecting
2233====================
2234*/
2235byte	COM_BlockSequenceCRCByte (byte *base, int length, int sequence)
2236{
2237	unsigned short crc;
2238	byte	*p;
2239	byte chkb[60 + 4];
2240
2241	p = chktbl + (sequence % (sizeof(chktbl) - 8));
2242
2243	if (length > 60)
2244		length = 60;
2245	memcpy (chkb, base, length);
2246
2247	chkb[length] = (sequence & 0xff) ^ p[0];
2248	chkb[length+1] = p[1];
2249	chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
2250	chkb[length+3] = p[3];
2251
2252	length += 4;
2253
2254	crc = CRC_Block(chkb, length);
2255
2256	crc &= 0xff;
2257
2258	return crc;
2259}
2260
2261// char *date = "Oct 24 1996";
2262static char *date = __DATE__ ;
2263static char *mon[12] =
2264{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
2265static char mond[12] =
2266{ 31,    28,    31,    30,    31,    30,    31,    31,    30,    31,    30,    31 };
2267
2268// returns days since Oct 24 1996
2269int build_number( void )
2270{
2271	int m = 0;
2272	int d = 0;
2273	int y = 0;
2274	static int b = 0;
2275
2276	if (b != 0)
2277		return b;
2278
2279	for (m = 0; m < 11; m++)
2280	{
2281		if (Q_strncasecmp( &date[0], mon[m], 3 ) == 0)
2282			break;
2283		d += mond[m];
2284	}
2285
2286	d += atoi( &date[4] ) - 1;
2287
2288	y = atoi( &date[7] ) - 1900;
2289
2290	b = d + (int)((y - 1) * 365.25);
2291
2292	if (((y % 4) == 0) && m > 1)
2293	{
2294		b += 1;
2295	}
2296
2297	b -= 35778; // Dec 16 1998
2298
2299	return b;
2300}
2301
2302