common.c revision 9fd67c44777b350dc56f3e70c88963b0d966ffc7
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 = {"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 < 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 < 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#ifndef SERVERONLY
1124// FIXME DEBUG -- only temporary
1125		if (com_modified)
1126			Sys_Error ("You must have the registered version to play QuakeWorld");
1127#endif
1128		return;
1129	}
1130
1131	fread (check, 1, sizeof(check), h);
1132	fclose (h);
1133
1134	for (i=0 ; i<128 ; i++)
1135		if (pop[i] != (unsigned short)BigShort (check[i]))
1136			Sys_Error ("Corrupted data file.");
1137
1138	Cvar_Set ("registered", "1");
1139	static_registered = 1;
1140	Con_Printf ("Playing registered version.\n");
1141}
1142
1143
1144
1145/*
1146================
1147COM_InitArgv
1148================
1149*/
1150void COM_InitArgv (int argc, char **argv)
1151{
1152	qboolean	safe;
1153	int			i;
1154
1155	safe = false;
1156
1157	for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1158		 com_argc++)
1159	{
1160		largv[com_argc] = argv[com_argc];
1161		if (!Q_strcmp ("-safe", argv[com_argc]))
1162			safe = true;
1163	}
1164
1165	if (safe)
1166	{
1167	// force all the safe-mode switches. Note that we reserved extra space in
1168	// case we need to add these, so we don't need an overflow check
1169		for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1170		{
1171			largv[com_argc] = safeargvs[i];
1172			com_argc++;
1173		}
1174	}
1175
1176	largv[com_argc] = argvdummy;
1177	com_argv = largv;
1178}
1179
1180/*
1181================
1182COM_AddParm
1183
1184Adds the given string at the end of the current argument list
1185================
1186*/
1187void COM_AddParm (char *parm)
1188{
1189	largv[com_argc++] = parm;
1190}
1191
1192
1193/*
1194================
1195COM_Init
1196================
1197*/
1198void COM_Init (void)
1199{
1200	byte	swaptest[2] = {1,0};
1201
1202// set the byte swapping variables in a portable manner
1203	if ( *(short *)swaptest == 1)
1204	{
1205		bigendien = false;
1206		BigShort = ShortSwap;
1207		LittleShort = ShortNoSwap;
1208		BigLong = LongSwap;
1209		LittleLong = LongNoSwap;
1210		BigFloat = FloatSwap;
1211		LittleFloat = FloatNoSwap;
1212	}
1213	else
1214	{
1215		bigendien = true;
1216		BigShort = ShortNoSwap;
1217		LittleShort = ShortSwap;
1218		BigLong = LongNoSwap;
1219		LittleLong = LongSwap;
1220		BigFloat = FloatNoSwap;
1221		LittleFloat = FloatSwap;
1222	}
1223
1224	Cvar_RegisterVariable (&registered);
1225	Cmd_AddCommand ("path", COM_Path_f);
1226
1227	COM_InitFilesystem ();
1228	COM_CheckRegistered ();
1229}
1230
1231
1232/*
1233============
1234va
1235
1236does a varargs printf into a temp buffer, so I don't need to have
1237varargs versions of all text functions.
1238FIXME: make this buffer size safe someday
1239============
1240*/
1241char	*va(char *format, ...)
1242{
1243	va_list		argptr;
1244	static char		string[1024];
1245
1246	va_start (argptr, format);
1247	vsprintf (string, format,argptr);
1248	va_end (argptr);
1249
1250	return string;
1251}
1252
1253
1254/// just for debugging
1255int	memsearch (byte *start, int count, int search)
1256{
1257	int		i;
1258
1259	for (i=0 ; i<count ; i++)
1260		if (start[i] == search)
1261			return i;
1262	return -1;
1263}
1264
1265/*
1266=============================================================================
1267
1268QUAKE FILESYSTEM
1269
1270=============================================================================
1271*/
1272
1273int	com_filesize;
1274
1275
1276//
1277// in memory
1278//
1279
1280typedef struct
1281{
1282	char	name[MAX_QPATH];
1283	int		filepos, filelen;
1284} packfile_t;
1285
1286typedef struct pack_s
1287{
1288	char	filename[MAX_OSPATH];
1289	FILE	*handle;
1290	int		numfiles;
1291	packfile_t	*files;
1292} pack_t;
1293
1294//
1295// on disk
1296//
1297typedef struct
1298{
1299	char	name[56];
1300	int		filepos, filelen;
1301} dpackfile_t;
1302
1303typedef struct
1304{
1305	char	id[4];
1306	int		dirofs;
1307	int		dirlen;
1308} dpackheader_t;
1309
1310#define	MAX_FILES_IN_PACK	2048
1311
1312char	com_gamedir[MAX_OSPATH];
1313char	com_basedir[MAX_OSPATH];
1314
1315typedef struct searchpath_s
1316{
1317	char	filename[MAX_OSPATH];
1318	pack_t	*pack;		// only one of filename / pack will be used
1319	struct searchpath_s *next;
1320} searchpath_t;
1321
1322searchpath_t	*com_searchpaths;
1323searchpath_t	*com_base_searchpaths;	// without gamedirs
1324
1325/*
1326================
1327COM_filelength
1328================
1329*/
1330int COM_filelength (FILE *f)
1331{
1332	int		pos;
1333	int		end;
1334
1335	pos = ftell (f);
1336	fseek (f, 0, SEEK_END);
1337	end = ftell (f);
1338	fseek (f, pos, SEEK_SET);
1339
1340	return end;
1341}
1342
1343int COM_FileOpenRead (char *path, FILE **hndl)
1344{
1345	FILE	*f;
1346
1347	f = fopen(path, "rb");
1348	if (!f)
1349	{
1350		*hndl = NULL;
1351		return -1;
1352	}
1353	*hndl = f;
1354
1355	return COM_filelength(f);
1356}
1357
1358/*
1359============
1360COM_Path_f
1361
1362============
1363*/
1364void COM_Path_f (void)
1365{
1366	searchpath_t	*s;
1367
1368	Con_Printf ("Current search path:\n");
1369	for (s=com_searchpaths ; s ; s=s->next)
1370	{
1371		if (s == com_base_searchpaths)
1372			Con_Printf ("----------\n");
1373		if (s->pack)
1374			Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1375		else
1376			Con_Printf ("%s\n", s->filename);
1377	}
1378}
1379
1380/*
1381============
1382COM_WriteFile
1383
1384The filename will be prefixed by the current game directory
1385============
1386*/
1387void COM_WriteFile (char *filename, void *data, int len)
1388{
1389	FILE	*f;
1390	char	name[MAX_OSPATH];
1391
1392	sprintf (name, "%s/%s", com_gamedir, filename);
1393
1394	f = fopen (name, "wb");
1395	if (!f) {
1396		Sys_mkdir(com_gamedir);
1397		f = fopen (name, "wb");
1398		if (!f)
1399			Sys_Error ("Error opening %s", filename);
1400	}
1401
1402	Sys_Printf ("COM_WriteFile: %s\n", name);
1403	fwrite (data, 1, len, f);
1404	fclose (f);
1405}
1406
1407
1408/*
1409============
1410COM_CreatePath
1411
1412Only used for CopyFile and download
1413============
1414*/
1415void	COM_CreatePath (char *path)
1416{
1417	char	*ofs;
1418
1419	for (ofs = path+1 ; *ofs ; ofs++)
1420	{
1421		if (*ofs == '/')
1422		{	// create the directory
1423			*ofs = 0;
1424			Sys_mkdir (path);
1425			*ofs = '/';
1426		}
1427	}
1428}
1429
1430
1431/*
1432===========
1433COM_CopyFile
1434
1435Copies a file over from the net to the local cache, creating any directories
1436needed.  This is for the convenience of developers using ISDN from home.
1437===========
1438*/
1439void COM_CopyFile (char *netpath, char *cachepath)
1440{
1441	FILE	*in, *out;
1442	int		remaining, count;
1443	char	buf[4096];
1444
1445	remaining = COM_FileOpenRead (netpath, &in);
1446	COM_CreatePath (cachepath);	// create directories up to the cache file
1447	out = fopen(cachepath, "wb");
1448	if (!out)
1449		Sys_Error ("Error opening %s", cachepath);
1450
1451	while (remaining)
1452	{
1453		if (remaining < sizeof(buf))
1454			count = remaining;
1455		else
1456			count = sizeof(buf);
1457		fread (buf, 1, count, in);
1458		fwrite (buf, 1, count, out);
1459		remaining -= count;
1460	}
1461
1462	fclose (in);
1463	fclose (out);
1464}
1465
1466/*
1467===========
1468COM_FindFile
1469
1470Finds the file in the search path.
1471Sets com_filesize and one of handle or file
1472===========
1473*/
1474int file_from_pak; // global indicating file came from pack file ZOID
1475
1476int COM_FOpenFile (char *filename, FILE **file)
1477{
1478	searchpath_t	*search;
1479	char		netpath[MAX_OSPATH];
1480	pack_t		*pak;
1481	int			i;
1482	int			findtime;
1483
1484	file_from_pak = 0;
1485
1486//
1487// search through the path, one element at a time
1488//
1489	for (search = com_searchpaths ; search ; search = search->next)
1490	{
1491	// is the element a pak file?
1492		if (search->pack)
1493		{
1494		// look through all the pak file elements
1495			pak = search->pack;
1496			for (i=0 ; i<pak->numfiles ; i++)
1497				if (!strcmp (pak->files[i].name, filename))
1498				{	// found it!
1499					Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename);
1500				// open a new file on the pakfile
1501					*file = fopen (pak->filename, "rb");
1502					if (!*file)
1503						Sys_Error ("Couldn't reopen %s", pak->filename);
1504					fseek (*file, pak->files[i].filepos, SEEK_SET);
1505					com_filesize = pak->files[i].filelen;
1506					file_from_pak = 1;
1507					return com_filesize;
1508				}
1509		}
1510		else
1511		{
1512	// check a file in the directory tree
1513			if (!static_registered)
1514			{	// if not a registered version, don't ever go beyond base
1515				if ( strchr (filename, '/') || strchr (filename,'\\'))
1516					continue;
1517			}
1518
1519			sprintf (netpath, "%s/%s",search->filename, filename);
1520
1521			findtime = Sys_FileTime (netpath);
1522			if (findtime == -1)
1523				continue;
1524
1525			Sys_Printf ("FindFile: %s\n",netpath);
1526
1527			*file = fopen (netpath, "rb");
1528			return COM_filelength (*file);
1529		}
1530
1531	}
1532
1533	Sys_Printf ("FindFile: can't find %s\n", filename);
1534
1535	*file = NULL;
1536	com_filesize = -1;
1537	return -1;
1538}
1539
1540/*
1541============
1542COM_LoadFile
1543
1544Filename are reletive to the quake directory.
1545Allways appends a 0 byte to the loaded data.
1546============
1547*/
1548cache_user_t *loadcache;
1549byte	*loadbuf;
1550int		loadsize;
1551byte *COM_LoadFile (char *path, int usehunk)
1552{
1553	FILE	*h;
1554	byte	*buf;
1555	char	base[32];
1556	int		len;
1557
1558	buf = NULL;	// quiet compiler warning
1559
1560// look for it in the filesystem or pack files
1561	len = com_filesize = COM_FOpenFile (path, &h);
1562	if (!h)
1563		return NULL;
1564
1565// extract the filename base name for hunk tag
1566	COM_FileBase (path, base);
1567
1568	if (usehunk == 1)
1569		buf = Hunk_AllocName (len+1, base);
1570	else if (usehunk == 2)
1571		buf = Hunk_TempAlloc (len+1);
1572	else if (usehunk == 0)
1573		buf = Z_Malloc (len+1);
1574	else if (usehunk == 3)
1575		buf = Cache_Alloc (loadcache, len+1, base);
1576	else if (usehunk == 4)
1577	{
1578		if (len+1 > loadsize)
1579			buf = Hunk_TempAlloc (len+1);
1580		else
1581			buf = loadbuf;
1582	}
1583	else
1584		Sys_Error ("COM_LoadFile: bad usehunk");
1585
1586	if (!buf)
1587		Sys_Error ("COM_LoadFile: not enough space for %s", path);
1588
1589	((byte *)buf)[len] = 0;
1590#ifndef SERVERONLY
1591	Draw_BeginDisc ();
1592#endif
1593	fread (buf, 1, len, h);
1594	fclose (h);
1595#ifndef SERVERONLY
1596	Draw_EndDisc ();
1597#endif
1598
1599	return buf;
1600}
1601
1602byte *COM_LoadHunkFile (char *path)
1603{
1604	return COM_LoadFile (path, 1);
1605}
1606
1607byte *COM_LoadTempFile (char *path)
1608{
1609	return COM_LoadFile (path, 2);
1610}
1611
1612void COM_LoadCacheFile (char *path, struct cache_user_s *cu)
1613{
1614	loadcache = cu;
1615	COM_LoadFile (path, 3);
1616}
1617
1618// uses temp hunk if larger than bufsize
1619byte *COM_LoadStackFile (char *path, void *buffer, int bufsize)
1620{
1621	byte	*buf;
1622
1623	loadbuf = (byte *)buffer;
1624	loadsize = bufsize;
1625	buf = COM_LoadFile (path, 4);
1626
1627	return buf;
1628}
1629
1630/*
1631=================
1632COM_LoadPackFile
1633
1634Takes an explicit (not game tree related) path to a pak file.
1635
1636Loads the header and directory, adding the files at the beginning
1637of the list so they override previous pack files.
1638=================
1639*/
1640pack_t *COM_LoadPackFile (char *packfile)
1641{
1642	dpackheader_t	header;
1643	int				i;
1644	packfile_t		*newfiles;
1645	int				numpackfiles;
1646	pack_t			*pack;
1647	FILE			*packhandle;
1648	dpackfile_t		info[MAX_FILES_IN_PACK];
1649	unsigned short		crc;
1650
1651	if (COM_FileOpenRead (packfile, &packhandle) == -1)
1652		return NULL;
1653
1654	fread (&header, 1, sizeof(header), packhandle);
1655	if (header.id[0] != 'P' || header.id[1] != 'A'
1656	|| header.id[2] != 'C' || header.id[3] != 'K')
1657		Sys_Error ("%s is not a packfile", packfile);
1658	header.dirofs = LittleLong (header.dirofs);
1659	header.dirlen = LittleLong (header.dirlen);
1660
1661	numpackfiles = header.dirlen / sizeof(dpackfile_t);
1662
1663	if (numpackfiles > MAX_FILES_IN_PACK)
1664		Sys_Error ("%s has %i files", packfile, numpackfiles);
1665
1666	if (numpackfiles != PAK0_COUNT)
1667		com_modified = true;	// not the original file
1668
1669	newfiles = Z_Malloc (numpackfiles * sizeof(packfile_t));
1670
1671	fseek (packhandle, header.dirofs, SEEK_SET);
1672	fread (&info, 1, header.dirlen, packhandle);
1673
1674// crc the directory to check for modifications
1675	crc = CRC_Block((byte *)info, header.dirlen);
1676
1677//	CRC_Init (&crc);
1678//	for (i=0 ; i<header.dirlen ; i++)
1679//		CRC_ProcessByte (&crc, ((byte *)info)[i]);
1680	if (crc != PAK0_CRC)
1681		com_modified = true;
1682
1683// parse the directory
1684	for (i=0 ; i<numpackfiles ; i++)
1685	{
1686		strcpy (newfiles[i].name, info[i].name);
1687		newfiles[i].filepos = LittleLong(info[i].filepos);
1688		newfiles[i].filelen = LittleLong(info[i].filelen);
1689	}
1690
1691	pack = Z_Malloc (sizeof (pack_t));
1692	strcpy (pack->filename, packfile);
1693	pack->handle = packhandle;
1694	pack->numfiles = numpackfiles;
1695	pack->files = newfiles;
1696
1697	Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1698	return pack;
1699}
1700
1701
1702/*
1703================
1704COM_AddGameDirectory
1705
1706Sets com_gamedir, adds the directory to the head of the path,
1707then loads and adds pak1.pak pak2.pak ...
1708================
1709*/
1710void COM_AddGameDirectory (char *dir)
1711{
1712	int				i;
1713	searchpath_t	*search;
1714	pack_t			*pak;
1715	char			pakfile[MAX_OSPATH];
1716	char			*p;
1717
1718	if ((p = strrchr(dir, '/')) != NULL)
1719		strcpy(gamedirfile, ++p);
1720	else
1721		strcpy(gamedirfile, p);
1722	strcpy (com_gamedir, dir);
1723
1724//
1725// add the directory to the search path
1726//
1727	search = Hunk_Alloc (sizeof(searchpath_t));
1728	strcpy (search->filename, dir);
1729	search->next = com_searchpaths;
1730	com_searchpaths = search;
1731
1732//
1733// add any pak files in the format pak0.pak pak1.pak, ...
1734//
1735	for (i=0 ; ; i++)
1736	{
1737		sprintf (pakfile, "%s/pak%i.pak", dir, i);
1738		pak = COM_LoadPackFile (pakfile);
1739		if (!pak)
1740			break;
1741		search = Hunk_Alloc (sizeof(searchpath_t));
1742		search->pack = pak;
1743		search->next = com_searchpaths;
1744		com_searchpaths = search;
1745	}
1746
1747}
1748
1749/*
1750================
1751COM_Gamedir
1752
1753Sets the gamedir and path to a different directory.
1754================
1755*/
1756void COM_Gamedir (char *dir)
1757{
1758	searchpath_t	*search, *next;
1759	int				i;
1760	pack_t			*pak;
1761	char			pakfile[MAX_OSPATH];
1762
1763	if (strstr(dir, "..") || strstr(dir, "/")
1764		|| strstr(dir, "\\") || strstr(dir, ":") )
1765	{
1766		Con_Printf ("Gamedir should be a single filename, not a path\n");
1767		return;
1768	}
1769
1770	if (!strcmp(gamedirfile, dir))
1771		return;		// still the same
1772	strcpy (gamedirfile, dir);
1773
1774	//
1775	// free up any current game dir info
1776	//
1777	while (com_searchpaths != com_base_searchpaths)
1778	{
1779		if (com_searchpaths->pack)
1780		{
1781			fclose (com_searchpaths->pack->handle);
1782			Z_Free (com_searchpaths->pack->files);
1783			Z_Free (com_searchpaths->pack);
1784		}
1785		next = com_searchpaths->next;
1786		Z_Free (com_searchpaths);
1787		com_searchpaths = next;
1788	}
1789
1790	//
1791	// flush all data, so it will be forced to reload
1792	//
1793	Cache_Flush ();
1794
1795	if (!strcmp(dir,"id1") || !strcmp(dir, "qw"))
1796		return;
1797
1798	sprintf (com_gamedir, "%s/%s", com_basedir, dir);
1799
1800	//
1801	// add the directory to the search path
1802	//
1803	search = Z_Malloc (sizeof(searchpath_t));
1804	strcpy (search->filename, com_gamedir);
1805	search->next = com_searchpaths;
1806	com_searchpaths = search;
1807
1808	//
1809	// add any pak files in the format pak0.pak pak1.pak, ...
1810	//
1811	for (i=0 ; ; i++)
1812	{
1813		sprintf (pakfile, "%s/pak%i.pak", com_gamedir, i);
1814		pak = COM_LoadPackFile (pakfile);
1815		if (!pak)
1816			break;
1817		search = Z_Malloc (sizeof(searchpath_t));
1818		search->pack = pak;
1819		search->next = com_searchpaths;
1820		com_searchpaths = search;
1821	}
1822}
1823
1824/*
1825================
1826COM_InitFilesystem
1827================
1828*/
1829void COM_InitFilesystem (void)
1830{
1831	int		i;
1832
1833//
1834// -basedir <path>
1835// Overrides the system supplied base directory (under id1)
1836//
1837	i = COM_CheckParm ("-basedir");
1838	if (i && i < com_argc-1)
1839		strcpy (com_basedir, com_argv[i+1]);
1840	else
1841		strcpy (com_basedir, host_parms.basedir);
1842
1843//
1844// start up with id1 by default
1845//
1846	COM_AddGameDirectory (va("%s/id1", com_basedir) );
1847	COM_AddGameDirectory (va("%s/qw", com_basedir) );
1848
1849	// any set gamedirs will be freed up to here
1850	com_base_searchpaths = com_searchpaths;
1851}
1852
1853
1854
1855/*
1856=====================================================================
1857
1858  INFO STRINGS
1859
1860=====================================================================
1861*/
1862
1863/*
1864===============
1865Info_ValueForKey
1866
1867Searches the string for the given
1868key and returns the associated value, or an empty string.
1869===============
1870*/
1871char *Info_ValueForKey (char *s, char *key)
1872{
1873	char	pkey[512];
1874	static	char value[4][512];	// use two buffers so compares
1875								// work without stomping on each other
1876	static	int	valueindex;
1877	char	*o;
1878
1879	valueindex = (valueindex + 1) % 4;
1880	if (*s == '\\')
1881		s++;
1882	while (1)
1883	{
1884		o = pkey;
1885		while (*s != '\\')
1886		{
1887			if (!*s)
1888				return "";
1889			*o++ = *s++;
1890		}
1891		*o = 0;
1892		s++;
1893
1894		o = value[valueindex];
1895
1896		while (*s != '\\' && *s)
1897		{
1898			if (!*s)
1899				return "";
1900			*o++ = *s++;
1901		}
1902		*o = 0;
1903
1904		if (!strcmp (key, pkey) )
1905			return value[valueindex];
1906
1907		if (!*s)
1908			return "";
1909		s++;
1910	}
1911}
1912
1913void Info_RemoveKey (char *s, char *key)
1914{
1915	char	*start;
1916	char	pkey[512];
1917	char	value[512];
1918	char	*o;
1919
1920	if (strstr (key, "\\"))
1921	{
1922		Con_Printf ("Can't use a key with a \\\n");
1923		return;
1924	}
1925
1926	while (1)
1927	{
1928		start = s;
1929		if (*s == '\\')
1930			s++;
1931		o = pkey;
1932		while (*s != '\\')
1933		{
1934			if (!*s)
1935				return;
1936			*o++ = *s++;
1937		}
1938		*o = 0;
1939		s++;
1940
1941		o = value;
1942		while (*s != '\\' && *s)
1943		{
1944			if (!*s)
1945				return;
1946			*o++ = *s++;
1947		}
1948		*o = 0;
1949
1950		if (!strcmp (key, pkey) )
1951		{
1952			strcpy (start, s);	// remove this part
1953			return;
1954		}
1955
1956		if (!*s)
1957			return;
1958	}
1959
1960}
1961
1962void Info_RemovePrefixedKeys (char *start, char prefix)
1963{
1964	char	*s;
1965	char	pkey[512];
1966	char	value[512];
1967	char	*o;
1968
1969	s = start;
1970
1971	while (1)
1972	{
1973		if (*s == '\\')
1974			s++;
1975		o = pkey;
1976		while (*s != '\\')
1977		{
1978			if (!*s)
1979				return;
1980			*o++ = *s++;
1981		}
1982		*o = 0;
1983		s++;
1984
1985		o = value;
1986		while (*s != '\\' && *s)
1987		{
1988			if (!*s)
1989				return;
1990			*o++ = *s++;
1991		}
1992		*o = 0;
1993
1994		if (pkey[0] == prefix)
1995		{
1996			Info_RemoveKey (start, pkey);
1997			s = start;
1998		}
1999
2000		if (!*s)
2001			return;
2002	}
2003
2004}
2005
2006
2007void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize)
2008{
2009	char	new[1024], *v;
2010	int		c;
2011#ifdef SERVERONLY
2012	extern cvar_t sv_highchars;
2013#endif
2014
2015	if (strstr (key, "\\") || strstr (value, "\\") )
2016	{
2017		Con_Printf ("Can't use keys or values with a \\\n");
2018		return;
2019	}
2020
2021	if (strstr (key, "\"") || strstr (value, "\"") )
2022	{
2023		Con_Printf ("Can't use keys or values with a \"\n");
2024		return;
2025	}
2026
2027	if (strlen(key) > 63 || strlen(value) > 63)
2028	{
2029		Con_Printf ("Keys and values must be < 64 characters.\n");
2030		return;
2031	}
2032
2033	// this next line is kinda trippy
2034	if (*(v = Info_ValueForKey(s, key))) {
2035		// key exists, make sure we have enough room for new value, if we don't,
2036		// don't change it!
2037		if (strlen(value) - strlen(v) + strlen(s) > maxsize) {
2038			Con_Printf ("Info string length exceeded\n");
2039			return;
2040		}
2041	}
2042	Info_RemoveKey (s, key);
2043	if (!value || !strlen(value))
2044		return;
2045
2046	sprintf (new, "\\%s\\%s", key, value);
2047
2048	if ((int)(strlen(new) + strlen(s)) > maxsize)
2049	{
2050		Con_Printf ("Info string length exceeded\n");
2051		return;
2052	}
2053
2054	// only copy ascii values
2055	s += strlen(s);
2056	v = new;
2057	while (*v)
2058	{
2059		c = (unsigned char)*v++;
2060#ifndef SERVERONLY
2061		// client only allows highbits on name
2062		if (stricmp(key, "name") != 0) {
2063			c &= 127;
2064			if (c < 32 || c > 127)
2065				continue;
2066			// auto lowercase team
2067			if (stricmp(key, "team") == 0)
2068				c = tolower(c);
2069		}
2070#else
2071		if (!sv_highchars.value) {
2072			c &= 127;
2073			if (c < 32 || c > 127)
2074				continue;
2075		}
2076#endif
2077//		c &= 127;		// strip high bits
2078		if (c > 13) // && c < 127)
2079			*s++ = c;
2080	}
2081	*s = 0;
2082}
2083
2084void Info_SetValueForKey (char *s, char *key, char *value, int maxsize)
2085{
2086	if (key[0] == '*')
2087	{
2088		Con_Printf ("Can't set * keys\n");
2089		return;
2090	}
2091
2092	Info_SetValueForStarKey (s, key, value, maxsize);
2093}
2094
2095void Info_Print (char *s)
2096{
2097	char	key[512];
2098	char	value[512];
2099	char	*o;
2100	int		l;
2101
2102	if (*s == '\\')
2103		s++;
2104	while (*s)
2105	{
2106		o = key;
2107		while (*s && *s != '\\')
2108			*o++ = *s++;
2109
2110		l = o - key;
2111		if (l < 20)
2112		{
2113			memset (o, ' ', 20-l);
2114			key[20] = 0;
2115		}
2116		else
2117			*o = 0;
2118		Con_Printf ("%s", key);
2119
2120		if (!*s)
2121		{
2122			Con_Printf ("MISSING VALUE\n");
2123			return;
2124		}
2125
2126		o = value;
2127		s++;
2128		while (*s && *s != '\\')
2129			*o++ = *s++;
2130		*o = 0;
2131
2132		if (*s)
2133			s++;
2134		Con_Printf ("%s\n", value);
2135	}
2136}
2137
2138static byte chktbl[1024 + 4] = {
21390x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,
21400x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,
21410x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,
21420xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,
21430x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,
21440x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,
21450x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,
21460x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,
21470xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,
21480xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,
21490xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,
21500x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,
21510xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,
21520x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,
21530xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,
21540x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,
21550xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,
21560xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,
21570x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,
21580xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,
21590x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,
21600xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,
21610x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,
21620xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,
21630xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,
21640x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,
21650xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,
21660x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,
21670xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,
21680x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,
21690xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,
21700x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,
2171
2172// map checksum goes here
21730x00,0x00,0x00,0x00
2174};
2175
2176static byte chkbuf[16 + 60 + 4];
2177
2178static unsigned last_mapchecksum = 0;
2179
2180#if 0
2181/*
2182====================
2183COM_BlockSequenceCheckByte
2184
2185For proxy protecting
2186====================
2187*/
2188byte	COM_BlockSequenceCheckByte (byte *base, int length, int sequence, unsigned mapchecksum)
2189{
2190	int		checksum;
2191	byte	*p;
2192
2193	if (last_mapchecksum != mapchecksum) {
2194		last_mapchecksum = mapchecksum;
2195		chktbl[1024] = (mapchecksum & 0xff000000) >> 24;
2196		chktbl[1025] = (mapchecksum & 0x00ff0000) >> 16;
2197		chktbl[1026] = (mapchecksum & 0x0000ff00) >> 8;
2198		chktbl[1027] = (mapchecksum & 0x000000ff);
2199
2200		Com_BlockFullChecksum (chktbl, sizeof(chktbl), chkbuf);
2201	}
2202
2203	p = chktbl + (sequence % (sizeof(chktbl) - 8));
2204
2205	if (length > 60)
2206		length = 60;
2207	memcpy (chkbuf + 16, base, length);
2208
2209	length += 16;
2210
2211	chkbuf[length] = (sequence & 0xff) ^ p[0];
2212	chkbuf[length+1] = p[1];
2213	chkbuf[length+2] = ((sequence>>8) & 0xff) ^ p[2];
2214	chkbuf[length+3] = p[3];
2215
2216	length += 4;
2217
2218	checksum = LittleLong(Com_BlockChecksum (chkbuf, length));
2219
2220	checksum &= 0xff;
2221
2222	return checksum;
2223}
2224#endif
2225
2226/*
2227====================
2228COM_BlockSequenceCRCByte
2229
2230For proxy protecting
2231====================
2232*/
2233byte	COM_BlockSequenceCRCByte (byte *base, int length, int sequence)
2234{
2235	unsigned short crc;
2236	byte	*p;
2237	byte chkb[60 + 4];
2238
2239	p = chktbl + (sequence % (sizeof(chktbl) - 8));
2240
2241	if (length > 60)
2242		length = 60;
2243	memcpy (chkb, base, length);
2244
2245	chkb[length] = (sequence & 0xff) ^ p[0];
2246	chkb[length+1] = p[1];
2247	chkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];
2248	chkb[length+3] = p[3];
2249
2250	length += 4;
2251
2252	crc = CRC_Block(chkb, length);
2253
2254	crc &= 0xff;
2255
2256	return crc;
2257}
2258
2259// char *date = "Oct 24 1996";
2260static char *date = __DATE__ ;
2261static char *mon[12] =
2262{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
2263static char mond[12] =
2264{ 31,    28,    31,    30,    31,    30,    31,    31,    30,    31,    30,    31 };
2265
2266// returns days since Oct 24 1996
2267int build_number( void )
2268{
2269	int m = 0;
2270	int d = 0;
2271	int y = 0;
2272	static int b = 0;
2273
2274	if (b != 0)
2275		return b;
2276
2277	for (m = 0; m < 11; m++)
2278	{
2279		if (Q_strncasecmp( &date[0], mon[m], 3 ) == 0)
2280			break;
2281		d += mond[m];
2282	}
2283
2284	d += atoi( &date[4] ) - 1;
2285
2286	y = atoi( &date[7] ) - 1900;
2287
2288	b = d + (int)((y - 1) * 365.25);
2289
2290	if (((y % 4) == 0) && m > 1)
2291	{
2292		b += 1;
2293	}
2294
2295	b -= 35778; // Dec 16 1998
2296
2297	return b;
2298}
2299
2300