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 "quakedef.h"
23
24#define NUM_SAFE_ARGVS  7
25
26static const char     *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
27static const char     *argvdummy = " ";
28
29static const char     *safeargvs[NUM_SAFE_ARGVS] =
30	{"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"};
31
32cvar_t	registered = CVAR2("registered","0");
33cvar_t  cmdline = CVAR4("cmdline","0", false, true);
34
35qboolean        com_modified;   // set true if using non-id files
36
37qboolean		proghack;
38
39int             static_registered = 1;  // only for startup check, then set
40
41qboolean		msg_suppress_1 = 0;
42
43void COM_InitFilesystem (void);
44
45// if a packfile directory differs from this, it is assumed to be hacked
46#define PAK0_COUNT              339
47#define PAK0_CRC                32981
48
49char	com_token[1024];
50int		com_argc;
51const char	**com_argv;
52
53#define CMDLINE_LENGTH	256
54char	com_cmdline[CMDLINE_LENGTH];
55
56qboolean		standard_quake = true, rogue, hipnotic;
57
58// this graphic needs to be in the pak file to use registered features
59unsigned short pop[] =
60{
61 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
62,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000
63,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000
64,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600
65,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563
66,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564
67,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564
68,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563
69,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500
70,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200
71,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000
72,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000
73,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000
74,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000
75,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000
76,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000
77};
78
79/*
80
81
82All 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.
83
84The "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
85only used during filesystem initialization.
86
87The "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.
88
89The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines.  If there is a cache directory
90specified, when a file is found by the normal search path, it will be mirrored
91into the cache directory, then opened there.
92
93
94
95FIXME:
96The file "parms.txt" will be read out of the game directory and appended to the current command line arguments to allow different games to initialize startup parms differently.  This could be used to add a "-sspeed 22050" for the high quality sound edition.  Because they are added at the end, they will not override an explicit setting on the original command line.
97
98*/
99
100//============================================================================
101
102
103// ClearLink is used for new headnodes
104void ClearLink (link_t *l)
105{
106	l->prev = l->next = l;
107}
108
109void RemoveLink (link_t *l)
110{
111	l->next->prev = l->prev;
112	l->prev->next = l->next;
113}
114
115void InsertLinkBefore (link_t *l, link_t *before)
116{
117	l->next = before;
118	l->prev = before->prev;
119	l->prev->next = l;
120	l->next->prev = l;
121}
122void InsertLinkAfter (link_t *l, link_t *after)
123{
124	l->next = after->next;
125	l->prev = after;
126	l->prev->next = l;
127	l->next->prev = l;
128}
129
130/*
131============================================================================
132
133					LIBRARY REPLACEMENT FUNCTIONS
134
135============================================================================
136*/
137
138void Q_memset (void *dest, int fill, int count)
139{
140	int             i;
141
142	if ( (((long)dest | count) & 3) == 0)
143	{
144		count >>= 2;
145		fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
146		for (i=0 ; i<count ; i++)
147			((int *)dest)[i] = fill;
148	}
149	else
150		for (i=0 ; i<count ; i++)
151			((byte *)dest)[i] = fill;
152}
153
154void Q_memcpy (void *dest, const void *src, int count)
155{
156	int             i;
157
158	if (( ( (long)dest | (long)src | count) & 3) == 0 )
159	{
160		count>>=2;
161		for (i=0 ; i<count ; i++)
162			((int *)dest)[i] = ((int *)src)[i];
163	}
164	else
165		for (i=0 ; i<count ; i++)
166			((byte *)dest)[i] = ((byte *)src)[i];
167}
168
169int Q_memcmp (const void *m1, const void *m2, int count)
170{
171	while(count)
172	{
173		count--;
174		if (((byte *)m1)[count] != ((byte *)m2)[count])
175			return -1;
176	}
177	return 0;
178}
179
180void Q_strcpy (char *dest, const char *src)
181{
182	while (*src)
183	{
184		*dest++ = *src++;
185	}
186	*dest++ = 0;
187}
188
189void Q_strncpy (char *dest, const char *src, int count)
190{
191	while (*src && count--)
192	{
193		*dest++ = *src++;
194	}
195	if (count)
196		*dest++ = 0;
197}
198
199int Q_strlen (const char *str)
200{
201	int             count;
202
203	count = 0;
204	while (str[count])
205		count++;
206
207	return count;
208}
209
210char *Q_strrchr(const char *s, char c)
211{
212    int len = Q_strlen(s);
213    s += len;
214    while (len--)
215	if (*--s == c) return (char*)  s;
216    return 0;
217}
218
219void Q_strcat (char *dest, const char *src)
220{
221	dest += Q_strlen(dest);
222	Q_strcpy (dest, src);
223}
224
225int Q_strcmp (const char *s1, const char *s2)
226{
227	while (1)
228	{
229		if (*s1 != *s2)
230			return -1;              // strings not equal
231		if (!*s1)
232			return 0;               // strings are equal
233		s1++;
234		s2++;
235	}
236
237	return -1;
238}
239
240int Q_strncmp (const char *s1, const char *s2, int count)
241{
242	while (1)
243	{
244		if (!count--)
245			return 0;
246		if (*s1 != *s2)
247			return -1;              // strings not equal
248		if (!*s1)
249			return 0;               // strings are equal
250		s1++;
251		s2++;
252	}
253
254	return -1;
255}
256
257int Q_strncasecmp (const char *s1, const char *s2, int n)
258{
259	int             c1, c2;
260
261	while (1)
262	{
263		c1 = *s1++;
264		c2 = *s2++;
265
266		if (!n--)
267			return 0;               // strings are equal until end point
268
269		if (c1 != c2)
270		{
271			if (c1 >= 'a' && c1 <= 'z')
272				c1 -= ('a' - 'A');
273			if (c2 >= 'a' && c2 <= 'z')
274				c2 -= ('a' - 'A');
275			if (c1 != c2)
276				return -1;              // strings not equal
277		}
278		if (!c1)
279			return 0;               // strings are equal
280//              s1++;
281//              s2++;
282	}
283
284	return -1;
285}
286
287int Q_strcasecmp (const char *s1, const char *s2)
288{
289	return Q_strncasecmp (s1, s2, 99999);
290}
291
292int Q_atoi (const char *str)
293{
294	int             val;
295	int             sign;
296	int             c;
297
298	if (*str == '-')
299	{
300		sign = -1;
301		str++;
302	}
303	else
304		sign = 1;
305
306	val = 0;
307
308//
309// check for hex
310//
311	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
312	{
313		str += 2;
314		while (1)
315		{
316			c = *str++;
317			if (c >= '0' && c <= '9')
318				val = (val<<4) + c - '0';
319			else if (c >= 'a' && c <= 'f')
320				val = (val<<4) + c - 'a' + 10;
321			else if (c >= 'A' && c <= 'F')
322				val = (val<<4) + c - 'A' + 10;
323			else
324				return val*sign;
325		}
326	}
327
328//
329// check for character
330//
331	if (str[0] == '\'')
332	{
333		return sign * str[1];
334	}
335
336//
337// assume decimal
338//
339	while (1)
340	{
341		c = *str++;
342		if (c <'0' || c > '9')
343			return val*sign;
344		val = val*10 + c - '0';
345	}
346
347	return 0;
348}
349
350
351float Q_atof (const char *str)
352{
353	double			val;
354	int             sign;
355	int             c;
356	int             decimal, total;
357
358	if (*str == '-')
359	{
360		sign = -1;
361		str++;
362	}
363	else
364		sign = 1;
365
366	val = 0;
367
368//
369// check for hex
370//
371	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
372	{
373		str += 2;
374		while (1)
375		{
376			c = *str++;
377			if (c >= '0' && c <= '9')
378				val = (val*16) + c - '0';
379			else if (c >= 'a' && c <= 'f')
380				val = (val*16) + c - 'a' + 10;
381			else if (c >= 'A' && c <= 'F')
382				val = (val*16) + c - 'A' + 10;
383			else
384				return val*sign;
385		}
386	}
387
388//
389// check for character
390//
391	if (str[0] == '\'')
392	{
393		return sign * str[1];
394	}
395
396//
397// assume decimal
398//
399	decimal = -1;
400	total = 0;
401	while (1)
402	{
403		c = *str++;
404		if (c == '.')
405		{
406			decimal = total;
407			continue;
408		}
409		if (c <'0' || c > '9')
410			break;
411		val = val*10 + c - '0';
412		total++;
413	}
414
415	if (decimal == -1)
416		return val*sign;
417	while (total > decimal)
418	{
419		val /= 10;
420		total--;
421	}
422
423	return val*sign;
424}
425
426/*
427============================================================================
428
429					BYTE ORDER FUNCTIONS
430
431============================================================================
432*/
433
434qboolean        bigendien;
435
436short   (*BigShort) (short l);
437short   (*LittleShort) (short l);
438int     (*BigLong) (int l);
439int     (*LittleLong) (int l);
440float   (*BigFloat) (float l);
441float   (*LittleFloat) (float l);
442
443short   ShortSwap (short l)
444{
445	byte    b1,b2;
446
447	b1 = l&255;
448	b2 = (l>>8)&255;
449
450	return (b1<<8) + b2;
451}
452
453short   ShortNoSwap (short l)
454{
455	return l;
456}
457
458int    LongSwap (int l)
459{
460	byte    b1,b2,b3,b4;
461
462	b1 = l&255;
463	b2 = (l>>8)&255;
464	b3 = (l>>16)&255;
465	b4 = (l>>24)&255;
466
467	return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
468}
469
470int     LongNoSwap (int l)
471{
472	return l;
473}
474
475float FloatSwap (float f)
476{
477	union
478	{
479		float   f;
480		byte    b[4];
481	} dat1, dat2;
482
483
484	dat1.f = f;
485	dat2.b[0] = dat1.b[3];
486	dat2.b[1] = dat1.b[2];
487	dat2.b[2] = dat1.b[1];
488	dat2.b[3] = dat1.b[0];
489	return dat2.f;
490}
491
492float FloatNoSwap (float f)
493{
494	return f;
495}
496
497/*
498==============================================================================
499
500			MESSAGE IO FUNCTIONS
501
502Handles byte ordering and avoids alignment errors
503==============================================================================
504*/
505
506//
507// writing functions
508//
509
510void MSG_WriteChar (sizebuf_t *sb, int c)
511{
512	byte    *buf;
513
514#ifdef PARANOID
515	if (c < -128 || c > 127)
516		Sys_Error ("MSG_WriteChar: range error");
517#endif
518
519	buf = (byte*) SZ_GetSpace (sb, 1);
520	buf[0] = c;
521}
522
523void MSG_WriteByte (sizebuf_t *sb, int c)
524{
525	byte    *buf;
526
527#ifdef PARANOID
528	if (c < 0 || c > 255)
529		Sys_Error ("MSG_WriteByte: range error");
530#endif
531
532	buf = (byte*) SZ_GetSpace (sb, 1);
533	buf[0] = c;
534}
535
536void MSG_WriteShort (sizebuf_t *sb, int c)
537{
538	byte    *buf;
539
540#ifdef PARANOID
541	if (c < ((short)0x8000) || c > (short)0x7fff)
542		Sys_Error ("MSG_WriteShort: range error");
543#endif
544
545	buf = (byte*) SZ_GetSpace (sb, 2);
546	buf[0] = c&0xff;
547	buf[1] = c>>8;
548}
549
550void MSG_WriteLong (sizebuf_t *sb, int c)
551{
552	byte    *buf;
553
554	buf = (byte*) SZ_GetSpace (sb, 4);
555	buf[0] = c&0xff;
556	buf[1] = (c>>8)&0xff;
557	buf[2] = (c>>16)&0xff;
558	buf[3] = c>>24;
559}
560
561void MSG_WriteFloat (sizebuf_t *sb, float f)
562{
563	union
564	{
565		float   f;
566		int     l;
567	} dat;
568
569
570	dat.f = f;
571	dat.l = LittleLong (dat.l);
572
573	SZ_Write (sb, &dat.l, 4);
574}
575
576void MSG_WriteString (sizebuf_t *sb, const char *s)
577{
578	if (!s)
579		SZ_Write (sb, "", 1);
580	else
581		SZ_Write (sb, s, Q_strlen(s)+1);
582}
583
584void MSG_WriteCoord (sizebuf_t *sb, float f)
585{
586	MSG_WriteShort (sb, (int)(f*8));
587}
588
589void MSG_WriteAngle (sizebuf_t *sb, float f)
590{
591	MSG_WriteByte (sb, ((int)f*256/360) & 255);
592}
593
594//
595// reading functions
596//
597int                     msg_readcount;
598qboolean        msg_badread;
599
600void MSG_BeginReading (void)
601{
602	msg_readcount = 0;
603	msg_badread = false;
604}
605
606// returns -1 and sets msg_badread if no more characters are available
607int MSG_ReadChar (void)
608{
609	int     c;
610
611	if (msg_readcount+1 > net_message.cursize)
612	{
613		msg_badread = true;
614		return -1;
615	}
616
617	c = (signed char)net_message.data[msg_readcount];
618	msg_readcount++;
619
620	return c;
621}
622
623int MSG_ReadByte (void)
624{
625	int     c;
626
627	if (msg_readcount+1 > net_message.cursize)
628	{
629		msg_badread = true;
630		return -1;
631	}
632
633	c = (unsigned char)net_message.data[msg_readcount];
634	msg_readcount++;
635
636	return c;
637}
638
639int MSG_ReadShort (void)
640{
641	int     c;
642
643	if (msg_readcount+2 > net_message.cursize)
644	{
645		msg_badread = true;
646		return -1;
647	}
648
649	c = (short)(net_message.data[msg_readcount]
650	+ (net_message.data[msg_readcount+1]<<8));
651
652	msg_readcount += 2;
653
654	return c;
655}
656
657int MSG_ReadLong (void)
658{
659	int     c;
660
661	if (msg_readcount+4 > net_message.cursize)
662	{
663		msg_badread = true;
664		return -1;
665	}
666
667	c = net_message.data[msg_readcount]
668	+ (net_message.data[msg_readcount+1]<<8)
669	+ (net_message.data[msg_readcount+2]<<16)
670	+ (net_message.data[msg_readcount+3]<<24);
671
672	msg_readcount += 4;
673
674	return c;
675}
676
677float MSG_ReadFloat (void)
678{
679	union
680	{
681		byte    b[4];
682		float   f;
683		int     l;
684	} dat;
685
686	dat.b[0] =      net_message.data[msg_readcount];
687	dat.b[1] =      net_message.data[msg_readcount+1];
688	dat.b[2] =      net_message.data[msg_readcount+2];
689	dat.b[3] =      net_message.data[msg_readcount+3];
690	msg_readcount += 4;
691
692	dat.l = LittleLong (dat.l);
693
694	return dat.f;
695}
696
697char *MSG_ReadString (void)
698{
699	static char     string[2048];
700	int             l,c;
701
702	l = 0;
703	do
704	{
705		c = MSG_ReadChar ();
706		if (c == -1 || c == 0)
707			break;
708		string[l] = c;
709		l++;
710	} while (l < (int) (sizeof(string)-1));
711
712	string[l] = 0;
713
714	return string;
715}
716
717float MSG_ReadCoord (void)
718{
719	return MSG_ReadShort() * (1.0/8);
720}
721
722float MSG_ReadAngle (void)
723{
724	return MSG_ReadChar() * (360.0/256);
725}
726
727
728
729//===========================================================================
730
731void SZ_Alloc (sizebuf_t *buf, int startsize)
732{
733	if (startsize < 256)
734		startsize = 256;
735	buf->data = (byte*) Hunk_AllocName (startsize, "sizebuf");
736	buf->maxsize = startsize;
737	buf->cursize = 0;
738}
739
740
741void SZ_Free (sizebuf_t *buf)
742{
743//      Z_Free (buf->data);
744//      buf->data = NULL;
745//      buf->maxsize = 0;
746	buf->cursize = 0;
747}
748
749void SZ_Clear (sizebuf_t *buf)
750{
751	buf->cursize = 0;
752}
753
754void *SZ_GetSpace (sizebuf_t *buf, int length)
755{
756	void    *data;
757
758	if (buf->cursize + length > buf->maxsize)
759	{
760		if (!buf->allowoverflow)
761			Sys_Error ("SZ_GetSpace: overflow without allowoverflow set");
762
763		if (length > buf->maxsize)
764			Sys_Error ("SZ_GetSpace: %i is > full buffer size", length);
765
766		buf->overflowed = true;
767		Con_Printf ("SZ_GetSpace: overflow");
768		SZ_Clear (buf);
769	}
770
771	data = buf->data + buf->cursize;
772	buf->cursize += length;
773
774	return data;
775}
776
777void SZ_Write (sizebuf_t *buf, const void *data, int length)
778{
779	Q_memcpy (SZ_GetSpace(buf,length),data,length);
780}
781
782void SZ_Print (sizebuf_t *buf, const char *data)
783{
784	int             len;
785
786	len = Q_strlen(data)+1;
787
788// byte * cast to keep VC++ happy
789	if (buf->data[buf->cursize-1])
790		Q_memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
791	else
792		Q_memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
793}
794
795
796//============================================================================
797
798
799/*
800============
801COM_SkipPath
802============
803*/
804const char *COM_SkipPath (const char *pathname)
805{
806	const char    *last;
807
808	last = pathname;
809	while (*pathname)
810	{
811		if (*pathname=='/')
812			last = pathname+1;
813		pathname++;
814	}
815	return last;
816}
817
818/*
819============
820COM_StripExtension
821============
822*/
823void COM_StripExtension (const char *in, char *out)
824{
825	while (*in && *in != '.')
826		*out++ = *in++;
827	*out = 0;
828}
829
830/*
831============
832COM_FileExtension
833============
834*/
835const char *COM_FileExtension (const char *in)
836{
837	static char exten[8];
838	int             i;
839
840	while (*in && *in != '.')
841		in++;
842	if (!*in)
843		return "";
844	in++;
845	for (i=0 ; i<7 && *in ; i++,in++)
846		exten[i] = *in;
847	exten[i] = 0;
848	return exten;
849}
850
851/*
852============
853COM_FileBase
854============
855*/
856void COM_FileBase (const char *in, char *out, size_t outSize)
857{
858	// Get the "base" part of a path, make sure we don't exceed outSize bytes
859
860	const char* start;
861	const char* end;
862	size_t len;
863
864	if(!outSize)
865		return;
866
867	start = strrchr(in, '/');
868	if(start)
869	{
870		start++;
871	}
872	else
873	{
874		start = in;
875	}
876
877	// Start now points to the beginning of the filename part of the file.
878
879	end = strrchr(start, '.');
880
881	if(!end)
882	{
883		end = start + strlen(start);
884	}
885
886	// end now points one character beyond the end of the base part of the file.
887
888	len = end - start;
889	if(len > outSize - 1)
890		len = outSize - 1;
891
892	memcpy(out, start, len);
893	out[len] = 0;
894}
895
896
897/*
898==================
899COM_DefaultExtension
900==================
901*/
902void COM_DefaultExtension (char *path, const char *extension)
903{
904	char    *src;
905//
906// if path doesn't have a .EXT, append extension
907// (extension should include the .)
908//
909	src = path + strlen(path) - 1;
910
911	while (*src != '/' && src != path)
912	{
913		if (*src == '.')
914			return;                 // it has an extension
915		src--;
916	}
917
918	strcat (path, extension);
919}
920
921
922/*
923==============
924COM_Parse
925
926Parse a token out of a string
927==============
928*/
929char *COM_Parse (char *data)
930{
931	int             c;
932	int             len;
933
934	len = 0;
935	com_token[0] = 0;
936
937	if (!data)
938		return NULL;
939
940// skip whitespace
941skipwhite:
942	while ( (c = *data) <= ' ')
943	{
944		if (c == 0)
945			return NULL;                    // end of file;
946		data++;
947	}
948
949// skip // comments
950	if (c=='/' && data[1] == '/')
951	{
952		while (*data && *data != '\n')
953			data++;
954		goto skipwhite;
955	}
956
957
958// handle quoted strings specially
959	if (c == '\"')
960	{
961		data++;
962		while (1)
963		{
964			c = *data++;
965			if (c=='\"' || !c)
966			{
967				com_token[len] = 0;
968				return data;
969			}
970			com_token[len] = c;
971			len++;
972		}
973	}
974
975// parse single characters
976	if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
977	{
978		com_token[len] = c;
979		len++;
980		com_token[len] = 0;
981		return data+1;
982	}
983
984// parse a regular word
985	do
986	{
987		com_token[len] = c;
988		data++;
989		len++;
990		c = *data;
991	if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
992			break;
993	} while (c>32);
994
995	com_token[len] = 0;
996	return data;
997}
998
999
1000/*
1001================
1002COM_CheckParm
1003
1004Returns the position (1 to argc-1) in the program's argument list
1005where the given parameter apears, or 0 if not present
1006================
1007*/
1008int COM_CheckParm (const char *parm)
1009{
1010	int             i;
1011
1012	for (i=1 ; i<com_argc ; i++)
1013	{
1014		if (!com_argv[i])
1015			continue;               // NEXTSTEP sometimes clears appkit vars.
1016		if (!Q_strcmp (parm,com_argv[i]))
1017			return i;
1018	}
1019
1020	return 0;
1021}
1022
1023/*
1024================
1025COM_CheckRegistered
1026
1027Looks for the pop.txt file and verifies it.
1028Sets the "registered" cvar.
1029Immediately exits out if an alternate game was attempted to be started without
1030being registered.
1031================
1032*/
1033void COM_CheckRegistered (void)
1034{
1035	int             h;
1036	unsigned short  check[128];
1037	int                     i;
1038
1039	COM_OpenFile("gfx/pop.lmp", &h);
1040	static_registered = 0;
1041
1042	if (h == -1)
1043	{
1044#if WINDED
1045	Sys_Error ("This dedicated server requires a full registered copy of Quake");
1046#endif
1047		Con_Printf ("Playing shareware version.\n");
1048		if (com_modified)
1049			Sys_Error ("You must have the registered version to use modified games");
1050
1051#ifdef USE_OPENGLES
1052		// For development purposes pretend we're registered. This allows glquake
1053		// file caching to work:
1054
1055		static_registered = 1;
1056#endif // USE_OPENGLES
1057		return;
1058	}
1059
1060	Sys_FileRead (h, check, sizeof(check));
1061	COM_CloseFile (h);
1062
1063	for (i=0 ; i<128 ; i++)
1064		if (pop[i] != (unsigned short)BigShort (check[i]))
1065			Sys_Error ("Corrupted data file.");
1066
1067	Cvar_Set ("cmdline", com_cmdline);
1068	Cvar_Set ("registered", "1");
1069	static_registered = 1;
1070	Con_Printf ("Playing registered version.\n");
1071}
1072
1073
1074void COM_Path_f (void);
1075
1076
1077/*
1078================
1079COM_InitArgv
1080================
1081*/
1082void COM_InitArgv (int argc, const char **argv)
1083{
1084	qboolean        safe;
1085	int             i, j, n;
1086
1087// reconstitute the command line for the cmdline externally visible cvar
1088	n = 0;
1089
1090	for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1091	{
1092		i = 0;
1093
1094		while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1095		{
1096			com_cmdline[n++] = argv[j][i++];
1097		}
1098
1099		if (n < (CMDLINE_LENGTH - 1))
1100			com_cmdline[n++] = ' ';
1101		else
1102			break;
1103	}
1104
1105	com_cmdline[n] = 0;
1106
1107	safe = false;
1108
1109	for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1110		 com_argc++)
1111	{
1112		largv[com_argc] = argv[com_argc];
1113		if (!Q_strcmp ("-safe", argv[com_argc]))
1114			safe = true;
1115	}
1116
1117	if (safe)
1118	{
1119	// force all the safe-mode switches. Note that we reserved extra space in
1120	// case we need to add these, so we don't need an overflow check
1121		for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1122		{
1123			largv[com_argc] = safeargvs[i];
1124			com_argc++;
1125		}
1126	}
1127
1128	largv[com_argc] = argvdummy;
1129	com_argv = largv;
1130
1131	if (COM_CheckParm ("-rogue"))
1132	{
1133		rogue = true;
1134		standard_quake = false;
1135	}
1136
1137	if (COM_CheckParm ("-hipnotic"))
1138	{
1139		hipnotic = true;
1140		standard_quake = false;
1141	}
1142}
1143
1144
1145/*
1146================
1147COM_Init
1148================
1149*/
1150
1151typedef union swapTest_ {
1152    byte b[2];
1153    short s;
1154} swapTest;
1155
1156void COM_Init (const char *basedir)
1157{
1158	swapTest swaptest;
1159	swaptest.b[0] = 1;
1160	swaptest.b[1] = 0;
1161
1162// set the byte swapping variables in a portable manner
1163	if ( swaptest.s == 1)
1164	{
1165		bigendien = false;
1166		BigShort = ShortSwap;
1167		LittleShort = ShortNoSwap;
1168		BigLong = LongSwap;
1169		LittleLong = LongNoSwap;
1170		BigFloat = FloatSwap;
1171		LittleFloat = FloatNoSwap;
1172	}
1173	else
1174	{
1175		bigendien = true;
1176		BigShort = ShortNoSwap;
1177		LittleShort = ShortSwap;
1178		BigLong = LongNoSwap;
1179		LittleLong = LongSwap;
1180		BigFloat = FloatNoSwap;
1181		LittleFloat = FloatSwap;
1182	}
1183
1184	Cvar_RegisterVariable (&registered);
1185	Cvar_RegisterVariable (&cmdline);
1186	Cmd_AddCommand ("path", COM_Path_f);
1187
1188	COM_InitFilesystem ();
1189	COM_CheckRegistered ();
1190}
1191
1192
1193/*
1194============
1195va
1196
1197does a varargs printf into a temp buffer, so I don't need to have
1198varargs versions of all text functions.
1199FIXME: make this buffer size safe someday
1200============
1201*/
1202char    *va(const char *format, ...)
1203{
1204	va_list         argptr;
1205	static char             string[1024];
1206
1207	va_start (argptr, format);
1208	vsprintf (string, format,argptr);
1209	va_end (argptr);
1210
1211	return string;
1212}
1213
1214
1215/// just for debugging
1216int     memsearch (const byte *start, int count, int search)
1217{
1218	int             i;
1219
1220	for (i=0 ; i<count ; i++)
1221		if (start[i] == search)
1222			return i;
1223	return -1;
1224}
1225
1226/*
1227=============================================================================
1228
1229QUAKE FILESYSTEM
1230
1231=============================================================================
1232*/
1233
1234int     com_filesize;
1235
1236
1237//
1238// in memory
1239//
1240
1241typedef struct
1242{
1243	char    name[MAX_QPATH];
1244	int             filepos, filelen;
1245} packfile_t;
1246
1247typedef struct pack_s
1248{
1249	char    filename[MAX_OSPATH];
1250	int             handle;
1251	int             numfiles;
1252	packfile_t      *files;
1253} pack_t;
1254
1255//
1256// on disk
1257//
1258typedef struct
1259{
1260	char    name[56];
1261	int             filepos, filelen;
1262} dpackfile_t;
1263
1264typedef struct
1265{
1266	char    id[4];
1267	int             dirofs;
1268	int             dirlen;
1269} dpackheader_t;
1270
1271#define MAX_FILES_IN_PACK       2048
1272
1273char    com_cachedir[MAX_OSPATH];
1274char    com_gamedir[MAX_OSPATH];
1275
1276typedef struct searchpath_s
1277{
1278	char    filename[MAX_OSPATH];
1279	pack_t  *pack;          // only one of filename / pack will be used
1280	struct searchpath_s *next;
1281} searchpath_t;
1282
1283searchpath_t    *com_searchpaths;
1284
1285/*
1286============
1287COM_Path_f
1288
1289============
1290*/
1291void COM_Path_f (void)
1292{
1293	searchpath_t    *s;
1294
1295	Con_Printf ("Current search path:\n");
1296	for (s=com_searchpaths ; s ; s=s->next)
1297	{
1298		if (s->pack)
1299		{
1300			Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1301		}
1302		else
1303			Con_Printf ("%s\n", s->filename);
1304	}
1305}
1306
1307/*
1308============
1309COM_WriteFile
1310
1311The filename will be prefixed by the current game directory
1312============
1313*/
1314void COM_WriteFile (const char *filename, void *data, int len)
1315{
1316	int             handle;
1317	char    name[MAX_OSPATH];
1318
1319	sprintf (name, "%s/%s", com_gamedir, filename);
1320
1321	handle = Sys_FileOpenWrite (name);
1322	if (handle == -1)
1323	{
1324		Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1325		return;
1326	}
1327
1328	Sys_Printf ("COM_WriteFile: %s\n", name);
1329	Sys_FileWrite (handle, data, len);
1330	Sys_FileClose (handle);
1331}
1332
1333
1334/*
1335============
1336COM_CreatePath
1337
1338Only used for CopyFile
1339============
1340*/
1341void    COM_CreatePath (const char *path)
1342{
1343    char    *ofs;
1344
1345	for (ofs = (char*) path+1 ; *ofs ; ofs++)
1346	{
1347		if (*ofs == '/')
1348		{       // create the directory
1349			*ofs = 0;
1350			Sys_mkdir (path);
1351			*ofs = '/';
1352		}
1353	}
1354}
1355
1356
1357/*
1358===========
1359COM_CopyFile
1360
1361Copies a file over from the net to the local cache, creating any directories
1362needed.  This is for the convenience of developers using ISDN from home.
1363===========
1364*/
1365void COM_CopyFile (const char *netpath, const char *cachepath)
1366{
1367	int             in, out;
1368	int             remaining, count;
1369	char    buf[4096];
1370
1371	remaining = Sys_FileOpenRead (netpath, &in);
1372	COM_CreatePath (cachepath);     // create directories up to the cache file
1373	out = Sys_FileOpenWrite (cachepath);
1374
1375	while (remaining)
1376	{
1377		if (remaining < (int) sizeof(buf))
1378			count = remaining;
1379		else
1380			count = sizeof(buf);
1381		Sys_FileRead (in, buf, count);
1382		Sys_FileWrite (out, buf, count);
1383		remaining -= count;
1384	}
1385
1386	Sys_FileClose (in);
1387	Sys_FileClose (out);
1388}
1389
1390/*
1391===========
1392COM_FindFile
1393
1394Finds the file in the search path.
1395Sets com_filesize and one of handle or file
1396===========
1397*/
1398int COM_FindFile (const char *filename, int *handle, FILE **file)
1399{
1400	searchpath_t    *search;
1401	char            netpath[MAX_OSPATH];
1402	char            cachepath[MAX_OSPATH];
1403	pack_t          *pak;
1404	int                     i;
1405	int                     findtime, cachetime;
1406
1407	if (file && handle)
1408		Sys_Error ("COM_FindFile: both handle and file set");
1409	if (!file && !handle)
1410		Sys_Error ("COM_FindFile: neither handle or file set");
1411
1412//
1413// search through the path, one element at a time
1414//
1415	search = com_searchpaths;
1416	if (proghack)
1417	{	// gross hack to use quake 1 progs with quake 2 maps
1418		if (!strcmp(filename, "progs.dat"))
1419			search = search->next;
1420	}
1421
1422	for ( ; search ; search = search->next)
1423	{
1424	// is the element a pak file?
1425		if (search->pack)
1426		{
1427		// look through all the pak file elements
1428			pak = search->pack;
1429			for (i=0 ; i<pak->numfiles ; i++)
1430				if (!strcmp (pak->files[i].name, filename))
1431				{       // found it!
1432					// Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename);
1433					if (handle)
1434					{
1435						*handle = pak->handle;
1436						Sys_FileSeek (pak->handle, pak->files[i].filepos);
1437					}
1438					else
1439					{       // open a new file on the pakfile
1440						*file = fopen (pak->filename, "rb");
1441						if (*file)
1442							fseek (*file, pak->files[i].filepos, SEEK_SET);
1443					}
1444					com_filesize = pak->files[i].filelen;
1445					return com_filesize;
1446				}
1447		}
1448		else
1449		{
1450	// check a file in the directory tree
1451			if (!static_registered)
1452			{       // if not a registered version, don't ever go beyond base
1453				if ( strchr (filename, '/') || strchr (filename,'\\'))
1454					continue;
1455			}
1456
1457			sprintf (netpath, "%s/%s",search->filename, filename);
1458
1459			findtime = Sys_FileTime (netpath);
1460			if (findtime == -1)
1461				continue;
1462
1463		// see if the file needs to be updated in the cache
1464			if (!com_cachedir[0])
1465				strcpy (cachepath, netpath);
1466			else
1467			{
1468#if defined(_WIN32)
1469				if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1470					sprintf (cachepath,"%s%s", com_cachedir, netpath);
1471				else
1472					sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1473#else
1474				sprintf (cachepath,"%s%s", com_cachedir, netpath);
1475#endif
1476
1477				cachetime = Sys_FileTime (cachepath);
1478
1479				if (cachetime < findtime)
1480					COM_CopyFile (netpath, cachepath);
1481				strcpy (netpath, cachepath);
1482			}
1483
1484			// Sys_Printf ("FindFile: %s\n",netpath);
1485			com_filesize = Sys_FileOpenRead (netpath, &i);
1486			if (handle)
1487				*handle = i;
1488			else
1489			{
1490				Sys_FileClose (i);
1491				*file = fopen (netpath, "rb");
1492			}
1493			return com_filesize;
1494		}
1495
1496	}
1497
1498	Sys_Printf ("FindFile: can't find %s\n", filename);
1499
1500	if (handle)
1501		*handle = -1;
1502	else
1503		*file = NULL;
1504	com_filesize = -1;
1505	return -1;
1506}
1507
1508
1509/*
1510===========
1511COM_OpenFile
1512
1513filename never has a leading slash, but may contain directory walks
1514returns a handle and a length
1515it may actually be inside a pak file
1516===========
1517*/
1518int COM_OpenFile (const char *filename, int *handle)
1519{
1520	return COM_FindFile (filename, handle, NULL);
1521}
1522
1523/*
1524===========
1525COM_FOpenFile
1526
1527If the requested file is inside a packfile, a new FILE * will be opened
1528into the file.
1529===========
1530*/
1531int COM_FOpenFile (const char *filename, FILE **file)
1532{
1533	return COM_FindFile (filename, NULL, file);
1534}
1535
1536/*
1537============
1538COM_CloseFile
1539
1540If it is a pak file handle, don't really close it
1541============
1542*/
1543void COM_CloseFile (int h)
1544{
1545	searchpath_t    *s;
1546
1547	for (s = com_searchpaths ; s ; s=s->next)
1548		if (s->pack && s->pack->handle == h)
1549			return;
1550
1551	Sys_FileClose (h);
1552}
1553
1554
1555/*
1556============
1557COM_LoadFile
1558
1559Filename are reletive to the quake directory.
1560Allways appends a 0 byte.
1561============
1562*/
1563cache_user_t *loadcache;
1564byte    *loadbuf;
1565int             loadsize;
1566byte *COM_LoadFile (const char *path, int usehunk)
1567{
1568	int             h;
1569	byte    *buf;
1570	char    base[32];
1571	int             len;
1572
1573	buf = NULL;     // quiet compiler warning
1574
1575// look for it in the filesystem or pack files
1576	len = COM_OpenFile (path, &h);
1577	if (h == -1)
1578		return NULL;
1579
1580// extract the filename base name for hunk tag
1581	COM_FileBase (path, base, sizeof(base));
1582
1583	if (usehunk == 1)
1584		buf = (byte*) Hunk_AllocName (len+1, base);
1585	else if (usehunk == 2)
1586		buf = (byte*) Hunk_TempAlloc (len+1);
1587	else if (usehunk == 0)
1588		buf = (byte*) Z_Malloc (len+1);
1589	else if (usehunk == 3)
1590		buf = (byte*) Cache_Alloc (loadcache, len+1, base);
1591	else if (usehunk == 4)
1592	{
1593		if (len+1 > loadsize)
1594			buf = (byte*) Hunk_TempAlloc (len+1);
1595		else
1596			buf = loadbuf;
1597	}
1598	else
1599		Sys_Error ("COM_LoadFile: bad usehunk");
1600
1601	if (!buf)
1602		Sys_Error ("COM_LoadFile: not enough space for %s", path);
1603
1604	((byte *)buf)[len] = 0;
1605
1606	Draw_BeginDisc ();
1607	Sys_FileRead (h, buf, len);
1608	COM_CloseFile (h);
1609	Draw_EndDisc ();
1610
1611	return buf;
1612}
1613
1614byte *COM_LoadHunkFile (const char *path)
1615{
1616	return COM_LoadFile (path, 1);
1617}
1618
1619byte *COM_LoadTempFile (const char *path)
1620{
1621	return COM_LoadFile (path, 2);
1622}
1623
1624void COM_LoadCacheFile (char *path, struct cache_user_s *cu)
1625{
1626	loadcache = cu;
1627	COM_LoadFile (path, 3);
1628}
1629
1630// uses temp hunk if larger than bufsize
1631byte *COM_LoadStackFile (const char *path, void *buffer, int bufsize)
1632{
1633	byte    *buf;
1634
1635	loadbuf = (byte *)buffer;
1636	loadsize = bufsize;
1637	buf = COM_LoadFile (path, 4);
1638
1639	return buf;
1640}
1641
1642/*
1643=================
1644COM_LoadPackFile
1645
1646Takes an explicit (not game tree related) path to a pak file.
1647
1648Loads the header and directory, adding the files at the beginning
1649of the list so they override previous pack files.
1650=================
1651*/
1652pack_t *COM_LoadPackFile (const char *packfile)
1653{
1654	dpackheader_t   header;
1655	int                             i;
1656	packfile_t              *newfiles;
1657	int                             numpackfiles;
1658	pack_t                  *pack;
1659	int                             packhandle;
1660	dpackfile_t             info[MAX_FILES_IN_PACK];
1661	unsigned short          crc;
1662
1663	if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1664	{
1665//              Con_Printf ("Couldn't open %s\n", packfile);
1666		return NULL;
1667	}
1668	Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1669	if (header.id[0] != 'P' || header.id[1] != 'A'
1670	|| header.id[2] != 'C' || header.id[3] != 'K')
1671		Sys_Error ("%s is not a packfile", packfile);
1672	header.dirofs = LittleLong (header.dirofs);
1673	header.dirlen = LittleLong (header.dirlen);
1674
1675	numpackfiles = header.dirlen / sizeof(dpackfile_t);
1676
1677	if (numpackfiles > MAX_FILES_IN_PACK)
1678		Sys_Error ("%s has %i files", packfile, numpackfiles);
1679
1680	if (numpackfiles != PAK0_COUNT)
1681		com_modified = true;    // not the original file
1682
1683	newfiles = (packfile_t*) Hunk_AllocName (numpackfiles * sizeof(packfile_t), "packfile");
1684
1685	Sys_FileSeek (packhandle, header.dirofs);
1686	Sys_FileRead (packhandle, (void *)info, header.dirlen);
1687
1688// crc the directory to check for modifications
1689	CRC_Init (&crc);
1690	for (i=0 ; i<header.dirlen ; i++)
1691		CRC_ProcessByte (&crc, ((byte *)info)[i]);
1692	if (crc != PAK0_CRC)
1693		com_modified = true;
1694
1695// parse the directory
1696	for (i=0 ; i<numpackfiles ; i++)
1697	{
1698		strcpy (newfiles[i].name, info[i].name);
1699		newfiles[i].filepos = LittleLong(info[i].filepos);
1700		newfiles[i].filelen = LittleLong(info[i].filelen);
1701	}
1702
1703	pack = (pack_t*) Hunk_Alloc (sizeof (pack_t));
1704	strcpy (pack->filename, packfile);
1705	pack->handle = packhandle;
1706	pack->numfiles = numpackfiles;
1707	pack->files = newfiles;
1708
1709	Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1710	return pack;
1711}
1712
1713
1714/*
1715================
1716COM_AddGameDirectory
1717
1718Sets com_gamedir, adds the directory to the head of the path,
1719then loads and adds pak1.pak pak2.pak ...
1720================
1721*/
1722void COM_AddGameDirectory (char *dir)
1723{
1724	int                             i;
1725	searchpath_t    *search;
1726	pack_t                  *pak;
1727	char                    pakfile[MAX_OSPATH];
1728
1729	strcpy (com_gamedir, dir);
1730
1731//
1732// add the directory to the search path
1733//
1734	search = (searchpath_t*) Hunk_Alloc (sizeof(searchpath_t));
1735	strcpy (search->filename, dir);
1736	search->next = com_searchpaths;
1737	com_searchpaths = search;
1738
1739//
1740// add any pak files in the format pak0.pak pak1.pak, ...
1741//
1742	for (i=0 ; ; i++)
1743	{
1744		sprintf (pakfile, "%s/pak%i.pak", dir, i);
1745		pak = COM_LoadPackFile (pakfile);
1746		if (!pak)
1747			break;
1748		search = (searchpath_t*) Hunk_Alloc (sizeof(searchpath_t));
1749		search->pack = pak;
1750		search->next = com_searchpaths;
1751		com_searchpaths = search;
1752	}
1753
1754//
1755// add the contents of the parms.txt file to the end of the command line
1756//
1757
1758}
1759
1760/*
1761================
1762COM_InitFilesystem
1763================
1764*/
1765void COM_InitFilesystem (void)
1766{
1767	int             i, j;
1768	char    basedir[MAX_OSPATH];
1769	searchpath_t    *search;
1770
1771//
1772// -basedir <path>
1773// Overrides the system supplied base directory (under GAMENAME)
1774//
1775	i = COM_CheckParm ("-basedir");
1776	if (i && i < com_argc-1)
1777		strcpy (basedir, com_argv[i+1]);
1778	else
1779		strcpy (basedir, host_parms.basedir);
1780
1781	j = strlen (basedir);
1782
1783	if (j > 0)
1784	{
1785		if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1786			basedir[j-1] = 0;
1787	}
1788
1789//
1790// -cachedir <path>
1791// Overrides the system supplied cache directory (NULL or /qcache)
1792// -cachedir - will disable caching.
1793//
1794	i = COM_CheckParm ("-cachedir");
1795	if (i && i < com_argc-1)
1796	{
1797		if (com_argv[i+1][0] == '-')
1798			com_cachedir[0] = 0;
1799		else
1800			strcpy (com_cachedir, com_argv[i+1]);
1801	}
1802	else if (host_parms.cachedir)
1803		strcpy (com_cachedir, host_parms.cachedir);
1804	else
1805		com_cachedir[0] = 0;
1806
1807//
1808// start up with GAMENAME by default (id1)
1809//
1810	COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1811
1812	if (COM_CheckParm ("-rogue"))
1813		COM_AddGameDirectory (va("%s/rogue", basedir) );
1814	if (COM_CheckParm ("-hipnotic"))
1815		COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1816
1817//
1818// -game <gamedir>
1819// Adds basedir/gamedir as an override game
1820//
1821	i = COM_CheckParm ("-game");
1822	if (i && i < com_argc-1)
1823	{
1824		com_modified = true;
1825		COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1826	}
1827
1828//
1829// -path <dir or packfile> [<dir or packfile>] ...
1830// Fully specifies the exact serach path, overriding the generated one
1831//
1832	i = COM_CheckParm ("-path");
1833	if (i)
1834	{
1835		com_modified = true;
1836		com_searchpaths = NULL;
1837		while (++i < com_argc)
1838		{
1839			if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1840				break;
1841
1842			search = (searchpath_t*) Hunk_Alloc (sizeof(searchpath_t));
1843			if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1844			{
1845				search->pack = COM_LoadPackFile (com_argv[i]);
1846				if (!search->pack)
1847					Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1848			}
1849			else
1850				strcpy (search->filename, com_argv[i]);
1851			search->next = com_searchpaths;
1852			com_searchpaths = search;
1853		}
1854	}
1855
1856	if (COM_CheckParm ("-proghack"))
1857		proghack = true;
1858}
1859
1860
1861