1 /*
2 Copyright (C) 1996-2001 Id Software, Inc.
3 Copyright (C) 2002-2009 John Fitzgibbons and others
4 Copyright (C) 2010-2014 QuakeSpasm developers
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 
15 See the GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 
21 */
22 
23 // common.c -- misc functions used in client and server
24 
25 #include "quakedef.h"
26 #include "q_ctype.h"
27 #include <errno.h>
28 
29 #include "miniz.h"
30 
31 static char	*largv[MAX_NUM_ARGVS + 1];
32 static char	argvdummy[] = " ";
33 
34 int		safemode;
35 
36 cvar_t	registered = {"registered","1",CVAR_ROM}; /* set to correct value in COM_CheckRegistered() */
37 cvar_t	cmdline = {"cmdline","",CVAR_ROM/*|CVAR_SERVERINFO*/}; /* sending cmdline upon CCREQ_RULE_INFO is evil */
38 
39 static qboolean		com_modified;	// set true if using non-id files
40 
41 qboolean		fitzmode;
42 
43 static void COM_Path_f (void);
44 
45 // if a packfile directory differs from this, it is assumed to be hacked
46 #define PAK0_COUNT		339	/* id1/pak0.pak - v1.0x */
47 #define PAK0_CRC_V100		13900	/* id1/pak0.pak - v1.00 */
48 #define PAK0_CRC_V101		62751	/* id1/pak0.pak - v1.01 */
49 #define PAK0_CRC_V106		32981	/* id1/pak0.pak - v1.06 */
50 #define PAK0_CRC	(PAK0_CRC_V106)
51 #define PAK0_COUNT_V091		308	/* id1/pak0.pak - v0.91/0.92, not supported */
52 #define PAK0_CRC_V091		28804	/* id1/pak0.pak - v0.91/0.92, not supported */
53 
54 char	com_token[1024];
55 int		com_argc;
56 char	**com_argv;
57 
58 #define CMDLINE_LENGTH	256		/* johnfitz -- mirrored in cmd.c */
59 char	com_cmdline[CMDLINE_LENGTH];
60 
61 qboolean standard_quake = true, rogue, hipnotic;
62 
63 // this graphic needs to be in the pak file to use registered features
64 static unsigned short pop[] =
65 {
66 	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
67 	0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000,
68 	0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000,
69 	0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600,
70 	0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563,
71 	0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564,
72 	0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564,
73 	0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563,
74 	0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500,
75 	0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200,
76 	0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000,
77 	0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000,
78 	0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000,
79 	0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000,
80 	0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000,
81 	0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000
82 };
83 
84 /*
85 
86 All of Quake's data access is through a hierchal file system, but the contents
87 of the file system can be transparently merged from several sources.
88 
89 The "base directory" is the path to the directory holding the quake.exe and all
90 game directories.  The sys_* files pass this to host_init in quakeparms_t->basedir.
91 This can be overridden with the "-basedir" command line parm to allow code
92 debugging in a different directory.  The base directory is only used during
93 filesystem initialization.
94 
95 The "game directory" is the first tree on the search path and directory that all
96 generated files (savegames, screenshots, demos, config files) will be saved to.
97 This can be overridden with the "-game" command line parameter.  The game
98 directory can never be changed while quake is executing.  This is a precacution
99 against having a malicious server instruct clients to write files over areas they
100 shouldn't.
101 
102 The "cache directory" is only used during development to save network bandwidth,
103 especially over ISDN / T1 lines.  If there is a cache directory specified, when
104 a file is found by the normal search path, it will be mirrored into the cache
105 directory, then opened there.
106 
107 FIXME:
108 The file "parms.txt" will be read out of the game directory and appended to the
109 current command line arguments to allow different games to initialize startup
110 parms differently.  This could be used to add a "-sspeed 22050" for the high
111 quality sound edition.  Because they are added at the end, they will not
112 override an explicit setting on the original command line.
113 
114 */
115 
116 //============================================================================
117 
118 
119 // ClearLink is used for new headnodes
ClearLink(link_t * l)120 void ClearLink (link_t *l)
121 {
122 	l->prev = l->next = l;
123 }
124 
RemoveLink(link_t * l)125 void RemoveLink (link_t *l)
126 {
127 	l->next->prev = l->prev;
128 	l->prev->next = l->next;
129 }
130 
InsertLinkBefore(link_t * l,link_t * before)131 void InsertLinkBefore (link_t *l, link_t *before)
132 {
133 	l->next = before;
134 	l->prev = before->prev;
135 	l->prev->next = l;
136 	l->next->prev = l;
137 }
138 
InsertLinkAfter(link_t * l,link_t * after)139 void InsertLinkAfter (link_t *l, link_t *after)
140 {
141 	l->next = after->next;
142 	l->prev = after;
143 	l->prev->next = l;
144 	l->next->prev = l;
145 }
146 
147 /*
148 ============================================================================
149 
150 					LIBRARY REPLACEMENT FUNCTIONS
151 
152 ============================================================================
153 */
154 
q_strcasecmp(const char * s1,const char * s2)155 int q_strcasecmp(const char * s1, const char * s2)
156 {
157 	const char * p1 = s1;
158 	const char * p2 = s2;
159 	char c1, c2;
160 
161 	if (p1 == p2)
162 		return 0;
163 
164 	do
165 	{
166 		c1 = q_tolower (*p1++);
167 		c2 = q_tolower (*p2++);
168 		if (c1 == '\0')
169 			break;
170 	} while (c1 == c2);
171 
172 	return (int)(c1 - c2);
173 }
174 
q_strncasecmp(const char * s1,const char * s2,size_t n)175 int q_strncasecmp(const char *s1, const char *s2, size_t n)
176 {
177 	const char * p1 = s1;
178 	const char * p2 = s2;
179 	char c1, c2;
180 
181 	if (p1 == p2 || n == 0)
182 		return 0;
183 
184 	do
185 	{
186 		c1 = q_tolower (*p1++);
187 		c2 = q_tolower (*p2++);
188 		if (c1 == '\0' || c1 != c2)
189 			break;
190 	} while (--n > 0);
191 
192 	return (int)(c1 - c2);
193 }
194 
195 //spike -- grabbed this from fte, because its useful to me
q_strcasestr(const char * haystack,const char * needle)196 char *q_strcasestr(const char *haystack, const char *needle)
197 {
198 	int c1, c2, c2f;
199 	int i;
200 	c2f = *needle;
201 	if (c2f >= 'a' && c2f <= 'z')
202 		c2f -= ('a' - 'A');
203 	if (!c2f)
204 		return (char*)haystack;
205 	while (1)
206 	{
207 		c1 = *haystack;
208 		if (!c1)
209 			return NULL;
210 		if (c1 >= 'a' && c1 <= 'z')
211 			c1 -= ('a' - 'A');
212 		if (c1 == c2f)
213 		{
214 			for (i = 1; ; i++)
215 			{
216 				c1 = haystack[i];
217 				c2 = needle[i];
218 				if (c1 >= 'a' && c1 <= 'z')
219 					c1 -= ('a' - 'A');
220 				if (c2 >= 'a' && c2 <= 'z')
221 					c2 -= ('a' - 'A');
222 				if (!c2)
223 					return (char*)haystack;	//end of needle means we found a complete match
224 				if (!c1)	//end of haystack means we can't possibly find needle in it any more
225 					return NULL;
226 				if (c1 != c2)	//mismatch means no match starting at haystack[0]
227 					break;
228 			}
229 		}
230 		haystack++;
231 	}
232 	return NULL;	//didn't find it
233 }
234 
q_strlwr(char * str)235 char *q_strlwr (char *str)
236 {
237 	char	*c;
238 	c = str;
239 	while (*c)
240 	{
241 		*c = q_tolower(*c);
242 		c++;
243 	}
244 	return str;
245 }
246 
q_strupr(char * str)247 char *q_strupr (char *str)
248 {
249 	char	*c;
250 	c = str;
251 	while (*c)
252 	{
253 		*c = q_toupper(*c);
254 		c++;
255 	}
256 	return str;
257 }
258 
259 /* platform dependant (v)snprintf function names: */
260 #if defined(_WIN32)
261 #define	snprintf_func		_snprintf
262 #define	vsnprintf_func		_vsnprintf
263 #else
264 #define	snprintf_func		snprintf
265 #define	vsnprintf_func		vsnprintf
266 #endif
267 
q_vsnprintf(char * str,size_t size,const char * format,va_list args)268 int q_vsnprintf(char *str, size_t size, const char *format, va_list args)
269 {
270 	int		ret;
271 
272 	ret = vsnprintf_func (str, size, format, args);
273 
274 	if (ret < 0)
275 		ret = (int)size;
276 	if (size == 0)	/* no buffer */
277 		return ret;
278 	if ((size_t)ret >= size)
279 		str[size - 1] = '\0';
280 
281 	return ret;
282 }
283 
q_snprintf(char * str,size_t size,const char * format,...)284 int q_snprintf (char *str, size_t size, const char *format, ...)
285 {
286 	int		ret;
287 	va_list		argptr;
288 
289 	va_start (argptr, format);
290 	ret = q_vsnprintf (str, size, format, argptr);
291 	va_end (argptr);
292 
293 	return ret;
294 }
295 
Q_memset(void * dest,int fill,size_t count)296 void Q_memset (void *dest, int fill, size_t count)
297 {
298 	size_t		i;
299 
300 	if ( (((uintptr_t)dest | count) & 3) == 0)
301 	{
302 		count >>= 2;
303 		uint32_t fill32 = (uint32_t)fill | ((uint32_t)fill<<8) | ((uint32_t)fill<<16) | ((uint32_t)fill<<24);;
304 		for (i = 0; i < count; i++)
305 			((uint32_t *)dest)[i] = fill32;
306 	}
307 	else
308 		for (i = 0; i < count; i++)
309 			((byte *)dest)[i] = fill;
310 }
311 
Q_memcpy(void * dest,const void * src,size_t count)312 void Q_memcpy (void *dest, const void *src, size_t count)
313 {
314 	size_t		i;
315 
316 	if (( ( (uintptr_t)dest | (uintptr_t)src | count) & 3) == 0)
317 	{
318 		count >>= 2;
319 		for (i = 0; i < count; i++)
320 			((int *)dest)[i] = ((int *)src)[i];
321 	}
322 	else
323 		for (i = 0; i < count; i++)
324 			((byte *)dest)[i] = ((byte *)src)[i];
325 }
326 
Q_memcmp(const void * m1,const void * m2,size_t count)327 int Q_memcmp (const void *m1, const void *m2, size_t count)
328 {
329 	while(count)
330 	{
331 		count--;
332 		if (((byte *)m1)[count] != ((byte *)m2)[count])
333 			return -1;
334 	}
335 	return 0;
336 }
337 
Q_strcpy(char * dest,const char * src)338 void Q_strcpy (char *dest, const char *src)
339 {
340 	while (*src)
341 	{
342 		*dest++ = *src++;
343 	}
344 	*dest++ = 0;
345 }
346 
Q_strncpy(char * dest,const char * src,int count)347 void Q_strncpy (char *dest, const char *src, int count)
348 {
349 	while (*src && count--)
350 	{
351 		*dest++ = *src++;
352 	}
353 	if (count)
354 		*dest++ = 0;
355 }
356 
Q_strlen(const char * str)357 int Q_strlen (const char *str)
358 {
359 	int		count;
360 
361 	count = 0;
362 	while (str[count])
363 		count++;
364 
365 	return count;
366 }
367 
Q_strrchr(const char * s,char c)368 char *Q_strrchr(const char *s, char c)
369 {
370 	int len = Q_strlen(s);
371 	s += len;
372 	while (len--)
373 	{
374 		if (*--s == c)
375 			return (char *)s;
376 	}
377 	return NULL;
378 }
379 
Q_strcat(char * dest,const char * src)380 void Q_strcat (char *dest, const char *src)
381 {
382 	dest += Q_strlen(dest);
383 	Q_strcpy (dest, src);
384 }
385 
Q_strcmp(const char * s1,const char * s2)386 int Q_strcmp (const char *s1, const char *s2)
387 {
388 	while (1)
389 	{
390 		if (*s1 != *s2)
391 			return -1;		// strings not equal
392 		if (!*s1)
393 			return 0;		// strings are equal
394 		s1++;
395 		s2++;
396 	}
397 
398 	return -1;
399 }
400 
Q_strncmp(const char * s1,const char * s2,int count)401 int Q_strncmp (const char *s1, const char *s2, int count)
402 {
403 	while (1)
404 	{
405 		if (!count--)
406 			return 0;
407 		if (*s1 != *s2)
408 			return -1;		// strings not equal
409 		if (!*s1)
410 			return 0;		// strings are equal
411 		s1++;
412 		s2++;
413 	}
414 
415 	return -1;
416 }
417 
Q_atoi(const char * str)418 int Q_atoi (const char *str)
419 {
420 	int		val;
421 	int		sign;
422 	int		c;
423 
424 	if (*str == '-')
425 	{
426 		sign = -1;
427 		str++;
428 	}
429 	else
430 		sign = 1;
431 
432 	val = 0;
433 
434 //
435 // check for hex
436 //
437 	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
438 	{
439 		str += 2;
440 		while (1)
441 		{
442 			c = *str++;
443 			if (c >= '0' && c <= '9')
444 				val = (val<<4) + c - '0';
445 			else if (c >= 'a' && c <= 'f')
446 				val = (val<<4) + c - 'a' + 10;
447 			else if (c >= 'A' && c <= 'F')
448 				val = (val<<4) + c - 'A' + 10;
449 			else
450 				return val*sign;
451 		}
452 	}
453 
454 //
455 // check for character
456 //
457 	if (str[0] == '\'')
458 	{
459 		return sign * str[1];
460 	}
461 
462 //
463 // assume decimal
464 //
465 	while (1)
466 	{
467 		c = *str++;
468 		if (c <'0' || c > '9')
469 			return val*sign;
470 		val = val*10 + c - '0';
471 	}
472 
473 	return 0;
474 }
475 
476 
Q_atof(const char * str)477 float Q_atof (const char *str)
478 {
479 	double		val;
480 	int		sign;
481 	int		c;
482 	int	decimal, total;
483 
484 	if (*str == '-')
485 	{
486 		sign = -1;
487 		str++;
488 	}
489 	else
490 		sign = 1;
491 
492 	val = 0;
493 
494 //
495 // check for hex
496 //
497 	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
498 	{
499 		str += 2;
500 		while (1)
501 		{
502 			c = *str++;
503 			if (c >= '0' && c <= '9')
504 				val = (val*16) + c - '0';
505 			else if (c >= 'a' && c <= 'f')
506 				val = (val*16) + c - 'a' + 10;
507 			else if (c >= 'A' && c <= 'F')
508 				val = (val*16) + c - 'A' + 10;
509 			else
510 				return val*sign;
511 		}
512 	}
513 
514 //
515 // check for character
516 //
517 	if (str[0] == '\'')
518 	{
519 		return sign * str[1];
520 	}
521 
522 //
523 // assume decimal
524 //
525 	decimal = -1;
526 	total = 0;
527 	while (1)
528 	{
529 		c = *str++;
530 		if (c == '.')
531 		{
532 			decimal = total;
533 			continue;
534 		}
535 		if (c <'0' || c > '9')
536 			break;
537 		val = val*10 + c - '0';
538 		total++;
539 	}
540 
541 	if (decimal == -1)
542 		return val*sign;
543 	while (total > decimal)
544 	{
545 		val /= 10;
546 		total--;
547 	}
548 
549 	return val*sign;
550 }
551 
552 // Q_ftoa: convert IEEE 754 float to a base-10 string with "infinite" decimal places
Q_ftoa(char * str,float in)553 void Q_ftoa(char *str, float in)
554 {
555 	struct {
556 		float f;
557 		unsigned int i;
558 	} u = {in};
559 
560 	int signbit = (u.i & 0x80000000) >> 31;
561 	int exp = (signed int)((u.i & 0x7F800000) >> 23) - 127;
562 	int mantissa = (u.i & 0x007FFFFF);
563 
564 	if (exp == 128) // 255(NaN/Infinity bits) - 127(bias)
565 	{
566 		if (signbit)
567 		{
568 			*str = '-';
569 			str++;
570 		}
571 		if (mantissa == 0) // infinity
572 			strcpy(str, "1.#INF");
573 		else // NaN or indeterminate
574 			strcpy(str, "1.#NAN");
575 		return;
576 	}
577 
578 	exp = -exp;
579 	exp = (int)(exp * 0.30102999957f); // convert base 2 to base 10
580 	exp += 8;
581 
582 	if (exp <= 0)
583 		sprintf(str, "%.0f", in);
584 	else
585 	{
586 		char tstr[32];
587 		char *lsig = str - 1;
588 		sprintf(tstr, "%%.%if", exp);
589 		sprintf(str, tstr, in);
590 		// find last significant digit and trim
591 		while (*str)
592 		{
593 			if (*str >= '1' && *str <= '9')
594 				lsig = str;
595 			else if (*str == '.')
596 				lsig = str - 1;
597 			str++;
598 		}
599 		lsig[1] = '\0';
600 	}
601 }
602 
wildcmp(const char * wild,const char * string)603 int wildcmp(const char *wild, const char *string)
604 {	//case-insensitive string compare with wildcards. returns true for a match.
605 	while (*string)
606 	{
607 		if (*wild == '*')
608 		{
609 			if (*string == '/' || *string == '\\')
610 			{
611 				//* terminates if we get a match on the char following it, or if its a \ or / char
612 				wild++;
613 				continue;
614 			}
615 			if (wildcmp(wild+1, string))
616 				return true;
617 			string++;
618 		}
619 		else if ((q_tolower(*wild) == q_tolower(*string)) || (*wild == '?'))
620 		{
621 			//this char matches
622 			wild++;
623 			string++;
624 		}
625 		else
626 		{
627 			//failure
628 			return false;
629 		}
630 	}
631 
632 	while (*wild == '*')
633 	{
634 		wild++;
635 	}
636 	return !*wild;
637 }
638 
Info_RemoveKey(char * info,const char * key)639 void Info_RemoveKey(char *info, const char *key)
640 {	//only shrinks, so no need for max size.
641 	size_t keylen = strlen(key);
642 
643 	while(*info)
644 	{
645 		char *l = info;
646 		if (*info++ != '\\')
647 			break;	//error / end-of-string
648 
649 		if (!strncmp(info, key, keylen) && info[keylen] == '\\')
650 		{
651 			//skip the key name
652 			info += keylen+1;
653 			//this is the old value for the key. skip over it
654 			while (*info && *info != '\\')
655 				info++;
656 
657 			//okay, we found it. strip it out now.
658 			memmove(l, info, strlen(info)+1);
659 			return;
660 		}
661 		else
662 		{
663 			//skip the key
664 			while (*info && *info != '\\')
665 				info++;
666 
667 			//validate that its a value now
668 			if (*info++ != '\\')
669 				break;	//error
670 			//skip the value
671 			while (*info && *info != '\\')
672 				info++;
673 		}
674 	}
675 }
Info_SetKey(char * info,size_t infosize,const char * key,const char * val)676 void Info_SetKey(char *info, size_t infosize, const char *key, const char *val)
677 {
678 	size_t keylen = strlen(key);
679 	size_t vallen = strlen(val);
680 
681 	Info_RemoveKey(info, key);
682 
683 	if (vallen)
684 	{
685 		char *o = info + strlen(info);
686 		char *e = info + infosize-1;
687 
688 		if (!*key || strchr(key, '\\') || strchr(val, '\\'))
689 			Con_Warning("Info_SetKey(%s): invalid key/value\n", key);
690 		else if (o + 2 + keylen + vallen >= e)
691 			Con_Warning("Info_SetKey(%s): length exceeds max\n", key);
692 		else
693 		{
694 			*o++ = '\\';
695 			memcpy(o, key, keylen);
696 			o += keylen;
697 			*o++ = '\\';
698 			memcpy(o, val, vallen);
699 			o += vallen;
700 
701 			*o = 0;
702 		}
703 	}
704 }
Info_GetKey(const char * info,const char * key,char * out,size_t outsize)705 const char *Info_GetKey(const char *info, const char *key, char *out, size_t outsize)
706 {
707 	const char *r = out;
708 	size_t keylen = strlen(key);
709 
710 	outsize--;
711 
712 	while(*info)
713 	{
714 		if (*info++ != '\\')
715 			break;	//error / end-of-string
716 
717 		if (!strncmp(info, key, keylen) && info[keylen] == '\\')
718 		{
719 			//skip the key name
720 			info += keylen+1;
721 			//this is the value for the key. copy it out
722 			while (*info && *info != '\\' && outsize-->0)
723 				*out++ = *info++;
724 			break;
725 		}
726 		else
727 		{
728 			//skip the key
729 			while (*info && *info != '\\')
730 				info++;
731 
732 			//validate that its a value now
733 			if (*info++ != '\\')
734 				break;	//error
735 			//skip the value
736 			while (*info && *info != '\\')
737 				info++;
738 		}
739 	}
740 	*out = 0;
741 	return r;
742 }
743 
Info_Enumerate(const char * info,void (* cb)(void * ctx,const char * key,const char * value),void * cbctx)744 void Info_Enumerate(const char *info, void(*cb)(void *ctx, const char *key, const char *value), void *cbctx)
745 {
746 	char key[1024];
747 	char val[1024];
748 	size_t kl, vl;
749 	while(*info)
750 	{
751 		kl=vl=0;
752 		if (*info++ != '\\')
753 			break;	//error / end-of-string
754 
755 		//skip the key
756 		while (*info && *info != '\\')
757 		{
758 			if (kl < sizeof(key)-1)
759 				key[kl++] = *info;
760 			info++;
761 		}
762 
763 		//validate that its a value now
764 		if (*info++ != '\\')
765 			break;	//error
766 		//skip the value
767 		while (*info && *info != '\\')
768 		{
769 			if (vl < sizeof(val)-1)
770 				val[vl++] = *info;
771 			info++;
772 		}
773 
774 		key[kl] = 0;
775 		val[vl] = 0;
776 		cb(cbctx, key, val);
777 	}
778 }
Info_Print_Callback(void * ctx,const char * key,const char * val)779 static void Info_Print_Callback(void *ctx, const char *key, const char *val)
780 {
781 	Con_Printf("%20s: %s\n", key, val);
782 }
Info_Print(const char * info)783 void Info_Print(const char *info)
784 {
785 	Info_Enumerate(info, Info_Print_Callback, NULL);
786 }
787 /*
788 ============================================================================
789 
790 					BYTE ORDER FUNCTIONS
791 
792 ============================================================================
793 */
794 
795 qboolean	host_bigendian;
796 
797 short	(*BigShort) (short l);
798 short	(*LittleShort) (short l);
799 int	(*BigLong) (int l);
800 int	(*LittleLong) (int l);
801 float	(*BigFloat) (float l);
802 float	(*LittleFloat) (float l);
803 
ShortSwap(short l)804 short ShortSwap (short l)
805 {
806 	byte	b1, b2;
807 
808 	b1 = l&255;
809 	b2 = (l>>8)&255;
810 
811 	return ((unsigned short)b1<<8) + b2;
812 }
813 
ShortNoSwap(short l)814 short ShortNoSwap (short l)
815 {
816 	return l;
817 }
818 
LongSwap(int l)819 int LongSwap (int l)
820 {
821 	byte	b1, b2, b3, b4;
822 
823 	b1 = l&255;
824 	b2 = (l>>8)&255;
825 	b3 = (l>>16)&255;
826 	b4 = (l>>24)&255;
827 
828 	return ((unsigned int)b1<<24) + ((unsigned int)b2<<16) + ((unsigned int)b3<<8) + b4;
829 }
830 
LongNoSwap(int l)831 int LongNoSwap (int l)
832 {
833 	return l;
834 }
835 
FloatSwap(float f)836 float FloatSwap (float f)
837 {
838 	union
839 	{
840 		float	f;
841 		byte	b[4];
842 	} dat1, dat2;
843 
844 
845 	dat1.f = f;
846 	dat2.b[0] = dat1.b[3];
847 	dat2.b[1] = dat1.b[2];
848 	dat2.b[2] = dat1.b[1];
849 	dat2.b[3] = dat1.b[0];
850 	return dat2.f;
851 }
852 
FloatNoSwap(float f)853 float FloatNoSwap (float f)
854 {
855 	return f;
856 }
857 
858 /*
859 ==============================================================================
860 
861 			MESSAGE IO FUNCTIONS
862 
863 Handles byte ordering and avoids alignment errors
864 ==============================================================================
865 */
866 
867 //
868 // writing functions
869 //
870 
MSG_WriteChar(sizebuf_t * sb,int c)871 void MSG_WriteChar (sizebuf_t *sb, int c)
872 {
873 	byte	*buf;
874 
875 #ifdef PARANOID
876 	if (c < -128 || c > 127)
877 		Sys_Error ("MSG_WriteChar: range error");
878 #endif
879 
880 	buf = (byte *) SZ_GetSpace (sb, 1);
881 	buf[0] = c;
882 }
883 
MSG_WriteByte(sizebuf_t * sb,int c)884 void MSG_WriteByte (sizebuf_t *sb, int c)
885 {
886 	byte	*buf;
887 
888 #ifdef PARANOID
889 	if (c < 0 || c > 255)
890 		Sys_Error ("MSG_WriteByte: range error");
891 #endif
892 
893 	buf = (byte *) SZ_GetSpace (sb, 1);
894 	buf[0] = c;
895 }
896 
MSG_WriteShort(sizebuf_t * sb,int c)897 void MSG_WriteShort (sizebuf_t *sb, int c)
898 {
899 	byte	*buf;
900 
901 #ifdef PARANOID
902 	if (c < ((short)0x8000) || c > (short)0x7fff)
903 		Sys_Error ("MSG_WriteShort: range error");
904 #endif
905 
906 	buf = (byte *) SZ_GetSpace (sb, 2);
907 	buf[0] = c&0xff;
908 	buf[1] = c>>8;
909 }
910 
MSG_WriteLong(sizebuf_t * sb,int c)911 void MSG_WriteLong (sizebuf_t *sb, int c)
912 {
913 	byte	*buf;
914 
915 	buf = (byte *) SZ_GetSpace (sb, 4);
916 	buf[0] = c&0xff;
917 	buf[1] = (c>>8)&0xff;
918 	buf[2] = (c>>16)&0xff;
919 	buf[3] = c>>24;
920 }
921 
MSG_WriteFloat(sizebuf_t * sb,float f)922 void MSG_WriteFloat (sizebuf_t *sb, float f)
923 {
924 	union
925 	{
926 		float	f;
927 		int	l;
928 	} dat;
929 
930 	dat.f = f;
931 	dat.l = LittleLong (dat.l);
932 
933 	SZ_Write (sb, &dat.l, 4);
934 }
935 
MSG_WriteString(sizebuf_t * sb,const char * s)936 void MSG_WriteString (sizebuf_t *sb, const char *s)
937 {
938 	if (!s)
939 		SZ_Write (sb, "", 1);
940 	else
941 		SZ_Write (sb, s, Q_strlen(s)+1);
942 }
MSG_WriteStringUnterminated(sizebuf_t * sb,const char * s)943 void MSG_WriteStringUnterminated (sizebuf_t *sb, const char *s)
944 {
945 	SZ_Write (sb, s, Q_strlen(s));
946 }
947 
948 //johnfitz -- original behavior, 13.3 fixed point coords, max range +-4096
MSG_WriteCoord16(sizebuf_t * sb,float f)949 void MSG_WriteCoord16 (sizebuf_t *sb, float f)
950 {
951 	MSG_WriteShort (sb, Q_rint(f*8));
952 }
953 
954 //johnfitz -- 16.8 fixed point coords, max range +-32768
MSG_WriteCoord24(sizebuf_t * sb,float f)955 void MSG_WriteCoord24 (sizebuf_t *sb, float f)
956 {
957 	MSG_WriteShort (sb, f);
958 	MSG_WriteByte (sb, (int)(f*255)%255);
959 }
960 
961 //johnfitz -- 32-bit float coords
MSG_WriteCoord32f(sizebuf_t * sb,float f)962 void MSG_WriteCoord32f (sizebuf_t *sb, float f)
963 {
964 	MSG_WriteFloat (sb, f);
965 }
966 
MSG_WriteCoord(sizebuf_t * sb,float f,unsigned int flags)967 void MSG_WriteCoord (sizebuf_t *sb, float f, unsigned int flags)
968 {
969 	if (flags & PRFL_FLOATCOORD)
970 		MSG_WriteFloat (sb, f);
971 	else if (flags & PRFL_INT32COORD)
972 		MSG_WriteLong (sb, Q_rint (f * 16));
973 	else if (flags & PRFL_24BITCOORD)
974 		MSG_WriteCoord24 (sb, f);
975 	else MSG_WriteCoord16 (sb, f);
976 }
977 
MSG_WriteAngle(sizebuf_t * sb,float f,unsigned int flags)978 void MSG_WriteAngle (sizebuf_t *sb, float f, unsigned int flags)
979 {
980 	if (flags & PRFL_FLOATANGLE)
981 		MSG_WriteFloat (sb, f);
982 	else if (flags & PRFL_SHORTANGLE)
983 		MSG_WriteShort (sb, Q_rint(f * 65536.0 / 360.0) & 65535);
984 	else MSG_WriteByte (sb, Q_rint(f * 256.0 / 360.0) & 255); //johnfitz -- use Q_rint instead of (int)	}
985 }
986 
987 //johnfitz -- for PROTOCOL_FITZQUAKE
MSG_WriteAngle16(sizebuf_t * sb,float f,unsigned int flags)988 void MSG_WriteAngle16 (sizebuf_t *sb, float f, unsigned int flags)
989 {
990 	if (flags & PRFL_FLOATANGLE)
991 		MSG_WriteFloat (sb, f);
992 	else MSG_WriteShort (sb, Q_rint(f * 65536.0 / 360.0) & 65535);
993 }
994 //johnfitz
995 
996 //spike -- for PEXT2_REPLACEMENTDELTAS
MSG_WriteEntity(sizebuf_t * sb,unsigned int entnum,unsigned int pext2)997 void MSG_WriteEntity (sizebuf_t *sb, unsigned int entnum, unsigned int pext2)
998 {
999 	//high short, low byte
1000 	if (entnum > 0x7fff && (pext2 & PEXT2_REPLACEMENTDELTAS))
1001 	{
1002 		MSG_WriteShort(sb, 0x8000|(entnum>>8));
1003 		MSG_WriteByte(sb, entnum&0xff);
1004 	}
1005 	else
1006 		MSG_WriteShort(sb, entnum);
1007 }
1008 
1009 //
1010 // reading functions
1011 //
1012 int		msg_readcount;
1013 qboolean	msg_badread;
1014 
MSG_BeginReading(void)1015 void MSG_BeginReading (void)
1016 {
1017 	msg_readcount = 0;
1018 	msg_badread = false;
1019 }
1020 
1021 // returns -1 and sets msg_badread if no more characters are available
MSG_ReadChar(void)1022 int MSG_ReadChar (void)
1023 {
1024 	int	c;
1025 
1026 	if (msg_readcount+1 > net_message.cursize)
1027 	{
1028 		msg_badread = true;
1029 		return -1;
1030 	}
1031 
1032 	c = (signed char)net_message.data[msg_readcount];
1033 	msg_readcount++;
1034 
1035 	return c;
1036 }
1037 
MSG_ReadByte(void)1038 int MSG_ReadByte (void)
1039 {
1040 	int	c;
1041 
1042 	if (msg_readcount+1 > net_message.cursize)
1043 	{
1044 		msg_badread = true;
1045 		return -1;
1046 	}
1047 
1048 	c = (unsigned char)net_message.data[msg_readcount];
1049 	msg_readcount++;
1050 
1051 	return c;
1052 }
1053 
MSG_ReadShort(void)1054 int MSG_ReadShort (void)
1055 {
1056 	int	c;
1057 
1058 	if (msg_readcount+2 > net_message.cursize)
1059 	{
1060 		msg_badread = true;
1061 		return -1;
1062 	}
1063 
1064 	c = (short)(net_message.data[msg_readcount]
1065 			+ (net_message.data[msg_readcount+1]<<8));
1066 
1067 	msg_readcount += 2;
1068 
1069 	return c;
1070 }
1071 
MSG_ReadLong(void)1072 int MSG_ReadLong (void)
1073 {
1074 	uint32_t c;
1075 
1076 	if (msg_readcount+4 > net_message.cursize)
1077 	{
1078 		msg_badread = true;
1079 		return -1;
1080 	}
1081 
1082 	c = (uint32_t)net_message.data[msg_readcount]
1083 			+ ((uint32_t)(net_message.data[msg_readcount+1])<<8)
1084 			+ ((uint32_t)(net_message.data[msg_readcount+2])<<16)
1085 			+ ((uint32_t)(net_message.data[msg_readcount+3])<<24);
1086 
1087 	msg_readcount += 4;
1088 
1089 	return c;
1090 }
1091 
MSG_ReadFloat(void)1092 float MSG_ReadFloat (void)
1093 {
1094 	union
1095 	{
1096 		byte	b[4];
1097 		float	f;
1098 		int	l;
1099 	} dat;
1100 
1101 	dat.b[0] = net_message.data[msg_readcount];
1102 	dat.b[1] = net_message.data[msg_readcount+1];
1103 	dat.b[2] = net_message.data[msg_readcount+2];
1104 	dat.b[3] = net_message.data[msg_readcount+3];
1105 	msg_readcount += 4;
1106 
1107 	dat.l = LittleLong (dat.l);
1108 
1109 	return dat.f;
1110 }
1111 
MSG_ReadString(void)1112 const char *MSG_ReadString (void)
1113 {
1114 	static char	string[2048];
1115 	int		c;
1116 	size_t		l;
1117 
1118 	l = 0;
1119 	do
1120 	{
1121 		c = MSG_ReadByte ();
1122 		if (c == -1 || c == 0)
1123 			break;
1124 		string[l] = c;
1125 		l++;
1126 	} while (l < sizeof(string) - 1);
1127 
1128 	string[l] = 0;
1129 
1130 	return string;
1131 }
1132 
1133 //johnfitz -- original behavior, 13.3 fixed point coords, max range +-4096
MSG_ReadCoord16(void)1134 float MSG_ReadCoord16 (void)
1135 {
1136 	return MSG_ReadShort() * (1.0/8);
1137 }
1138 
1139 //johnfitz -- 16.8 fixed point coords, max range +-32768
MSG_ReadCoord24(void)1140 float MSG_ReadCoord24 (void)
1141 {
1142 	return MSG_ReadShort() + MSG_ReadByte() * (1.0/255);
1143 }
1144 
1145 //johnfitz -- 32-bit float coords
MSG_ReadCoord32f(void)1146 float MSG_ReadCoord32f (void)
1147 {
1148 	return MSG_ReadFloat();
1149 }
1150 
MSG_ReadCoord(unsigned int flags)1151 float MSG_ReadCoord (unsigned int flags)
1152 {
1153 	if (flags & PRFL_FLOATCOORD)
1154 		return MSG_ReadFloat ();
1155 	else if (flags & PRFL_INT32COORD)
1156 		return MSG_ReadLong () * (1.0 / 16.0);
1157 	else if (flags & PRFL_24BITCOORD)
1158 		return MSG_ReadCoord24 ();
1159 	else return MSG_ReadCoord16 ();
1160 }
1161 
MSG_ReadAngle(unsigned int flags)1162 float MSG_ReadAngle (unsigned int flags)
1163 {
1164 	if (flags & PRFL_FLOATANGLE)
1165 		return MSG_ReadFloat ();
1166 	else if (flags & PRFL_SHORTANGLE)
1167 		return MSG_ReadShort () * (360.0 / 65536);
1168 	else return MSG_ReadChar () * (360.0 / 256);
1169 }
1170 
1171 //johnfitz -- for PROTOCOL_FITZQUAKE
MSG_ReadAngle16(unsigned int flags)1172 float MSG_ReadAngle16 (unsigned int flags)
1173 {
1174 	if (flags & PRFL_FLOATANGLE)
1175 		return MSG_ReadFloat ();	// make sure
1176 	else return MSG_ReadShort () * (360.0 / 65536);
1177 }
1178 //johnfitz
1179 
MSG_ReadEntity(unsigned int pext2)1180 unsigned int MSG_ReadEntity(unsigned int pext2)
1181 {
1182 	unsigned int e = (unsigned short)MSG_ReadShort();
1183 	if (pext2 & PEXT2_REPLACEMENTDELTAS)
1184 	{
1185 		if (e & 0x8000)
1186 		{
1187 			e = (e & 0x7fff) << 8;
1188 			e |= MSG_ReadByte();
1189 		}
1190 	}
1191 	return e;
1192 }
1193 
1194 //===========================================================================
1195 
SZ_Alloc(sizebuf_t * buf,int startsize)1196 void SZ_Alloc (sizebuf_t *buf, int startsize)
1197 {
1198 	if (startsize < 256)
1199 		startsize = 256;
1200 	buf->data = (byte *) Hunk_AllocName (startsize, "sizebuf");
1201 	buf->maxsize = startsize;
1202 	buf->cursize = 0;
1203 }
1204 
1205 
SZ_Free(sizebuf_t * buf)1206 void SZ_Free (sizebuf_t *buf)
1207 {
1208 //	Z_Free (buf->data);
1209 //	buf->data = NULL;
1210 //	buf->maxsize = 0;
1211 	buf->cursize = 0;
1212 }
1213 
SZ_Clear(sizebuf_t * buf)1214 void SZ_Clear (sizebuf_t *buf)
1215 {
1216 	buf->cursize = 0;
1217 	buf->overflowed = false;
1218 }
1219 
SZ_GetSpace(sizebuf_t * buf,int length)1220 void *SZ_GetSpace (sizebuf_t *buf, int length)
1221 {
1222 	void	*data;
1223 
1224 	if (buf->cursize + length > buf->maxsize)
1225 	{
1226 		if (!buf->allowoverflow)
1227 			Host_Error ("SZ_GetSpace: overflow without allowoverflow set"); // ericw -- made Host_Error to be less annoying
1228 
1229 		if (length > buf->maxsize)
1230 			Sys_Error ("SZ_GetSpace: %i is > full buffer size", length);
1231 
1232 		Con_Printf ("SZ_GetSpace: overflow\n");
1233 		SZ_Clear (buf);
1234 		buf->overflowed = true;
1235 	}
1236 
1237 	data = buf->data + buf->cursize;
1238 	buf->cursize += length;
1239 
1240 	return data;
1241 }
1242 
SZ_Write(sizebuf_t * buf,const void * data,int length)1243 void SZ_Write (sizebuf_t *buf, const void *data, int length)
1244 {
1245 	Q_memcpy (SZ_GetSpace(buf,length),data,length);
1246 }
1247 
SZ_Print(sizebuf_t * buf,const char * data)1248 void SZ_Print (sizebuf_t *buf, const char *data)
1249 {
1250 	int		len = Q_strlen(data) + 1;
1251 
1252 	if (buf->data[buf->cursize-1])
1253 	{	/* no trailing 0 */
1254 		Q_memcpy ((byte *)SZ_GetSpace(buf, len  )  , data, len);
1255 	}
1256 	else
1257 	{	/* write over trailing 0 */
1258 		Q_memcpy ((byte *)SZ_GetSpace(buf, len-1)-1, data, len);
1259 	}
1260 }
1261 
1262 
1263 //============================================================================
1264 
1265 /*
1266 ============
1267 COM_SkipPath
1268 ============
1269 */
COM_SkipPath(const char * pathname)1270 const char *COM_SkipPath (const char *pathname)
1271 {
1272 	const char	*last;
1273 
1274 	last = pathname;
1275 	while (*pathname)
1276 	{
1277 		if (*pathname == '/')
1278 			last = pathname + 1;
1279 		pathname++;
1280 	}
1281 	return last;
1282 }
1283 
1284 /*
1285 ============
1286 COM_StripExtension
1287 ============
1288 */
COM_StripExtension(const char * in,char * out,size_t outsize)1289 void COM_StripExtension (const char *in, char *out, size_t outsize)
1290 {
1291 	int	length;
1292 
1293 	if (!*in)
1294 	{
1295 		*out = '\0';
1296 		return;
1297 	}
1298 	if (in != out)	/* copy when not in-place editing */
1299 		q_strlcpy (out, in, outsize);
1300 	length = (int)strlen(out) - 1;
1301 	while (length > 0 && out[length] != '.')
1302 	{
1303 		--length;
1304 		if (out[length] == '/' || out[length] == '\\')
1305 			return;	/* no extension */
1306 	}
1307 	if (length > 0)
1308 		out[length] = '\0';
1309 }
1310 
1311 /*
1312 ============
1313 COM_FileGetExtension - doesn't return NULL
1314 ============
1315 */
COM_FileGetExtension(const char * in)1316 const char *COM_FileGetExtension (const char *in)
1317 {
1318 	const char	*src;
1319 	size_t		len;
1320 
1321 	len = strlen(in);
1322 	if (len < 2)	/* nothing meaningful */
1323 		return "";
1324 
1325 	src = in + len - 1;
1326 	while (src != in && src[-1] != '.')
1327 		src--;
1328 	if (src == in || strchr(src, '/') != NULL || strchr(src, '\\') != NULL)
1329 		return "";	/* no extension, or parent directory has a dot */
1330 
1331 	return src;
1332 }
1333 
1334 /*
1335 ============
1336 COM_ExtractExtension
1337 ============
1338 */
COM_ExtractExtension(const char * in,char * out,size_t outsize)1339 void COM_ExtractExtension (const char *in, char *out, size_t outsize)
1340 {
1341 	const char *ext = COM_FileGetExtension (in);
1342 	if (! *ext)
1343 		*out = '\0';
1344 	else
1345 		q_strlcpy (out, ext, outsize);
1346 }
1347 
1348 /*
1349 ============
1350 COM_FileBase
1351 take 'somedir/otherdir/filename.ext',
1352 write only 'filename' to the output
1353 ============
1354 */
COM_FileBase(const char * in,char * out,size_t outsize)1355 void COM_FileBase (const char *in, char *out, size_t outsize)
1356 {
1357 	const char	*dot, *slash, *s;
1358 
1359 	s = in;
1360 	slash = in;
1361 	dot = NULL;
1362 	while (*s)
1363 	{
1364 		if (*s == '/')
1365 			slash = s + 1;
1366 		if (*s == '.')
1367 			dot = s;
1368 		s++;
1369 	}
1370 	if (dot == NULL)
1371 		dot = s;
1372 
1373 	if (dot - slash < 2)
1374 		q_strlcpy (out, "?model?", outsize);
1375 	else
1376 	{
1377 		size_t	len = dot - slash;
1378 		if (len >= outsize)
1379 			len = outsize - 1;
1380 		memcpy (out, slash, len);
1381 		out[len] = '\0';
1382 	}
1383 }
1384 
1385 /*
1386 ==================
1387 COM_DefaultExtension
1388 if path doesn't have a .EXT, append extension
1389 (extension should include the leading ".")
1390 ==================
1391 */
1392 #if 0 /* can be dangerous */
1393 void COM_DefaultExtension (char *path, const char *extension, size_t len)
1394 {
1395 	char	*src;
1396 
1397 	if (!*path) return;
1398 	src = path + strlen(path) - 1;
1399 
1400 	while (*src != '/' && *src != '\\' && src != path)
1401 	{
1402 		if (*src == '.')
1403 			return; // it has an extension
1404 		src--;
1405 	}
1406 
1407 	q_strlcat(path, extension, len);
1408 }
1409 #endif
1410 
1411 /*
1412 ==================
1413 COM_AddExtension
1414 if path extension doesn't match .EXT, append it
1415 (extension should include the leading ".")
1416 ==================
1417 */
COM_AddExtension(char * path,const char * extension,size_t len)1418 void COM_AddExtension (char *path, const char *extension, size_t len)
1419 {
1420 	if (strcmp(COM_FileGetExtension(path), extension + 1) != 0)
1421 		q_strlcat(path, extension, len);
1422 }
1423 
1424 
1425 /*
1426 ==============
1427 COM_Parse
1428 
1429 Parse a token out of a string
1430 ==============
1431 */
COM_Parse(const char * data)1432 const char *COM_Parse (const char *data)
1433 {
1434 	int		c;
1435 	int		len;
1436 
1437 	len = 0;
1438 	com_token[0] = 0;
1439 
1440 	if (!data)
1441 		return NULL;
1442 
1443 // skip whitespace
1444 skipwhite:
1445 	while ((c = *data) <= ' ')
1446 	{
1447 		if (c == 0)
1448 			return NULL;	// end of file
1449 		data++;
1450 	}
1451 
1452 // skip // comments
1453 	if (c == '/' && data[1] == '/')
1454 	{
1455 		while (*data && *data != '\n')
1456 			data++;
1457 		goto skipwhite;
1458 	}
1459 
1460 // skip /*..*/ comments
1461 	if (c == '/' && data[1] == '*')
1462 	{
1463 		data += 2;
1464 		while (*data && !(*data == '*' && data[1] == '/'))
1465 			data++;
1466 		if (*data)
1467 			data += 2;
1468 		goto skipwhite;
1469 	}
1470 
1471 // handle quoted strings specially
1472 	if (c == '\"')
1473 	{
1474 		data++;
1475 		while (1)
1476 		{
1477 			if ((c = *data) != 0)
1478 				++data;
1479 			if (c == '\"' || !c)
1480 			{
1481 				com_token[len] = 0;
1482 				return data;
1483 			}
1484 			com_token[len] = c;
1485 			len++;
1486 		}
1487 	}
1488 
1489 // parse single characters
1490 	if (c == '{' || c == '}'|| c == '('|| c == ')' || c == '\'' || c == ':')
1491 	{
1492 		com_token[len] = c;
1493 		len++;
1494 		com_token[len] = 0;
1495 		return data+1;
1496 	}
1497 
1498 // parse a regular word
1499 	do
1500 	{
1501 		com_token[len] = c;
1502 		data++;
1503 		len++;
1504 		c = *data;
1505 		/* commented out the check for ':' so that ip:port works */
1506 		if (c == '{' || c == '}'|| c == '('|| c == ')' || c == '\''/* || c == ':' */)
1507 			break;
1508 	} while (c > 32);
1509 
1510 	com_token[len] = 0;
1511 	return data;
1512 }
1513 
1514 
1515 /*
1516 ================
1517 COM_CheckParm
1518 
1519 Returns the position (1 to argc-1) in the program's argument list
1520 where the given parameter apears, or 0 if not present
1521 ================
1522 */
COM_CheckParmNext(int last,const char * parm)1523 int COM_CheckParmNext (int last, const char *parm)
1524 {
1525 	int		i;
1526 
1527 	for (i = last+1; i < com_argc; i++)
1528 	{
1529 		if (!com_argv[i])
1530 			continue;		// NEXTSTEP sometimes clears appkit vars.
1531 		if (!Q_strcmp (parm,com_argv[i]))
1532 			return i;
1533 	}
1534 
1535 	return 0;
1536 }
COM_CheckParm(const char * parm)1537 int COM_CheckParm (const char *parm)
1538 {
1539 	return COM_CheckParmNext(0, parm);
1540 }
1541 
1542 /*
1543 ================
1544 COM_CheckRegistered
1545 
1546 Looks for the pop.txt file and verifies it.
1547 Sets the "registered" cvar.
1548 Immediately exits out if an alternate game was attempted to be started without
1549 being registered.
1550 ================
1551 */
COM_CheckRegistered(void)1552 static void COM_CheckRegistered (void)
1553 {
1554 	int		h;
1555 	unsigned short	check[128];
1556 	int		i;
1557 
1558 	COM_OpenFile("gfx/pop.lmp", &h, NULL);
1559 
1560 	if (h == -1)
1561 	{
1562 		Cvar_SetROM ("registered", "0");
1563 		Con_Printf ("Playing shareware version.\n");
1564 		if (com_modified)
1565 			Sys_Error ("You must have the registered version to use modified games.\n\n"
1566 				   "Basedir is: %s\n\n"
1567 				   "Check that this has an " GAMENAME " subdirectory containing pak0.pak and pak1.pak, "
1568 				   "or use the -basedir command-line option to specify another directory.",
1569 				   com_basedir);
1570 		return;
1571 	}
1572 
1573 	Sys_FileRead (h, check, sizeof(check));
1574 	COM_CloseFile (h);
1575 
1576 	for (i = 0; i < 128; i++)
1577 	{
1578 		if (pop[i] != (unsigned short)BigShort (check[i]))
1579 			Sys_Error ("Corrupted data file.");
1580 	}
1581 
1582 	for (i = 0; com_cmdline[i]; i++)
1583 	{
1584 		if (com_cmdline[i]!= ' ')
1585 			break;
1586 	}
1587 
1588 	Cvar_SetROM ("cmdline", &com_cmdline[i]);
1589 	Cvar_SetROM ("registered", "1");
1590 	Con_Printf ("Playing registered version.\n");
1591 }
1592 
1593 
1594 /*
1595 ================
1596 COM_InitArgv
1597 ================
1598 */
COM_InitArgv(int argc,char ** argv)1599 void COM_InitArgv (int argc, char **argv)
1600 {
1601 	int		i, j, n;
1602 
1603 // reconstitute the command line for the cmdline externally visible cvar
1604 	n = 0;
1605 
1606 	for (j = 0; (j<MAX_NUM_ARGVS) && (j< argc); j++)
1607 	{
1608 		i = 0;
1609 
1610 		while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1611 		{
1612 			com_cmdline[n++] = argv[j][i++];
1613 		}
1614 
1615 		if (n < (CMDLINE_LENGTH - 1))
1616 			com_cmdline[n++] = ' ';
1617 		else
1618 			break;
1619 	}
1620 
1621 	if (n > 0 && com_cmdline[n-1] == ' ')
1622 		com_cmdline[n-1] = 0; //johnfitz -- kill the trailing space
1623 
1624 	Con_Printf("Command line: %s\n", com_cmdline);
1625 
1626 	for (com_argc = 0; (com_argc < MAX_NUM_ARGVS) && (com_argc < argc); com_argc++)
1627 	{
1628 		largv[com_argc] = argv[com_argc];
1629 		if (!Q_strcmp ("-safe", argv[com_argc]))
1630 			safemode = 1;
1631 	}
1632 
1633 	largv[com_argc] = argvdummy;
1634 	com_argv = largv;
1635 
1636 	if (COM_CheckParm ("-rogue"))
1637 	{
1638 		rogue = true;
1639 		standard_quake = false;
1640 	}
1641 
1642 	if (COM_CheckParm ("-hipnotic") || COM_CheckParm ("-quoth")) //johnfitz -- "-quoth" support
1643 	{
1644 		hipnotic = true;
1645 		standard_quake = false;
1646 	}
1647 }
1648 
1649 
1650 entity_state_t nullentitystate;
COM_SetupNullState(void)1651 static void COM_SetupNullState(void)
1652 {
1653 	//the null state has some specific default values
1654 //	nullentitystate.drawflags = /*SCALE_ORIGIN_ORIGIN*/96;
1655 	nullentitystate.colormod[0] = 32;
1656 	nullentitystate.colormod[1] = 32;
1657 	nullentitystate.colormod[2] = 32;
1658 //	nullentitystate.glowmod[0] = 32;
1659 //	nullentitystate.glowmod[1] = 32;
1660 //	nullentitystate.glowmod[2] = 32;
1661 	nullentitystate.alpha = 0;	//fte has 255 by default, with 0 for invisible. fitz uses 1 for invisible, 0 default, and 255=full alpha
1662 	nullentitystate.scale = 16;
1663 //	nullentitystate.solidsize = 0;//ES_SOLID_BSP;
1664 }
1665 
1666 /*
1667 ================
1668 COM_Init
1669 ================
1670 */
COM_Init(void)1671 void COM_Init (void)
1672 {
1673 	int	i = 0x12345678;
1674 		/*    U N I X */
1675 
1676 	/*
1677 	BE_ORDER:  12 34 56 78
1678 		   U  N  I  X
1679 
1680 	LE_ORDER:  78 56 34 12
1681 		   X  I  N  U
1682 
1683 	PDP_ORDER: 34 12 78 56
1684 		   N  U  X  I
1685 	*/
1686 	if ( *(char *)&i == 0x12 )
1687 		host_bigendian = true;
1688 	else if ( *(char *)&i == 0x78 )
1689 		host_bigendian = false;
1690 	else /* if ( *(char *)&i == 0x34 ) */
1691 		Sys_Error ("Unsupported endianism.");
1692 
1693 	if (host_bigendian)
1694 	{
1695 		BigShort = ShortNoSwap;
1696 		LittleShort = ShortSwap;
1697 		BigLong = LongNoSwap;
1698 		LittleLong = LongSwap;
1699 		BigFloat = FloatNoSwap;
1700 		LittleFloat = FloatSwap;
1701 	}
1702 	else /* assumed LITTLE_ENDIAN. */
1703 	{
1704 		BigShort = ShortSwap;
1705 		LittleShort = ShortNoSwap;
1706 		BigLong = LongSwap;
1707 		LittleLong = LongNoSwap;
1708 		BigFloat = FloatSwap;
1709 		LittleFloat = FloatNoSwap;
1710 	}
1711 
1712 	if (COM_CheckParm("-fitz"))
1713 		fitzmode = true;
1714 
1715 	COM_SetupNullState();
1716 }
1717 
1718 
1719 /*
1720 ============
1721 va
1722 
1723 does a varargs printf into a temp buffer. cycles between
1724 4 different static buffers. the number of buffers cycled
1725 is defined in VA_NUM_BUFFS.
1726 FIXME: make this buffer size safe someday
1727 ============
1728 */
1729 #define	VA_NUM_BUFFS	4
1730 #define	VA_BUFFERLEN	1024
1731 
get_va_buffer(void)1732 static char *get_va_buffer(void)
1733 {
1734 	static char va_buffers[VA_NUM_BUFFS][VA_BUFFERLEN];
1735 	static int buffer_idx = 0;
1736 	buffer_idx = (buffer_idx + 1) & (VA_NUM_BUFFS - 1);
1737 	return va_buffers[buffer_idx];
1738 }
1739 
va(const char * format,...)1740 char *va (const char *format, ...)
1741 {
1742 	va_list		argptr;
1743 	char		*va_buf;
1744 
1745 	va_buf = get_va_buffer ();
1746 	va_start (argptr, format);
1747 	q_vsnprintf (va_buf, VA_BUFFERLEN, format, argptr);
1748 	va_end (argptr);
1749 
1750 	return va_buf;
1751 }
1752 
1753 /*
1754 =============================================================================
1755 
1756 QUAKE FILESYSTEM
1757 
1758 =============================================================================
1759 */
1760 
1761 int	com_filesize;
1762 
1763 
1764 //
1765 // on-disk pakfile
1766 //
1767 typedef struct
1768 {
1769 	char	name[56];
1770 	int		filepos, filelen;
1771 } dpackfile_t;
1772 
1773 typedef struct
1774 {
1775 	char	id[4];
1776 	int		dirofs;
1777 	int		dirlen;
1778 } dpackheader_t;
1779 
1780 #define MAX_FILES_IN_PACK	2048
1781 
1782 char	com_gamenames[1024];	//eg: "hipnotic;quoth;warp" ... no id1
1783 char	com_gamedir[MAX_OSPATH];
1784 char	com_basedir[MAX_OSPATH];
1785 int	file_from_pak;		// ZOID: global indicating that file came from a pak
1786 
1787 searchpath_t	*com_searchpaths;
1788 searchpath_t	*com_base_searchpaths;
1789 
1790 /*
1791 ============
1792 COM_Path_f
1793 ============
1794 */
COM_Path_f(void)1795 static void COM_Path_f (void)
1796 {
1797 	searchpath_t	*s;
1798 
1799 	Con_Printf ("Current search path:\n");
1800 	for (s = com_searchpaths; s; s = s->next)
1801 	{
1802 		if (s->pack)
1803 		{
1804 			Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1805 		}
1806 		else
1807 			Con_Printf ("%s\n", s->filename);
1808 	}
1809 }
1810 
1811 /*
1812 ============
1813 COM_WriteFile
1814 
1815 The filename will be prefixed by the current game directory
1816 ============
1817 */
COM_WriteFile(const char * filename,const void * data,int len)1818 void COM_WriteFile (const char *filename, const void *data, int len)
1819 {
1820 	int		handle;
1821 	char	name[MAX_OSPATH];
1822 
1823 	Sys_mkdir (com_gamedir); //johnfitz -- if we've switched to a nonexistant gamedir, create it now so we don't crash
1824 
1825 	q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, filename);
1826 
1827 	handle = Sys_FileOpenWrite (name);
1828 	if (handle == -1)
1829 	{
1830 		Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1831 		return;
1832 	}
1833 
1834 	Sys_Printf ("COM_WriteFile: %s\n", name);
1835 	Sys_FileWrite (handle, data, len);
1836 	Sys_FileClose (handle);
1837 }
1838 
1839 /*
1840 ============
1841 COM_CreatePath
1842 ============
1843 */
COM_CreatePath(char * path)1844 void COM_CreatePath (char *path)
1845 {
1846 	char	*ofs;
1847 
1848 	for (ofs = path + 1; *ofs; ofs++)
1849 	{
1850 		if (*ofs == '/')
1851 		{	// create the directory
1852 			*ofs = 0;
1853 			Sys_mkdir (path);
1854 			*ofs = '/';
1855 		}
1856 	}
1857 }
1858 
1859 /*
1860 ================
1861 COM_filelength
1862 ================
1863 */
COM_filelength(FILE * f)1864 long COM_filelength (FILE *f)
1865 {
1866 	long		pos, end;
1867 
1868 	pos = ftell (f);
1869 	fseek (f, 0, SEEK_END);
1870 	end = ftell (f);
1871 	fseek (f, pos, SEEK_SET);
1872 
1873 	return end;
1874 }
1875 
1876 /*
1877 ===========
1878 COM_FindFile
1879 
1880 Finds the file in the search path.
1881 Sets com_filesize and one of handle or file
1882 If neither of file or handle is set, this
1883 can be used for detecting a file's presence.
1884 ===========
1885 */
COM_FindFile(const char * filename,int * handle,FILE ** file,unsigned int * path_id)1886 static int COM_FindFile (const char *filename, int *handle, FILE **file,
1887 							unsigned int *path_id)
1888 {
1889 	searchpath_t	*search;
1890 	char		netpath[MAX_OSPATH];
1891 	pack_t		*pak;
1892 	int		i, findtime;
1893 
1894 	if (file && handle)
1895 		Sys_Error ("COM_FindFile: both handle and file set");
1896 
1897 	file_from_pak = 0;
1898 
1899 //
1900 // search through the path, one element at a time
1901 //
1902 	for (search = com_searchpaths; search; search = search->next)
1903 	{
1904 		if (search->pack)	/* look through all the pak file elements */
1905 		{
1906 			pak = search->pack;
1907 			for (i = 0; i < pak->numfiles; i++)
1908 			{
1909 				if (strcmp(pak->files[i].name, filename) != 0)
1910 					continue;
1911 				// found it!
1912 				com_filesize = pak->files[i].filelen;
1913 				file_from_pak = 1;
1914 				if (path_id)
1915 					*path_id = search->path_id;
1916 				if (handle)
1917 				{
1918 					*handle = pak->handle;
1919 					Sys_FileSeek (pak->handle, pak->files[i].filepos);
1920 					return com_filesize;
1921 				}
1922 				else if (file)
1923 				{ /* open a new file on the pakfile */
1924 					*file = fopen (pak->filename, "rb");
1925 					if (*file)
1926 						fseek (*file, pak->files[i].filepos, SEEK_SET);
1927 					return com_filesize;
1928 				}
1929 				else /* for COM_FileExists() */
1930 				{
1931 					return com_filesize;
1932 				}
1933 			}
1934 		}
1935 		else	/* check a file in the directory tree */
1936 		{
1937 			if (!registered.value)
1938 			{ /* if not a registered version, don't ever go beyond base */
1939 				if ( strchr (filename, '/') || strchr (filename,'\\'))
1940 					continue;
1941 			}
1942 
1943 			q_snprintf (netpath, sizeof(netpath), "%s/%s",search->filename, filename);
1944 			findtime = Sys_FileTime (netpath);
1945 			if (findtime == -1)
1946 				continue;
1947 
1948 			if (path_id)
1949 				*path_id = search->path_id;
1950 			if (handle)
1951 			{
1952 				com_filesize = Sys_FileOpenRead (netpath, &i);
1953 				*handle = i;
1954 				return com_filesize;
1955 			}
1956 			else if (file)
1957 			{
1958 				*file = fopen (netpath, "rb");
1959 				com_filesize = (*file == NULL) ? -1 : COM_filelength (*file);
1960 				return com_filesize;
1961 			}
1962 			else
1963 			{
1964 				return 0; /* dummy valid value for COM_FileExists() */
1965 			}
1966 		}
1967 	}
1968 
1969 	if (strcmp(COM_FileGetExtension(filename), "pcx") != 0
1970 		&& strcmp(COM_FileGetExtension(filename), "tga") != 0
1971 		&& strcmp(COM_FileGetExtension(filename), "lit") != 0
1972 		&& strcmp(COM_FileGetExtension(filename), "vis") != 0
1973 		&& strcmp(COM_FileGetExtension(filename), "ent") != 0)
1974 		Con_DPrintf ("FindFile: can't find %s\n", filename);
1975 	else	Con_DPrintf2("FindFile: can't find %s\n", filename);
1976 
1977 	if (handle)
1978 		*handle = -1;
1979 	if (file)
1980 		*file = NULL;
1981 	com_filesize = -1;
1982 	return com_filesize;
1983 }
1984 
1985 
1986 /*
1987 ===========
1988 COM_FileExists
1989 
1990 Returns whether the file is found in the quake filesystem.
1991 ===========
1992 */
COM_FileExists(const char * filename,unsigned int * path_id)1993 qboolean COM_FileExists (const char *filename, unsigned int *path_id)
1994 {
1995 	int ret = COM_FindFile (filename, NULL, NULL, path_id);
1996 	return (ret == -1) ? false : true;
1997 }
1998 
1999 /*
2000 ===========
2001 COM_OpenFile
2002 
2003 filename never has a leading slash, but may contain directory walks
2004 returns a handle and a length
2005 it may actually be inside a pak file
2006 ===========
2007 */
COM_OpenFile(const char * filename,int * handle,unsigned int * path_id)2008 int COM_OpenFile (const char *filename, int *handle, unsigned int *path_id)
2009 {
2010 	return COM_FindFile (filename, handle, NULL, path_id);
2011 }
2012 
2013 /*
2014 ===========
2015 COM_FOpenFile
2016 
2017 If the requested file is inside a packfile, a new FILE * will be opened
2018 into the file.
2019 ===========
2020 */
COM_FOpenFile(const char * filename,FILE ** file,unsigned int * path_id)2021 int COM_FOpenFile (const char *filename, FILE **file, unsigned int *path_id)
2022 {
2023 	return COM_FindFile (filename, NULL, file, path_id);
2024 }
2025 
2026 /*
2027 ============
2028 COM_CloseFile
2029 
2030 If it is a pak file handle, don't really close it
2031 ============
2032 */
COM_CloseFile(int h)2033 void COM_CloseFile (int h)
2034 {
2035 	searchpath_t	*s;
2036 
2037 	for (s = com_searchpaths; s; s = s->next)
2038 		if (s->pack && s->pack->handle == h)
2039 			return;
2040 
2041 	Sys_FileClose (h);
2042 }
2043 
2044 
2045 /*
2046 ============
2047 COM_LoadFile
2048 
2049 Filename are reletive to the quake directory.
2050 Allways appends a 0 byte.
2051 ============
2052 */
2053 #define	LOADFILE_ZONE		0
2054 #define	LOADFILE_HUNK		1
2055 #define	LOADFILE_TEMPHUNK	2
2056 #define	LOADFILE_CACHE		3
2057 #define	LOADFILE_STACK		4
2058 #define	LOADFILE_MALLOC		5
2059 
2060 static byte	*loadbuf;
2061 static cache_user_t *loadcache;
2062 static int	loadsize;
2063 
COM_LoadFile(const char * path,int usehunk,unsigned int * path_id)2064 byte *COM_LoadFile (const char *path, int usehunk, unsigned int *path_id)
2065 {
2066 	int		h;
2067 	byte	*buf;
2068 	char	base[32];
2069 	int		len;
2070 
2071 	buf = NULL;	// quiet compiler warning
2072 
2073 // look for it in the filesystem or pack files
2074 	len = COM_OpenFile (path, &h, path_id);
2075 	if (h == -1)
2076 		return NULL;
2077 
2078 // extract the filename base name for hunk tag
2079 	COM_FileBase (path, base, sizeof(base));
2080 
2081 	switch (usehunk)
2082 	{
2083 	case LOADFILE_HUNK:
2084 		buf = (byte *) Hunk_AllocName (len+1, base);
2085 		break;
2086 	case LOADFILE_TEMPHUNK:
2087 		buf = (byte *) Hunk_TempAlloc (len+1);
2088 		break;
2089 	case LOADFILE_ZONE:
2090 		buf = (byte *) Z_Malloc (len+1);
2091 		break;
2092 	case LOADFILE_CACHE:
2093 		buf = (byte *) Cache_Alloc (loadcache, len+1, base);
2094 		break;
2095 	case LOADFILE_STACK:
2096 		if (len < loadsize)
2097 			buf = loadbuf;
2098 		else
2099 			buf = (byte *) Hunk_TempAlloc (len+1);
2100 		break;
2101 	case LOADFILE_MALLOC:
2102 		buf = (byte *) malloc (len+1);
2103 		break;
2104 	default:
2105 		Sys_Error ("COM_LoadFile: bad usehunk");
2106 	}
2107 
2108 	if (!buf)
2109 		Sys_Error ("COM_LoadFile: not enough space for %s", path);
2110 
2111 	((byte *)buf)[len] = 0;
2112 
2113 	Sys_FileRead (h, buf, len);
2114 	COM_CloseFile (h);
2115 
2116 	return buf;
2117 }
2118 
COM_LoadHunkFile(const char * path,unsigned int * path_id)2119 byte *COM_LoadHunkFile (const char *path, unsigned int *path_id)
2120 {
2121 	return COM_LoadFile (path, LOADFILE_HUNK, path_id);
2122 }
2123 
COM_LoadZoneFile(const char * path,unsigned int * path_id)2124 byte *COM_LoadZoneFile (const char *path, unsigned int *path_id)
2125 {
2126 	return COM_LoadFile (path, LOADFILE_ZONE, path_id);
2127 }
2128 
COM_LoadTempFile(const char * path,unsigned int * path_id)2129 byte *COM_LoadTempFile (const char *path, unsigned int *path_id)
2130 {
2131 	return COM_LoadFile (path, LOADFILE_TEMPHUNK, path_id);
2132 }
2133 
COM_LoadCacheFile(const char * path,struct cache_user_s * cu,unsigned int * path_id)2134 void COM_LoadCacheFile (const char *path, struct cache_user_s *cu, unsigned int *path_id)
2135 {
2136 	loadcache = cu;
2137 	COM_LoadFile (path, LOADFILE_CACHE, path_id);
2138 }
2139 
2140 // uses temp hunk if larger than bufsize
COM_LoadStackFile(const char * path,void * buffer,int bufsize,unsigned int * path_id)2141 byte *COM_LoadStackFile (const char *path, void *buffer, int bufsize, unsigned int *path_id)
2142 {
2143 	byte	*buf;
2144 
2145 	loadbuf = (byte *)buffer;
2146 	loadsize = bufsize;
2147 	buf = COM_LoadFile (path, LOADFILE_STACK, path_id);
2148 
2149 	return buf;
2150 }
2151 
2152 // returns malloc'd memory
COM_LoadMallocFile(const char * path,unsigned int * path_id)2153 byte *COM_LoadMallocFile (const char *path, unsigned int *path_id)
2154 {
2155 	return COM_LoadFile (path, LOADFILE_MALLOC, path_id);
2156 }
2157 
COM_LoadMallocFile_TextMode_OSPath(const char * path,long * len_out)2158 byte *COM_LoadMallocFile_TextMode_OSPath (const char *path, long *len_out)
2159 {
2160 	FILE	*f;
2161 	byte	*data;
2162 	long	len, actuallen;
2163 
2164 	// ericw -- this is used by Host_Loadgame_f. Translate CRLF to LF on load games,
2165 	// othewise multiline messages have a garbage character at the end of each line.
2166 	// TODO: could handle in a way that allows loading CRLF savegames on mac/linux
2167 	// without the junk characters appearing.
2168 	f = fopen (path, "rt");
2169 	if (f == NULL)
2170 		return NULL;
2171 
2172 	len = COM_filelength (f);
2173 	if (len < 0)
2174 		return NULL;
2175 
2176 	data = (byte *) malloc (len + 1);
2177 	if (data == NULL)
2178 		return NULL;
2179 
2180 	// (actuallen < len) if CRLF to LF translation was performed
2181 	actuallen = fread (data, 1, len, f);
2182 	if (ferror(f))
2183 	{
2184 		free (data);
2185 		return NULL;
2186 	}
2187 	data[actuallen] = '\0';
2188 
2189 	if (len_out != NULL)
2190 		*len_out = actuallen;
2191 	return data;
2192 }
2193 
COM_ParseIntNewline(const char * buffer,int * value)2194 const char *COM_ParseIntNewline(const char *buffer, int *value)
2195 {
2196 	int consumed = 0;
2197 	sscanf (buffer, "%i\n%n", value, &consumed);
2198 	return buffer + consumed;
2199 }
2200 
COM_ParseFloatNewline(const char * buffer,float * value)2201 const char *COM_ParseFloatNewline(const char *buffer, float *value)
2202 {
2203 	int consumed = 0;
2204 	sscanf (buffer, "%f\n%n", value, &consumed);
2205 	return buffer + consumed;
2206 }
2207 
COM_ParseStringNewline(const char * buffer)2208 const char *COM_ParseStringNewline(const char *buffer)
2209 {
2210 	int consumed = 0;
2211 	com_token[0] = '\0';
2212 	sscanf (buffer, "%1023s\n%n", com_token, &consumed);
2213 	return buffer + consumed;
2214 }
2215 
2216 /*
2217 =================
2218 COM_LoadPackFile -- johnfitz -- modified based on topaz's tutorial
2219 
2220 Takes an explicit (not game tree related) path to a pak file.
2221 
2222 Loads the header and directory, adding the files at the beginning
2223 of the list so they override previous pack files.
2224 =================
2225 */
COM_LoadPackFile(const char * packfile)2226 static pack_t *COM_LoadPackFile (const char *packfile)
2227 {
2228 	dpackheader_t	header;
2229 	int		i;
2230 	packfile_t	*newfiles;
2231 	int		numpackfiles;
2232 	pack_t		*pack;
2233 	int		packhandle;
2234 	dpackfile_t	info[MAX_FILES_IN_PACK];
2235 	unsigned short	crc;
2236 
2237 	if (Sys_FileOpenRead (packfile, &packhandle) == -1)
2238 		return NULL;
2239 
2240 	Sys_FileRead (packhandle, (void *)&header, sizeof(header));
2241 	if (header.id[0] != 'P' || header.id[1] != 'A' || header.id[2] != 'C' || header.id[3] != 'K')
2242 		Sys_Error ("%s is not a packfile", packfile);
2243 
2244 	header.dirofs = LittleLong (header.dirofs);
2245 	header.dirlen = LittleLong (header.dirlen);
2246 
2247 	numpackfiles = header.dirlen / sizeof(dpackfile_t);
2248 
2249 	if (header.dirlen < 0 || header.dirofs < 0)
2250 	{
2251 		Sys_Error ("Invalid packfile %s (dirlen: %i, dirofs: %i)",
2252 					packfile, header.dirlen, header.dirofs);
2253 	}
2254 	if (!numpackfiles)
2255 	{
2256 		Sys_Printf ("WARNING: %s has no files, ignored\n", packfile);
2257 		Sys_FileClose (packhandle);
2258 		return NULL;
2259 	}
2260 	if (numpackfiles > MAX_FILES_IN_PACK)
2261 		Sys_Error ("%s has %i files", packfile, numpackfiles);
2262 
2263 	if (numpackfiles != PAK0_COUNT)
2264 		com_modified = true;	// not the original file
2265 
2266 	newfiles = (packfile_t *) Z_Malloc(numpackfiles * sizeof(packfile_t));
2267 
2268 	Sys_FileSeek (packhandle, header.dirofs);
2269 	Sys_FileRead (packhandle, (void *)info, header.dirlen);
2270 
2271 	// crc the directory to check for modifications
2272 	CRC_Init (&crc);
2273 	for (i = 0; i < header.dirlen; i++)
2274 		CRC_ProcessByte (&crc, ((byte *)info)[i]);
2275 	if (crc != PAK0_CRC_V106 && crc != PAK0_CRC_V101 && crc != PAK0_CRC_V100)
2276 		com_modified = true;
2277 
2278 	// parse the directory
2279 	for (i = 0; i < numpackfiles; i++)
2280 	{
2281 		q_strlcpy (newfiles[i].name, info[i].name, sizeof(newfiles[i].name));
2282 		newfiles[i].filepos = LittleLong(info[i].filepos);
2283 		newfiles[i].filelen = LittleLong(info[i].filelen);
2284 	}
2285 
2286 	pack = (pack_t *) Z_Malloc (sizeof (pack_t));
2287 	q_strlcpy (pack->filename, packfile, sizeof(pack->filename));
2288 	pack->handle = packhandle;
2289 	pack->numfiles = numpackfiles;
2290 	pack->files = newfiles;
2291 
2292 	//Sys_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
2293 	return pack;
2294 }
2295 
COM_GetGameNames(qboolean full)2296 const char *COM_GetGameNames(qboolean full)
2297 {
2298 	if (full)
2299 	{
2300 		if (*com_gamenames)
2301 			return va("%s;%s", GAMENAME, com_gamenames);
2302 		else
2303 			return GAMENAME;
2304 	}
2305 	return com_gamenames;
2306 //	return COM_SkipPath(com_gamedir);
2307 }
2308 
2309 //if either contain id1 then that gets ignored
COM_GameDirMatches(const char * tdirs)2310 qboolean COM_GameDirMatches(const char *tdirs)
2311 {
2312 	int gnl = strlen(GAMENAME);
2313 	const char *odirs = COM_GetGameNames(false);
2314 
2315 	//ignore any core paths.
2316 	if (!strncmp(tdirs, GAMENAME, gnl) && (tdirs[gnl] == ';' || !tdirs[gnl]))
2317 	{
2318 		tdirs+=gnl;
2319 		if (*tdirs == ';')
2320 			tdirs++;
2321 	}
2322 	if (!strncmp(odirs, GAMENAME, gnl) && (odirs[gnl] == ';' || !odirs[gnl]))
2323 	{
2324 		odirs+=gnl;
2325 		if (*odirs == ';')
2326 			odirs++;
2327 	}
2328 	//skip any qw in there from quakeworld (remote servers should really be skipping this, unless its maybe the only one in the path).
2329 	if (!strncmp(tdirs, "qw;", 3) || !strcmp(tdirs, "qw"))
2330 	{
2331 		tdirs+=2;
2332 		if (*tdirs==';')
2333 			tdirs++;
2334 	}
2335 	if (!strncmp(odirs, "qw;", 3) || !strcmp(odirs, "qw"))	//need to cope with ourselves setting it that way too, just in case.
2336 	{
2337 		odirs+=2;
2338 		if (*odirs==';')
2339 			odirs++;
2340 	}
2341 
2342 	//okay, now check it properly
2343 	if (!strcmp(odirs, tdirs))
2344 		return true;
2345 	return false;
2346 }
2347 
2348 /*
2349 =================
2350 COM_AddGameDirectory -- johnfitz -- modified based on topaz's tutorial
2351 =================
2352 */
COM_AddGameDirectory(const char * dir)2353 static void COM_AddGameDirectory (const char *dir)
2354 {
2355 	const char *base = com_basedir;
2356 	int i;
2357 	unsigned int path_id;
2358 	searchpath_t *search;
2359 	pack_t *pak, *qspak;
2360 	char pakfile[MAX_OSPATH];
2361 	qboolean been_here = false;
2362 
2363 	if (*com_gamenames)
2364 		q_strlcat(com_gamenames, ";", sizeof(com_gamenames));
2365 	q_strlcat(com_gamenames, dir, sizeof(com_gamenames));
2366 
2367 	// quakespasm enables mission pack flags automatically,
2368 	// so e.g. -game rogue works without breaking the hud
2369 	if (!q_strcasecmp(dir,"rogue")) {
2370 		rogue = true;
2371 		standard_quake = false;
2372 	}
2373 	if (!q_strcasecmp(dir,"hipnotic") || !q_strcasecmp(dir,"quoth")) {
2374 		hipnotic = true;
2375 		standard_quake = false;
2376 	}
2377 
2378 	q_strlcpy (com_gamedir, va("%s/%s", base, dir), sizeof(com_gamedir));
2379 
2380 	// assign a path_id to this game directory
2381 	if (com_searchpaths)
2382 		path_id = com_searchpaths->path_id << 1;
2383 	else	path_id = 1U;
2384 
2385 _add_path:
2386 	// add the directory to the search path
2387 	search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t));
2388 	search->path_id = path_id;
2389 	q_strlcpy (search->filename, com_gamedir, sizeof(search->filename));
2390 	search->next = com_searchpaths;
2391 	com_searchpaths = search;
2392 
2393 	// add any pak files in the format pak0.pak pak1.pak, ...
2394 	for (i = 0; ; i++)
2395 	{
2396 		q_snprintf (pakfile, sizeof(pakfile), "%s/pak%i.pak", com_gamedir, i);
2397 		pak = COM_LoadPackFile (pakfile);
2398 		if (i != 0 || path_id != 1 || fitzmode)
2399 			qspak = NULL;
2400 		else {
2401 			qboolean old = com_modified;
2402 			if (been_here) base = host_parms->userdir;
2403 			q_snprintf (pakfile, sizeof(pakfile), "%s/vkquake.pak", base);
2404 			qspak = COM_LoadPackFile (pakfile);
2405 			com_modified = old;
2406 		}
2407 		if (pak) {
2408 			search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t));
2409 			search->path_id = path_id;
2410 			search->pack = pak;
2411 			search->next = com_searchpaths;
2412 			com_searchpaths = search;
2413 		}
2414 		if (qspak) {
2415 			search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t));
2416 			search->path_id = path_id;
2417 			search->pack = qspak;
2418 			search->next = com_searchpaths;
2419 			com_searchpaths = search;
2420 		}
2421 		if (!pak) break;
2422 	}
2423 
2424 	if (!been_here && host_parms->userdir != host_parms->basedir)
2425 	{
2426 		been_here = true;
2427 		q_strlcpy(com_gamedir, va("%s/%s", host_parms->userdir, dir), sizeof(com_gamedir));
2428 		Sys_mkdir(com_gamedir);
2429 		goto _add_path;
2430 	}
2431 }
2432 
COM_ResetGameDirectories(const char * newdirs)2433 void COM_ResetGameDirectories(const char *newdirs)
2434 {
2435 	char *newgamedirs = Z_Strdup(newdirs);
2436 	char *newpath, *path;
2437 	searchpath_t *search;
2438 	//Kill the extra game if it is loaded
2439 	while (com_searchpaths != com_base_searchpaths)
2440 	{
2441 		if (com_searchpaths->pack)
2442 		{
2443 			Sys_FileClose (com_searchpaths->pack->handle);
2444 			Z_Free (com_searchpaths->pack->files);
2445 			Z_Free (com_searchpaths->pack);
2446 		}
2447 		search = com_searchpaths->next;
2448 		Z_Free (com_searchpaths);
2449 		com_searchpaths = search;
2450 	}
2451 	hipnotic = false;
2452 	rogue = false;
2453 	standard_quake = true;
2454 	//wipe the list of mod gamedirs
2455 	*com_gamenames = 0;
2456 	//reset this too
2457 	q_strlcpy (com_gamedir, va("%s/%s", (host_parms->userdir != host_parms->basedir)?host_parms->userdir:com_basedir, GAMENAME), sizeof(com_gamedir));
2458 
2459 	for(newpath = newgamedirs; newpath && *newpath; )
2460 	{
2461 		char *e = strchr(newpath, ';');
2462 		if (e)
2463 			*e++ = 0;
2464 
2465 		if (!q_strcasecmp(GAMENAME, newpath))
2466 			path = NULL;
2467 		else
2468 		{
2469 		    for (path = newgamedirs; path < newpath; path += strlen(path)+1)
2470 		    {
2471 			if (!q_strcasecmp(path, newpath))
2472 				break;
2473 		    }
2474 		}
2475 
2476 		if (path == newpath)	//not already loaded
2477 			COM_AddGameDirectory(newpath);
2478 		newpath = e;
2479 	}
2480 	Z_Free(newgamedirs);
2481 }
2482 
2483 //==============================================================================
2484 //johnfitz -- dynamic gamedir stuff -- modified by QuakeSpasm team.
2485 //==============================================================================
COM_Game_f(void)2486 static void COM_Game_f (void)
2487 {
2488 	if (Cmd_Argc() > 1)
2489 	{
2490 		int i, pri;
2491 		char paths[1024];
2492 
2493 		if (!registered.value) //disable shareware quake
2494 		{
2495 			Con_Printf("You must have the registered version to use modified games\n");
2496 			return;
2497 		}
2498 
2499 		*paths = 0;
2500 		q_strlcat(paths, GAMENAME, sizeof(paths));
2501 		for (pri = 0; pri <= 1; pri++)
2502 		{
2503 			for (i = 1; i < Cmd_Argc(); i++)
2504 			{
2505 				const char *p = Cmd_Argv(i);
2506 				if (!*p)
2507 					p = GAMENAME;
2508 				if (pri == 0)
2509 				{
2510 					if (*p != '-')
2511 						continue;
2512 					p++;
2513 				}
2514 				else if (*p == '-')
2515 					continue;
2516 
2517 				if (!*p || !strcmp(p, ".") || strstr(p, "..") || strstr(p, "/") || strstr(p, "\\") || strstr(p, ":"))
2518 				{
2519 					Con_Printf ("gamedir should be a single directory name, not a path\n");
2520 					return;
2521 				}
2522 
2523 				if (!q_strcasecmp(p, GAMENAME))
2524 					continue; //don't add id1, its not interesting enough.
2525 
2526 				if (*paths)
2527 					q_strlcat(paths, ";", sizeof(paths));
2528 				q_strlcat(paths, p, sizeof(paths));
2529 			}
2530 		}
2531 
2532 		if (!q_strcasecmp(paths, COM_GetGameNames(true)))
2533 		{
2534 			Con_Printf("\"game\" is already \"%s\"\n", COM_GetGameNames(true));
2535 			return;
2536 		}
2537 
2538 		com_modified = true;
2539 
2540 		//Kill the server
2541 		CL_Disconnect ();
2542 		Host_ShutdownServer(true);
2543 
2544 		//Write config file
2545 		Host_WriteConfiguration ();
2546 
2547 		COM_ResetGameDirectories(paths);
2548 
2549 		//clear out and reload appropriate data
2550 		Cache_Flush ();
2551 		Mod_ResetAll();
2552 		Sky_ClearAll();
2553 		if (!isDedicated)
2554 		{
2555 			TexMgr_NewGame ();
2556 			Draw_NewGame ();
2557 			R_NewGame ();
2558 		}
2559 		ExtraMaps_NewGame ();
2560 		DemoList_Rebuild ();
2561 
2562 		Con_Printf("\"game\" changed to \"%s\"\n", COM_GetGameNames(true));
2563 
2564 		VID_Lock ();
2565 		Cbuf_AddText ("exec quake.rc\n");
2566 		Cbuf_AddText ("vid_unlock\n");
2567 	}
2568 	else //Diplay the current gamedir
2569 		Con_Printf("\"game\" is \"%s\"\n", COM_GetGameNames(true));
2570 }
2571 
2572 /*
2573 =================
2574 COM_InitFilesystem
2575 =================
2576 */
COM_InitFilesystem(void)2577 void COM_InitFilesystem (void) //johnfitz -- modified based on topaz's tutorial
2578 {
2579 	int i, j;
2580 	const char *p;
2581 
2582 	Cvar_RegisterVariable (&registered);
2583 	Cvar_RegisterVariable (&cmdline);
2584 	Cmd_AddCommand ("path", COM_Path_f);
2585 	Cmd_AddCommand ("game", COM_Game_f); //johnfitz
2586 
2587 	i = COM_CheckParm ("-basedir");
2588 	if (i && i < com_argc-1)
2589 		q_strlcpy (com_basedir, com_argv[i + 1], sizeof(com_basedir));
2590 	else
2591 		q_strlcpy (com_basedir, DATADIR, sizeof(com_basedir));
2592 
2593 	j = strlen (com_basedir);
2594 	if (j < 1) Sys_Error("Bad argument to -basedir");
2595 	if ((com_basedir[j-1] == '\\') || (com_basedir[j-1] == '/'))
2596 		com_basedir[j-1] = 0;
2597 
2598 	i = COM_CheckParmNext (i, "-basegame");
2599 	if (i)
2600 	{	//-basegame:
2601 		// a) replaces all hardcoded dirs (read: alternative to id1)
2602 		// b) isn't flushed on normal gamedir switches (like id1).
2603 		com_modified = true; //shouldn't be relevant when not using id content... but we don't really know.
2604 		for(;; i = COM_CheckParmNext (i, "-basegame"))
2605 		{
2606 			if (!i || i >= com_argc-1)
2607 				break;
2608 
2609 			p = com_argv[i + 1];
2610 			if (!*p || !strcmp(p, ".") || strstr(p, "..") || strstr(p, "/") || strstr(p, "\\") || strstr(p, ":"))
2611 				Sys_Error ("gamedir should be a single directory name, not a path\n");
2612 			if (p != NULL)
2613 				COM_AddGameDirectory (p);
2614 		}
2615 	}
2616 	else
2617 	{
2618 		// start up with GAMENAME by default (id1)
2619 		COM_AddGameDirectory (GAMENAME);
2620 	}
2621 
2622 	/* this is the end of our base searchpath:
2623 	 * any set gamedirs, such as those from -game command line
2624 	 * arguments or by the 'game' console command will be freed
2625 	 * up to here upon a new game command. */
2626 	com_base_searchpaths = com_searchpaths;
2627 	COM_ResetGameDirectories("");
2628 
2629 	// add mission pack requests (only one should be specified)
2630 	if (COM_CheckParm ("-rogue"))
2631 		COM_AddGameDirectory ("rogue");
2632 	if (COM_CheckParm ("-hipnotic"))
2633 		COM_AddGameDirectory ("hipnotic");
2634 	if (COM_CheckParm ("-quoth"))
2635 		COM_AddGameDirectory ("quoth");
2636 
2637 
2638 	for(i = 0;;)
2639 	{
2640 		i = COM_CheckParmNext (i, "-game");
2641 		if (!i || i >= com_argc-1)
2642 			break;
2643 
2644 		p = com_argv[i + 1];
2645 		if (!*p || !strcmp(p, ".") || strstr(p, "..") || strstr(p, "/") || strstr(p, "\\") || strstr(p, ":"))
2646 			Sys_Error ("gamedir should be a single directory name, not a path\n");
2647 		com_modified = true;
2648 		if (p != NULL)
2649 			COM_AddGameDirectory (p);
2650 	}
2651 
2652 	if (COM_CheckParm ("-validation"))
2653 		vulkan_globals.validation = true;
2654 
2655 	COM_CheckRegistered ();
2656 }
2657 
2658 
2659 /* The following FS_*() stdio replacements are necessary if one is
2660  * to perform non-sequential reads on files reopened on pak files
2661  * because we need the bookkeeping about file start/end positions.
2662  * Allocating and filling in the fshandle_t structure is the users'
2663  * responsibility when the file is initially opened. */
2664 
FS_fread(void * ptr,size_t size,size_t nmemb,fshandle_t * fh)2665 size_t FS_fread(void *ptr, size_t size, size_t nmemb, fshandle_t *fh)
2666 {
2667 	long byte_size;
2668 	long bytes_read;
2669 	size_t nmemb_read;
2670 
2671 	if (!fh) {
2672 		errno = EBADF;
2673 		return 0;
2674 	}
2675 	if (!ptr) {
2676 		errno = EFAULT;
2677 		return 0;
2678 	}
2679 	if (!size || !nmemb) {	/* no error, just zero bytes wanted */
2680 		errno = 0;
2681 		return 0;
2682 	}
2683 
2684 	byte_size = nmemb * size;
2685 	if (byte_size > fh->length - fh->pos)	/* just read to end */
2686 		byte_size = fh->length - fh->pos;
2687 	bytes_read = fread(ptr, 1, byte_size, fh->file);
2688 	fh->pos += bytes_read;
2689 
2690 	/* fread() must return the number of elements read,
2691 	 * not the total number of bytes. */
2692 	nmemb_read = bytes_read / size;
2693 	/* even if the last member is only read partially
2694 	 * it is counted as a whole in the return value. */
2695 	if (bytes_read % size)
2696 		nmemb_read++;
2697 
2698 	return nmemb_read;
2699 }
2700 
FS_fseek(fshandle_t * fh,long offset,int whence)2701 int FS_fseek(fshandle_t *fh, long offset, int whence)
2702 {
2703 /* I don't care about 64 bit off_t or fseeko() here.
2704  * the quake/hexen2 file system is 32 bits, anyway. */
2705 	int ret;
2706 
2707 	if (!fh) {
2708 		errno = EBADF;
2709 		return -1;
2710 	}
2711 
2712 	/* the relative file position shouldn't be smaller
2713 	 * than zero or bigger than the filesize. */
2714 	switch (whence)
2715 	{
2716 	case SEEK_SET:
2717 		break;
2718 	case SEEK_CUR:
2719 		offset += fh->pos;
2720 		break;
2721 	case SEEK_END:
2722 		offset = fh->length + offset;
2723 		break;
2724 	default:
2725 		errno = EINVAL;
2726 		return -1;
2727 	}
2728 
2729 	if (offset < 0) {
2730 		errno = EINVAL;
2731 		return -1;
2732 	}
2733 
2734 	if (offset > fh->length)	/* just seek to end */
2735 		offset = fh->length;
2736 
2737 	ret = fseek(fh->file, fh->start + offset, SEEK_SET);
2738 	if (ret < 0)
2739 		return ret;
2740 
2741 	fh->pos = offset;
2742 	return 0;
2743 }
2744 
FS_fclose(fshandle_t * fh)2745 int FS_fclose(fshandle_t *fh)
2746 {
2747 	if (!fh) {
2748 		errno = EBADF;
2749 		return -1;
2750 	}
2751 	return fclose(fh->file);
2752 }
2753 
FS_ftell(fshandle_t * fh)2754 long FS_ftell(fshandle_t *fh)
2755 {
2756 	if (!fh) {
2757 		errno = EBADF;
2758 		return -1;
2759 	}
2760 	return fh->pos;
2761 }
2762 
FS_rewind(fshandle_t * fh)2763 void FS_rewind(fshandle_t *fh)
2764 {
2765 	if (!fh) return;
2766 	clearerr(fh->file);
2767 	fseek(fh->file, fh->start, SEEK_SET);
2768 	fh->pos = 0;
2769 }
2770 
FS_feof(fshandle_t * fh)2771 int FS_feof(fshandle_t *fh)
2772 {
2773 	if (!fh) {
2774 		errno = EBADF;
2775 		return -1;
2776 	}
2777 	if (fh->pos >= fh->length)
2778 		return -1;
2779 	return 0;
2780 }
2781 
FS_ferror(fshandle_t * fh)2782 int FS_ferror(fshandle_t *fh)
2783 {
2784 	if (!fh) {
2785 		errno = EBADF;
2786 		return -1;
2787 	}
2788 	return ferror(fh->file);
2789 }
2790 
FS_fgetc(fshandle_t * fh)2791 int FS_fgetc(fshandle_t *fh)
2792 {
2793 	if (!fh) {
2794 		errno = EBADF;
2795 		return EOF;
2796 	}
2797 	if (fh->pos >= fh->length)
2798 		return EOF;
2799 	fh->pos += 1;
2800 	return fgetc(fh->file);
2801 }
2802 
FS_fgets(char * s,int size,fshandle_t * fh)2803 char *FS_fgets(char *s, int size, fshandle_t *fh)
2804 {
2805 	char *ret;
2806 
2807 	if (FS_feof(fh))
2808 		return NULL;
2809 
2810 	if (size > (fh->length - fh->pos) + 1)
2811 		size = (fh->length - fh->pos) + 1;
2812 
2813 	ret = fgets(s, size, fh->file);
2814 	fh->pos = ftell(fh->file) - fh->start;
2815 
2816 	return ret;
2817 }
2818 
FS_filelength(fshandle_t * fh)2819 long FS_filelength (fshandle_t *fh)
2820 {
2821 	if (!fh) {
2822 		errno = EBADF;
2823 		return -1;
2824 	}
2825 	return fh->length;
2826 }
2827 
2828 #ifdef PSET_SCRIPT
2829 //for compat with dpp7 protocols, and mods that cba to precache things.
COM_Effectinfo_Enumerate(int (* cb)(const char * pname))2830 void COM_Effectinfo_Enumerate(int (*cb)(const char *pname))
2831 {
2832 	int i;
2833 	const char *f, *e;
2834 	char *buf;
2835 	static const char *dpnames[] =
2836 	{
2837 		"TE_GUNSHOT",
2838 		"TE_GUNSHOTQUAD",
2839 		"TE_SPIKE",
2840 		"TE_SPIKEQUAD",
2841 		"TE_SUPERSPIKE",
2842 		"TE_SUPERSPIKEQUAD",
2843 		"TE_WIZSPIKE",
2844 		"TE_KNIGHTSPIKE",
2845 		"TE_EXPLOSION",
2846 		"TE_EXPLOSIONQUAD",
2847 		"TE_TAREXPLOSION",
2848 		"TE_TELEPORT",
2849 		"TE_LAVASPLASH",
2850 		"TE_SMALLFLASH",
2851 		"TE_FLAMEJET",
2852 		"EF_FLAME",
2853 		"TE_BLOOD",
2854 		"TE_SPARK",
2855 		"TE_PLASMABURN",
2856 		"TE_TEI_G3",
2857 		"TE_TEI_SMOKE",
2858 		"TE_TEI_BIGEXPLOSION",
2859 		"TE_TEI_PLASMAHIT",
2860 		"EF_STARDUST",
2861 		"TR_ROCKET",
2862 		"TR_GRENADE",
2863 		"TR_BLOOD",
2864 		"TR_WIZSPIKE",
2865 		"TR_SLIGHTBLOOD",
2866 		"TR_KNIGHTSPIKE",
2867 		"TR_VORESPIKE",
2868 		"TR_NEHAHRASMOKE",
2869 		"TR_NEXUIZPLASMA",
2870 		"TR_GLOWTRAIL",
2871 		"SVC_PARTICLE",
2872 		NULL
2873 	};
2874 
2875 	buf = (char*)COM_LoadMallocFile("effectinfo.txt", NULL);
2876 	if (!buf)
2877 		return;
2878 
2879 	for (i = 0; dpnames[i]; i++)
2880 		cb(dpnames[i]);
2881 
2882 	for (f = buf; f; f = e)
2883 	{
2884 		e = COM_Parse (f);
2885 		if (!strcmp(com_token, "effect"))
2886 		{
2887 			e = COM_Parse (e);
2888 			cb(com_token);
2889 		}
2890 		while (e && *e && *e != '\n')
2891 			e++;
2892 	}
2893 	free(buf);
2894 }
2895 #endif
2896 
2897 /*
2898 ============================================================================
2899 								LOCALIZATION
2900 ============================================================================
2901 */
2902 typedef struct
2903 {
2904 	char *key;
2905 	char *value;
2906 } locentry_t;
2907 
2908 typedef struct
2909 {
2910 	int			numentries;
2911 	int			maxnumentries;
2912 	int			numindices;
2913 	unsigned	*indices;
2914 	locentry_t	*entries;
2915 	char		*text;
2916 } localization_t;
2917 
2918 static localization_t localization;
2919 
2920 /*
2921 ================
2922 COM_HashString
2923 Computes the FNV-1a hash of string str
2924 ================
2925 */
COM_HashString(const char * str)2926 unsigned COM_HashString (const char *str)
2927 {
2928 	unsigned hash = 0x811c9dc5u;
2929 	while (*str)
2930 	{
2931 		hash ^= *str++;
2932 		hash *= 0x01000193u;
2933 	}
2934 	return hash;
2935 }
2936 
mz_zip_file_read_func(void * opaque,mz_uint64 ofs,void * buf,size_t n)2937 static size_t mz_zip_file_read_func(void *opaque, mz_uint64 ofs, void *buf, size_t n)
2938 {
2939 	if (SDL_RWseek((SDL_RWops*)opaque, (Sint64)ofs, RW_SEEK_SET) < 0)
2940 		return 0;
2941 	return SDL_RWread((SDL_RWops*)opaque, buf, 1, n);
2942 }
2943 
2944 /*
2945 ================
2946 LOC_LoadFile
2947 ================
2948 */
LOC_LoadFile(const char * file)2949 void LOC_LoadFile (const char *file)
2950 {
2951 	char path[1024];
2952 	int i,lineno;
2953 	char *cursor;
2954 
2955 	SDL_RWops *rw = NULL;
2956 	Sint64 sz;
2957 	mz_zip_archive archive;
2958 	size_t size = 0;
2959 
2960 	// clear existing data
2961 	if (localization.text)
2962 	{
2963 		free(localization.text);
2964 		localization.text = NULL;
2965 	}
2966 	localization.numentries = 0;
2967 	localization.numindices = 0;
2968 
2969 	if (!file || !*file)
2970 		return;
2971 
2972 	Con_Printf("\nLanguage initialization\n");
2973 
2974 	memset(&archive, 0, sizeof(archive));
2975 	q_snprintf(path, sizeof(path), "%s/%s", com_basedir, file);
2976 	rw = SDL_RWFromFile(path, "rb");
2977 	#if defined(DO_USERDIRS)
2978 	if (!rw) {
2979 		q_snprintf(path, sizeof(path), "%s/%s", host_parms->userdir, file);
2980 		rw = SDL_RWFromFile(path, "rb");
2981 	}
2982 	#endif
2983 	if (!rw)
2984 	{
2985 		q_snprintf(path, sizeof(path), "%s/QuakeEX.kpf", com_basedir);
2986 		rw = SDL_RWFromFile(path, "rb");
2987 		#if defined(DO_USERDIRS)
2988 		if (!rw) {
2989 			q_snprintf(path, sizeof(path), "%s/QuakeEX.kpf", host_parms->userdir);
2990 			rw = SDL_RWFromFile(path, "rb");
2991 		}
2992 		#endif
2993 		if (!rw) goto fail;
2994 		sz = SDL_RWsize(rw);
2995 		if (sz <= 0) goto fail;
2996 		archive.m_pRead = mz_zip_file_read_func;
2997 		archive.m_pIO_opaque = rw;
2998 		if (!mz_zip_reader_init(&archive, sz, 0)) goto fail;
2999 		localization.text = (char *) mz_zip_reader_extract_file_to_heap(&archive, file, &size, 0);
3000 		if (!localization.text) goto fail;
3001 		mz_zip_reader_end(&archive);
3002 		SDL_RWclose(rw);
3003 		localization.text = (char *) realloc(localization.text, size+1);
3004 		localization.text[size] = 0;
3005 	}
3006 	else
3007 	{
3008 		sz = SDL_RWsize(rw);
3009 		if (sz <= 0) goto fail;
3010 		localization.text = (char *) calloc(1, sz+1);
3011 		if (!localization.text)
3012 		{
3013 fail:			mz_zip_reader_end(&archive);
3014 			if (rw) SDL_RWclose(rw);
3015 			Con_Printf("Couldn't load '%s'\nfrom '%s'\n", file, com_basedir);
3016 			return;
3017 		}
3018 		SDL_RWread(rw, localization.text, 1, sz);
3019 		SDL_RWclose(rw);
3020 	}
3021 
3022 	cursor = localization.text;
3023 
3024 	// skip BOM
3025 	if ((unsigned char)(cursor[0]) == 0xEF && (unsigned char)(cursor[1]) == 0xBB && cursor[2] == 0xB)
3026 		cursor += 3;
3027 
3028 	lineno = 0;
3029 	while (*cursor)
3030 	{
3031 		char *line, *equals;
3032 
3033 		lineno++;
3034 
3035 		// skip leading whitespace
3036 		while (q_isblank(*cursor))
3037 			++cursor;
3038 
3039 		line = cursor;
3040 		equals = NULL;
3041 		// find line end and first equals sign, if any
3042 		while (*cursor && *cursor != '\n')
3043 		{
3044 			if (*cursor == '=' && !equals)
3045 				equals = cursor;
3046 			cursor++;
3047 		}
3048 
3049 		if (line[0] == '/')
3050 		{
3051 			if (line[1] != '/')
3052 				Con_DPrintf("LOC_LoadFile: malformed comment on line %d\n", lineno);
3053 		}
3054 		else if (equals)
3055 		{
3056 			char *key_end = equals;
3057 			qboolean leading_quote;
3058 			qboolean trailing_quote;
3059 			locentry_t *entry;
3060 			char *value_src;
3061 			char *value_dst;
3062 			char *value;
3063 
3064 			// trim whitespace before equals sign
3065 			while (key_end != line && q_isspace(key_end[-1]))
3066 				key_end--;
3067 			*key_end = 0;
3068 
3069 			value = equals + 1;
3070 			// skip whitespace after equals sign
3071 			while (value != cursor && q_isspace(*value))
3072 				value++;
3073 
3074 			leading_quote = (*value == '\"');
3075 			trailing_quote = false;
3076 			value += leading_quote;
3077 
3078 			// transform escape sequences in-place
3079 			value_src = value;
3080 			value_dst = value;
3081 			while (value_src != cursor)
3082 			{
3083 				if (*value_src == '\\' && value_src + 1 != cursor)
3084 				{
3085 					char c = value_src[1];
3086 					value_src += 2;
3087 					switch (c)
3088 					{
3089 						case 'n': *value_dst++ = '\n'; break;
3090 						case 't': *value_dst++ = '\t'; break;
3091 						case 'v': *value_dst++ = '\v'; break;
3092 						case 'b': *value_dst++ = '\b'; break;
3093 						case 'f': *value_dst++ = '\f'; break;
3094 
3095 						case '"':
3096 						case '\'':
3097 							*value_dst++ = c;
3098 							break;
3099 
3100 						default:
3101 							Con_Printf("LOC_LoadFile: unrecognized escape sequence \\%c on line %d\n", c, lineno);
3102 							*value_dst++ = c;
3103 							break;
3104 					}
3105 					continue;
3106 				}
3107 
3108 				if (*value_src == '\"')
3109 				{
3110 					trailing_quote = true;
3111 					*value_dst = 0;
3112 					break;
3113 				}
3114 
3115 				*value_dst++ = *value_src++;
3116 			}
3117 
3118 			// if not a quoted string, trim trailing whitespace
3119 			if (!trailing_quote)
3120 			{
3121 				while (value_dst != value && q_isblank(value_dst[-1]))
3122 				{
3123 					*value_dst = 0;
3124 					value_dst--;
3125 				}
3126 			}
3127 
3128 			if (localization.numentries == localization.maxnumentries)
3129 			{
3130 				// grow by 50%
3131 				localization.maxnumentries += localization.maxnumentries >> 1;
3132 				localization.maxnumentries = q_max(localization.maxnumentries, 32);
3133 				localization.entries = (locentry_t*) realloc(localization.entries, sizeof(*localization.entries) * localization.maxnumentries);
3134 			}
3135 
3136 			entry = &localization.entries[localization.numentries++];
3137 			entry->key = line;
3138 			entry->value = value;
3139 		}
3140 
3141 		if (*cursor)
3142 			*cursor++ = 0; // terminate line and advance to next
3143 	}
3144 
3145 	// hash all entries
3146 
3147 	localization.numindices = localization.numentries * 2; // 50% load factor
3148 	if (localization.numindices == 0)
3149 	{
3150 		Con_Printf("No localized strings in file '%s'\n", file);
3151 		return;
3152 	}
3153 
3154 	localization.indices = (unsigned*) realloc(localization.indices, localization.numindices * sizeof(*localization.indices));
3155 	memset(localization.indices, 0, localization.numindices * sizeof(*localization.indices));
3156 
3157 	for (i = 0; i < localization.numentries; i++)
3158 	{
3159 		locentry_t *entry = &localization.entries[i];
3160 		unsigned pos = COM_HashString(entry->key) % localization.numindices, end = pos;
3161 
3162 		for (;;)
3163 		{
3164 			if (!localization.indices[pos])
3165 			{
3166 				localization.indices[pos] = i + 1;
3167 				break;
3168 			}
3169 
3170 			++pos;
3171 			if (pos == localization.numindices)
3172 				pos = 0;
3173 
3174 			if (pos == end)
3175 				Sys_Error("LOC_LoadFile failed");
3176 		}
3177 	}
3178 
3179 	Con_Printf("Loaded %d strings from '%s'\n", localization.numentries, file);
3180 }
3181 
3182 /*
3183 ================
3184 LOC_Init
3185 ================
3186 */
LOC_Init(void)3187 void LOC_Init(void)
3188 {
3189 	LOC_LoadFile("localization/loc_english.txt");
3190 }
3191 
3192 /*
3193 ================
3194 LOC_Shutdown
3195 ================
3196 */
LOC_Shutdown(void)3197 void LOC_Shutdown(void)
3198 {
3199 	free(localization.indices);
3200 	free(localization.entries);
3201 	free(localization.text);
3202 }
3203 
3204 /*
3205 ================
3206 LOC_GetRawString
3207 
3208 Returns localized string if available, or NULL otherwise
3209 ================
3210 */
LOC_GetRawString(const char * key)3211 const char* LOC_GetRawString (const char *key)
3212 {
3213 	unsigned pos, end;
3214 
3215 	if (!localization.numindices || !key || !*key || *key != '$')
3216 		return NULL;
3217 	key++;
3218 
3219 	pos = COM_HashString(key) % localization.numindices;
3220 	end = pos;
3221 
3222 	do
3223 	{
3224 		unsigned idx = localization.indices[pos];
3225 		locentry_t *entry;
3226 		if (!idx)
3227 			return NULL;
3228 
3229 		entry = &localization.entries[idx - 1];
3230 		if (!Q_strcmp(entry->key, key))
3231 			return entry->value;
3232 
3233 		++pos;
3234 		if (pos == localization.numindices)
3235 			pos = 0;
3236 	} while (pos != end);
3237 
3238 	return NULL;
3239 }
3240 
3241 /*
3242 ================
3243 LOC_GetString
3244 
3245 Returns localized string if available, or input string otherwise
3246 ================
3247 */
LOC_GetString(const char * key)3248 const char* LOC_GetString (const char *key)
3249 {
3250 	const char* value = LOC_GetRawString(key);
3251 	return value ? value : key;
3252 }
3253 
3254 /*
3255 ================
3256 LOC_ParseArg
3257 
3258 Returns argument index (>= 0) and advances the string if it starts with a placeholder ({} or {N}),
3259 otherwise returns a negative value and leaves the pointer unchanged
3260 ================
3261 */
LOC_ParseArg(const char ** pstr)3262 static int LOC_ParseArg (const char **pstr)
3263 {
3264 	int arg;
3265 	const char *str = *pstr;
3266 
3267 	// opening brace
3268 	if (*str != '{')
3269 		return -1;
3270 	++str;
3271 
3272 	// optional index, defaulting to 0
3273 	arg = 0;
3274 	while (q_isdigit(*str))
3275 		arg = arg * 10 + *str++ - '0';
3276 
3277 	// closing brace
3278 	if (*str != '}')
3279 		return -1;
3280 	*pstr = ++str;
3281 
3282 	return arg;
3283 }
3284 
3285 /*
3286 ================
3287 LOC_HasPlaceholders
3288 ================
3289 */
LOC_HasPlaceholders(const char * str)3290 qboolean LOC_HasPlaceholders (const char *str)
3291 {
3292 	if (!localization.numindices)
3293 		return false;
3294 	while (*str)
3295 	{
3296 		if (LOC_ParseArg(&str) >= 0)
3297 			return true;
3298 		str++;
3299 	}
3300 	return false;
3301 }
3302 
3303 /*
3304 ================
3305 LOC_Format
3306 
3307 Replaces placeholders (of the form {} or {N}) with the corresponding arguments
3308 
3309 Returns number of written chars, excluding the NUL terminator
3310 If len > 0, output is always NUL-terminated
3311 ================
3312 */
LOC_Format(const char * format,const char * (* getarg_fn)(int idx,void * userdata),void * userdata,char * out,size_t len)3313 size_t LOC_Format (const char *format, const char* (*getarg_fn) (int idx, void* userdata), void* userdata, char* out, size_t len)
3314 {
3315 	size_t written = 0;
3316 	int numargs = 0;
3317 
3318 	if (!len)
3319 	{
3320 		Con_DPrintf("LOC_Format: no output space\n");
3321 		return 0;
3322 	}
3323 	--len; // reserve space for the terminator
3324 
3325 	while (*format && written < len)
3326 	{
3327 		const char* insert;
3328 		size_t space_left;
3329 		size_t insert_len;
3330 		int argindex = LOC_ParseArg(&format);
3331 
3332 		if (argindex < 0)
3333 		{
3334 			out[written++] = *format++;
3335 			continue;
3336 		}
3337 
3338 		insert = getarg_fn(argindex, userdata);
3339 		space_left = len - written;
3340 		insert_len = Q_strlen(insert);
3341 
3342 		if (insert_len > space_left)
3343 		{
3344 			Con_DPrintf("LOC_Format: overflow at argument #%d\n", numargs);
3345 			insert_len = space_left;
3346 		}
3347 
3348 		Q_memcpy(out + written, insert, insert_len);
3349 		written += insert_len;
3350 	}
3351 
3352 	if (*format)
3353 		Con_DPrintf("LOC_Format: overflow\n");
3354 
3355 	out[written] = 0;
3356 
3357 	return written;
3358 }
3359