blob: 881c01e22e91f7b5c1db108c624978e1450c7e76 [file] [log] [blame]
Franklin Wei5d05b9d2018-02-11 15:34:30 -05001/*
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 char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
27static char *argvdummy = " ";
28
29static char *safeargvs[NUM_SAFE_ARGVS] =
30 {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"};
31
32cvar_t registered = {"registered","0"};
33cvar_t cmdline = {"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;
51char **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, 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 (void *m1, 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, char *src)
181{
182 while (*src)
183 {
184 *dest++ = *src++;
185 }
186 *dest++ = 0;
187}
188
189void Q_strncpy (char *dest, 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 (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(char *s, char c)
211{
212 int len = Q_strlen(s);
213 s += len;
214 while (len--)
215 if (*--s == c) return s;
216 return 0;
217}
218
219void Q_strcat (char *dest, char *src)
220{
221 dest += Q_strlen(dest);
222 Q_strcpy (dest, src);
223}
224
225int Q_strcmp (char *s1, 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 (char *s1, 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 (char *s1, 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 (char *s1, char *s2)
288{
289 return Q_strncasecmp (s1, s2, 99999);
290}
291
292int Q_atoi (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 (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
434#ifdef SDL
435#include "SDL_byteorder.h"
436#endif
437
438qboolean bigendien;
439
440short (*BigShort) (short l);
441short (*LittleShort) (short l);
442int (*BigLong) (int l);
443int (*LittleLong) (int l);
444float (*BigFloat) (float l);
445float (*LittleFloat) (float l);
446
447short ShortSwap (short l)
448{
Franklin Weief9ee892019-07-20 20:40:51 -0400449 byte b1,b2;
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500450
Franklin Weief9ee892019-07-20 20:40:51 -0400451 b1 = l&255;
452 b2 = (l>>8)&255;
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500453
Franklin Weief9ee892019-07-20 20:40:51 -0400454 return (b1<<8) + b2;
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500455}
456
457short ShortNoSwap (short l)
458{
Franklin Weief9ee892019-07-20 20:40:51 -0400459 return l;
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500460}
461
462int LongSwap (int l)
463{
Franklin Weief9ee892019-07-20 20:40:51 -0400464 byte b1,b2,b3,b4;
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500465
Franklin Weief9ee892019-07-20 20:40:51 -0400466 b1 = l&255;
467 b2 = (l>>8)&255;
468 b3 = (l>>16)&255;
469 b4 = (l>>24)&255;
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500470
Franklin Weief9ee892019-07-20 20:40:51 -0400471 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500472}
473
474int LongNoSwap (int l)
475{
Franklin Weief9ee892019-07-20 20:40:51 -0400476 return l;
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500477}
478
479float FloatSwap (float f)
480{
Franklin Weief9ee892019-07-20 20:40:51 -0400481 union
482 {
483 float f;
484 byte b[4];
485 } dat1, dat2;
486
487
488 dat1.f = f;
489 dat2.b[0] = dat1.b[3];
490 dat2.b[1] = dat1.b[2];
491 dat2.b[2] = dat1.b[1];
492 dat2.b[3] = dat1.b[0];
493 return dat2.f;
494}
495
496float FloatNoSwap (float f)
497{
498 return f;
499}
500
501// safe for unaligned accesses
Franklin Wei7e4902b2019-07-24 21:01:44 -0400502short ReadLittleShort (unsigned char *l)
Franklin Weief9ee892019-07-20 20:40:51 -0400503{
504 return *(l + 0) | (*(l + 1) << 8);
505}
506
Franklin Wei7e4902b2019-07-24 21:01:44 -0400507short ReadBigShort (unsigned char *l)
Franklin Weief9ee892019-07-20 20:40:51 -0400508{
509 return *(l + 1) | (*(l + 0) << 8);
510}
511
Franklin Wei7e4902b2019-07-24 21:01:44 -0400512int ReadLittleLong (unsigned char *l)
Franklin Weief9ee892019-07-20 20:40:51 -0400513{
514 return *(l + 0) | (*(l + 1) << 8) | (*(l + 2) << 16) | (*(l + 3) << 24);
515}
516
Franklin Wei7e4902b2019-07-24 21:01:44 -0400517int ReadBigLong (unsigned char *l)
Franklin Weief9ee892019-07-20 20:40:51 -0400518{
519 return *(l + 3) | (*(l + 2) << 8) | (*(l + 1) << 16) | (*(l + 0) << 24);
520}
521
522// same
Franklin Wei7e4902b2019-07-24 21:01:44 -0400523float ReadLittleFloat (unsigned char *f)
Franklin Weief9ee892019-07-20 20:40:51 -0400524{
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500525 union
526 {
527 float f;
528 byte b[4];
Franklin Weief9ee892019-07-20 20:40:51 -0400529 } dat2;
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500530
531
Franklin Weief9ee892019-07-20 20:40:51 -0400532 dat2.b[0] = f[0];
533 dat2.b[1] = f[1];
534 dat2.b[2] = f[2];
535 dat2.b[3] = f[3];
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500536 return dat2.f;
537}
538
Franklin Wei7e4902b2019-07-24 21:01:44 -0400539float ReadBigFloat (unsigned char *f)
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500540{
Franklin Weief9ee892019-07-20 20:40:51 -0400541 union
542 {
543 float f;
544 byte b[4];
545 } dat2;
546
547
548 dat2.b[0] = f[0];
549 dat2.b[1] = f[1];
550 dat2.b[2] = f[2];
551 dat2.b[3] = f[3];
552 return dat2.f;
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500553}
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500554/*
555==============================================================================
556
557 MESSAGE IO FUNCTIONS
558
559Handles byte ordering and avoids alignment errors
560==============================================================================
561*/
562
563//
564// writing functions
565//
566
567void MSG_WriteChar (sizebuf_t *sb, int c)
568{
569 byte *buf;
570
571#ifdef PARANOID
572 if (c < -128 || c > 127)
573 Sys_Error ("MSG_WriteChar: range error");
574#endif
575
576 buf = SZ_GetSpace (sb, 1);
577 buf[0] = c;
578}
579
580void MSG_WriteByte (sizebuf_t *sb, int c)
581{
582 byte *buf;
583
584#ifdef PARANOID
585 if (c < 0 || c > 255)
586 Sys_Error ("MSG_WriteByte: range error");
587#endif
588
589 buf = SZ_GetSpace (sb, 1);
590 buf[0] = c;
591}
592
593void MSG_WriteShort (sizebuf_t *sb, int c)
594{
595 byte *buf;
596
597#ifdef PARANOID
598 if (c < ((short)0x8000) || c > (short)0x7fff)
599 Sys_Error ("MSG_WriteShort: range error");
600#endif
601
602 buf = SZ_GetSpace (sb, 2);
603 buf[0] = c&0xff;
604 buf[1] = c>>8;
605}
606
607void MSG_WriteLong (sizebuf_t *sb, int c)
608{
609 byte *buf;
610
611 buf = SZ_GetSpace (sb, 4);
612 buf[0] = c&0xff;
613 buf[1] = (c>>8)&0xff;
614 buf[2] = (c>>16)&0xff;
615 buf[3] = c>>24;
616}
617
618void MSG_WriteFloat (sizebuf_t *sb, float f)
619{
620 union
621 {
622 float f;
623 int l;
624 } dat;
625
626
627 dat.f = f;
628 dat.l = LittleLong (dat.l);
629
630 SZ_Write (sb, &dat.l, 4);
631}
632
633void MSG_WriteString (sizebuf_t *sb, char *s)
634{
635 if (!s)
636 SZ_Write (sb, "", 1);
637 else
638 SZ_Write (sb, s, Q_strlen(s)+1);
639}
640
641void MSG_WriteCoord (sizebuf_t *sb, float f)
642{
643 MSG_WriteShort (sb, (int)(f*8));
644}
645
646void MSG_WriteAngle (sizebuf_t *sb, float f)
647{
648 MSG_WriteByte (sb, ((int)f*256/360) & 255);
649}
650
651//
652// reading functions
653//
654int msg_readcount;
655qboolean msg_badread;
656
657void MSG_BeginReading (void)
658{
659 msg_readcount = 0;
660 msg_badread = false;
661}
662
663// returns -1 and sets msg_badread if no more characters are available
664int MSG_ReadChar (void)
665{
666 int c;
667
668 if (msg_readcount+1 > net_message.cursize)
669 {
670 msg_badread = true;
671 return -1;
672 }
673
674 c = (signed char)net_message.data[msg_readcount];
675 msg_readcount++;
676
677 return c;
678}
679
680int MSG_ReadByte (void)
681{
682 int c;
683
684 if (msg_readcount+1 > net_message.cursize)
685 {
686 msg_badread = true;
687 return -1;
688 }
689
690 c = (unsigned char)net_message.data[msg_readcount];
691 msg_readcount++;
692
693 return c;
694}
695
696int MSG_ReadShort (void)
697{
698 int c;
699
700 if (msg_readcount+2 > net_message.cursize)
701 {
702 msg_badread = true;
703 return -1;
704 }
705
706 c = (short)(net_message.data[msg_readcount]
707 + (net_message.data[msg_readcount+1]<<8));
708
709 msg_readcount += 2;
710
711 return c;
712}
713
714int MSG_ReadLong (void)
715{
716 int c;
717
718 if (msg_readcount+4 > net_message.cursize)
719 {
720 msg_badread = true;
721 return -1;
722 }
723
724 c = net_message.data[msg_readcount]
725 + (net_message.data[msg_readcount+1]<<8)
726 + (net_message.data[msg_readcount+2]<<16)
727 + (net_message.data[msg_readcount+3]<<24);
728
729 msg_readcount += 4;
730
731 return c;
732}
733
734float MSG_ReadFloat (void)
735{
736 union
737 {
738 byte b[4];
739 float f;
740 int l;
741 } dat;
742
743 dat.b[0] = net_message.data[msg_readcount];
744 dat.b[1] = net_message.data[msg_readcount+1];
745 dat.b[2] = net_message.data[msg_readcount+2];
746 dat.b[3] = net_message.data[msg_readcount+3];
747 msg_readcount += 4;
748
749 dat.l = LittleLong (dat.l);
750
751 return dat.f;
752}
753
754char *MSG_ReadString (void)
755{
756 static char string[2048];
757 int l,c;
758
759 l = 0;
760 do
761 {
762 c = MSG_ReadChar ();
763 if (c == -1 || c == 0)
764 break;
765 string[l] = c;
766 l++;
767 } while (l < sizeof(string)-1);
768
769 string[l] = 0;
770
771 return string;
772}
773
774float MSG_ReadCoord (void)
775{
776 return MSG_ReadShort() * (1.0/8);
777}
778
779float MSG_ReadAngle (void)
780{
781 return MSG_ReadChar() * (360.0/256);
782}
783
784
785
786//===========================================================================
787
788void SZ_Alloc (sizebuf_t *buf, int startsize)
789{
790 if (startsize < 256)
791 startsize = 256;
792 buf->data = Hunk_AllocName (startsize, "sizebuf");
793 buf->maxsize = startsize;
794 buf->cursize = 0;
795}
796
797
798void SZ_Free (sizebuf_t *buf)
799{
800// Z_Free (buf->data);
801// buf->data = NULL;
802// buf->maxsize = 0;
803 buf->cursize = 0;
804}
805
806void SZ_Clear (sizebuf_t *buf)
807{
808 buf->cursize = 0;
809}
810
811void *SZ_GetSpace (sizebuf_t *buf, int length)
812{
813 void *data;
814
815 if (buf->cursize + length > buf->maxsize)
816 {
817 if (!buf->allowoverflow)
818 Sys_Error ("SZ_GetSpace: overflow without allowoverflow set");
819
820 if (length > buf->maxsize)
821 Sys_Error ("SZ_GetSpace: %i is > full buffer size", length);
822
823 buf->overflowed = true;
824 Con_Printf ("SZ_GetSpace: overflow");
825 SZ_Clear (buf);
826 }
827
828 data = buf->data + buf->cursize;
829 buf->cursize += length;
830
831 return data;
832}
833
834void SZ_Write (sizebuf_t *buf, void *data, int length)
835{
836 Q_memcpy (SZ_GetSpace(buf,length),data,length);
837}
838
839void SZ_Print (sizebuf_t *buf, char *data)
840{
841 int len;
842
843 len = Q_strlen(data)+1;
844
845// byte * cast to keep VC++ happy
846 if (buf->data[buf->cursize-1])
847 Q_memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
848 else
849 Q_memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
850}
851
852
853//============================================================================
854
855
856/*
857============
858COM_SkipPath
859============
860*/
861char *COM_SkipPath (char *pathname)
862{
863 char *last;
864
865 last = pathname;
866 while (*pathname)
867 {
868 if (*pathname=='/')
869 last = pathname+1;
870 pathname++;
871 }
872 return last;
873}
874
875/*
876============
877COM_StripExtension
878============
879*/
880void COM_StripExtension (char *in, char *out)
881{
882 while (*in && *in != '.')
883 *out++ = *in++;
884 *out = 0;
885}
886
887/*
888============
889COM_FileExtension
890============
891*/
892char *COM_FileExtension (char *in)
893{
894 static char exten[8];
895 int i;
896
897 while (*in && *in != '.')
898 in++;
899 if (!*in)
900 return "";
901 in++;
902 for (i=0 ; i<7 && *in ; i++,in++)
903 exten[i] = *in;
904 exten[i] = 0;
905 return exten;
906}
907
908/*
909============
910COM_FileBase
911============
912*/
913int *check_ptr = NULL;
914
915void COM_FileBase (char *in, char *out)
916{
917 char *s, *s2;
918
919 s = in + strlen(in) - 1;
920
921 while (s != in && *s != '.')
922 s--;
923
924 for (s2 = s ; *s2 && *s2 != '/' && s2 != in; s2--)
925 ;
926
927 if (s-s2 < 2)
928 {
929 strcpy (out,"?model?");
930 }
931 else
932 {
933 /* BUG */
934 s--;
935 //printf("writing %d bytes to outbuf", s-s2);
936 Q_strncpy (out,s2+1, s-s2);
937 out[s-s2] = 0;
938 }
939}
940
941
942/*
943==================
944COM_DefaultExtension
945==================
946*/
947void COM_DefaultExtension (char *path, char *extension)
948{
949 char *src;
950//
951// if path doesn't have a .EXT, append extension
952// (extension should include the .)
953//
954 src = path + strlen(path) - 1;
955
956 while (*src != '/' && src != path)
957 {
958 if (*src == '.')
959 return; // it has an extension
960 src--;
961 }
962
963 strcat (path, extension);
964}
965
966
967/*
968==============
969COM_Parse
970
971Parse a token out of a string
972==============
973*/
974char *COM_Parse (char *data)
975{
976 int c;
977 int len;
978
979 len = 0;
980 com_token[0] = 0;
981
982 if (!data)
983 return NULL;
984
985// skip whitespace
986skipwhite:
987 while ( (c = *data) <= ' ')
988 {
989 if (c == 0)
990 return NULL; // end of file;
991 data++;
992 }
993
994// skip // comments
995 if (c=='/' && data[1] == '/')
996 {
997 while (*data && *data != '\n')
998 data++;
999 goto skipwhite;
1000 }
1001
1002
1003// handle quoted strings specially
1004 if (c == '\"')
1005 {
1006 data++;
1007 while (1)
1008 {
1009 c = *data++;
1010 if (c=='\"' || !c)
1011 {
1012 com_token[len] = 0;
1013 return data;
1014 }
1015 com_token[len] = c;
1016 len++;
1017 }
1018 }
1019
1020// parse single characters
1021 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1022 {
1023 com_token[len] = c;
1024 len++;
1025 com_token[len] = 0;
1026 return data+1;
1027 }
1028
1029// parse a regular word
1030 do
1031 {
1032 com_token[len] = c;
1033 data++;
1034 len++;
1035 c = *data;
1036 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1037 break;
1038 } while (c>32);
1039
1040 com_token[len] = 0;
1041 return data;
1042}
1043
1044
1045/*
1046================
1047COM_CheckParm
1048
1049Returns the position (1 to argc-1) in the program's argument list
1050where the given parameter apears, or 0 if not present
1051================
1052*/
1053int COM_CheckParm (char *parm)
1054{
1055 int i;
1056
1057 for (i=1 ; i<com_argc ; i++)
1058 {
1059 if (!com_argv[i])
1060 continue; // NEXTSTEP sometimes clears appkit vars.
1061 if (!Q_strcmp (parm,com_argv[i]))
1062 return i;
1063 }
1064
1065 return 0;
1066}
1067
1068/*
1069================
1070COM_CheckRegistered
1071
1072Looks for the pop.txt file and verifies it.
1073Sets the "registered" cvar.
1074Immediately exits out if an alternate game was attempted to be started without
1075being registered.
1076================
1077*/
1078void COM_CheckRegistered (void)
1079{
1080 int h;
1081 unsigned short check[128];
1082 int i;
1083
1084 COM_OpenFile("gfx/pop.lmp", &h);
1085 static_registered = 0;
1086
1087 if (h == -1)
1088 {
1089#if WINDED
1090 Sys_Error ("This dedicated server requires a full registered copy of Quake");
1091#endif
1092 Con_Printf ("Playing shareware version.\n");
1093 if (com_modified)
1094 Sys_Error ("You must have the registered version to use modified games");
1095 return;
1096 }
1097
1098 Sys_FileRead (h, check, sizeof(check));
1099 COM_CloseFile (h);
1100
1101 for (i=0 ; i<128 ; i++)
1102 if (pop[i] != (unsigned short)BigShort (check[i]))
1103 Sys_Error ("Corrupted data file.");
1104
1105 Cvar_Set ("cmdline", com_cmdline);
1106 Cvar_Set ("registered", "1");
1107 static_registered = 1;
1108 Con_Printf ("Playing registered version.\n");
1109}
1110
1111
1112void COM_Path_f (void);
1113
1114
1115/*
1116================
1117COM_InitArgv
1118================
1119*/
1120void COM_InitArgv (int argc, char **argv)
1121{
1122 qboolean safe;
1123 int i, j, n;
1124
1125// reconstitute the command line for the cmdline externally visible cvar
1126 n = 0;
1127
1128 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1129 {
1130 i = 0;
1131
1132 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1133 {
1134 com_cmdline[n++] = argv[j][i++];
1135 }
1136
1137 if (n < (CMDLINE_LENGTH - 1))
1138 com_cmdline[n++] = ' ';
1139 else
1140 break;
1141 }
1142
1143 com_cmdline[n] = 0;
1144
1145 safe = false;
1146
1147 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1148 com_argc++)
1149 {
1150 largv[com_argc] = argv[com_argc];
1151 if (!Q_strcmp ("-safe", argv[com_argc]))
1152 safe = true;
1153 }
1154
1155 if (safe)
1156 {
1157 // force all the safe-mode switches. Note that we reserved extra space in
1158 // case we need to add these, so we don't need an overflow check
1159 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1160 {
1161 largv[com_argc] = safeargvs[i];
1162 com_argc++;
1163 }
1164 }
1165
1166 largv[com_argc] = argvdummy;
1167 com_argv = largv;
1168
1169 if (COM_CheckParm ("-rogue"))
1170 {
1171 rogue = true;
1172 standard_quake = false;
1173 }
1174
1175 if (COM_CheckParm ("-hipnotic"))
1176 {
1177 hipnotic = true;
1178 standard_quake = false;
1179 }
1180}
1181
1182
1183/*
1184================
1185COM_Init
1186================
1187*/
1188void COM_Init (char *basedir)
1189{
1190 byte swaptest[2] = {1,0};
1191
Franklin Weief9ee892019-07-20 20:40:51 -04001192#if 1
Franklin Wei5d05b9d2018-02-11 15:34:30 -05001193// set the byte swapping variables in a portable manner
1194#ifdef SDL
1195 // This is necessary because egcs 1.1.1 mis-compiles swaptest with -O2
1196 if ( SDL_BYTEORDER == SDL_LIL_ENDIAN )
1197#else
1198 if ( *(short *)swaptest == 1)
1199#endif
1200 {
1201 bigendien = false;
1202 BigShort = ShortSwap;
1203 LittleShort = ShortNoSwap;
1204 BigLong = LongSwap;
1205 LittleLong = LongNoSwap;
1206 BigFloat = FloatSwap;
1207 LittleFloat = FloatNoSwap;
1208 }
1209 else
1210 {
1211 bigendien = true;
1212 BigShort = ShortNoSwap;
1213 LittleShort = ShortSwap;
1214 BigLong = LongNoSwap;
1215 LittleLong = LongSwap;
1216 BigFloat = FloatNoSwap;
1217 LittleFloat = FloatSwap;
1218 }
Franklin Weief9ee892019-07-20 20:40:51 -04001219#endif
Franklin Wei5d05b9d2018-02-11 15:34:30 -05001220
1221 Cvar_RegisterVariable (&registered);
1222 Cvar_RegisterVariable (&cmdline);
1223 Cmd_AddCommand ("path", COM_Path_f);
1224
1225 COM_InitFilesystem ();
1226 COM_CheckRegistered ();
1227}
1228
1229
1230/*
1231============
1232va
1233
1234does a varargs printf into a temp buffer, so I don't need to have
1235varargs versions of all text functions.
1236FIXME: make this buffer size safe someday
1237============
1238*/
1239char *va(char *format, ...)
1240{
1241 va_list argptr;
1242 static char string[1024];
1243
1244 va_start (argptr, format);
1245 vsprintf (string, format,argptr);
1246 va_end (argptr);
1247
1248 return string;
1249}
1250
1251
1252/// just for debugging
1253int memsearch (byte *start, int count, int search)
1254{
1255 int i;
1256
1257 for (i=0 ; i<count ; i++)
1258 if (start[i] == search)
1259 return i;
1260 return -1;
1261}
1262
1263/*
1264=============================================================================
1265
1266QUAKE FILESYSTEM
1267
1268=============================================================================
1269*/
1270
1271int com_filesize;
1272
1273
1274//
1275// in memory
1276//
1277
1278typedef struct
1279{
1280 char name[MAX_QPATH];
1281 int filepos, filelen;
1282} packfile_t;
1283
1284typedef struct pack_s
1285{
1286 char filename[MAX_OSPATH];
1287 int handle;
1288 int numfiles;
1289 packfile_t *files;
1290} pack_t;
1291
1292//
1293// on disk
1294//
1295typedef struct
1296{
1297 char name[56];
1298 int filepos, filelen;
1299} dpackfile_t;
1300
1301typedef struct
1302{
1303 char id[4];
1304 int dirofs;
1305 int dirlen;
1306} dpackheader_t;
1307
1308#define MAX_FILES_IN_PACK 2048
1309
1310char com_cachedir[MAX_OSPATH];
1311char com_gamedir[MAX_OSPATH];
1312
1313typedef struct searchpath_s
1314{
1315 char filename[MAX_OSPATH];
1316 pack_t *pack; // only one of filename / pack will be used
1317 struct searchpath_s *next;
1318} searchpath_t;
1319
1320searchpath_t *com_searchpaths;
1321
1322/*
1323============
1324COM_Path_f
1325
1326============
1327*/
1328void COM_Path_f (void)
1329{
1330 searchpath_t *s;
1331
1332 Con_Printf ("Current search path:\n");
1333 for (s=com_searchpaths ; s ; s=s->next)
1334 {
1335 if (s->pack)
1336 {
1337 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1338 }
1339 else
1340 Con_Printf ("%s\n", s->filename);
1341 }
1342}
1343
1344/*
1345============
1346COM_WriteFile
1347
1348The filename will be prefixed by the current game directory
1349============
1350*/
1351void COM_WriteFile (char *filename, void *data, int len)
1352{
1353 int handle;
1354 char name[MAX_OSPATH];
1355
1356 sprintf (name, "%s/%s", com_gamedir, filename);
1357
1358 handle = Sys_FileOpenWrite (name);
1359 if (handle == -1)
1360 {
1361 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1362 return;
1363 }
1364
1365 Sys_Printf ("COM_WriteFile: %s\n", name);
1366 Sys_FileWrite (handle, data, len);
1367 Sys_FileClose (handle);
1368}
1369
1370
1371/*
1372============
1373COM_CreatePath
1374
1375Only used for CopyFile
1376============
1377*/
1378void COM_CreatePath (char *path)
1379{
1380 char *ofs;
1381
1382 for (ofs = path+1 ; *ofs ; ofs++)
1383 {
1384 if (*ofs == '/')
1385 { // create the directory
1386 *ofs = 0;
1387 Sys_mkdir (path);
1388 *ofs = '/';
1389 }
1390 }
1391}
1392
1393
1394/*
1395===========
1396COM_CopyFile
1397
1398Copies a file over from the net to the local cache, creating any directories
1399needed. This is for the convenience of developers using ISDN from home.
1400===========
1401*/
1402void COM_CopyFile (char *netpath, char *cachepath)
1403{
1404 int in, out;
1405 int remaining, count;
1406 char buf[4096];
1407
1408 remaining = Sys_FileOpenRead (netpath, &in);
1409 COM_CreatePath (cachepath); // create directories up to the cache file
1410 out = Sys_FileOpenWrite (cachepath);
1411
1412 while (remaining)
1413 {
1414 if (remaining < sizeof(buf))
1415 count = remaining;
1416 else
1417 count = sizeof(buf);
1418 Sys_FileRead (in, buf, count);
1419 Sys_FileWrite (out, buf, count);
1420 remaining -= count;
1421 }
1422
1423 Sys_FileClose (in);
1424 Sys_FileClose (out);
1425}
1426
1427/*
1428===========
1429COM_FindFile
1430
1431Finds the file in the search path.
1432Sets com_filesize and one of handle or file
1433===========
1434*/
1435int COM_FindFile (char *filename, int *handle, FILE **file)
1436{
1437 searchpath_t *search;
1438 char netpath[MAX_OSPATH];
1439 char cachepath[MAX_OSPATH];
1440 pack_t *pak;
1441 int i;
1442 int findtime, cachetime;
1443
1444 if (file && handle)
1445 Sys_Error ("COM_FindFile: both handle and file set");
1446 if (!file && !handle)
1447 Sys_Error ("COM_FindFile: neither handle or file set");
1448
1449//
1450// search through the path, one element at a time
1451//
1452 search = com_searchpaths;
1453 if (proghack)
1454 { // gross hack to use quake 1 progs with quake 2 maps
1455 if (!strcmp(filename, "progs.dat"))
1456 search = search->next;
1457 }
1458
1459 for ( ; search ; search = search->next)
1460 {
1461 // is the element a pak file?
1462 if (search->pack)
1463 {
1464 // look through all the pak file elements
1465 pak = search->pack;
1466 for (i=0 ; i<pak->numfiles ; i++)
1467 if (!strcmp (pak->files[i].name, filename))
1468 { // found it!
1469 Con_DPrintf ("PackFile: %s : %s\n",pak->filename, filename);
1470 if (handle)
1471 {
1472 *handle = pak->handle;
1473 Sys_FileSeek (pak->handle, pak->files[i].filepos);
1474 //if(*handle != 1)
1475 //rb->splashf(HZ, "handle found: %d", *handle);
1476 }
1477 else
1478 { // open a new file on the pakfile
1479 *file = fopen (pak->filename, "rb");
1480 if (*file)
1481 fseek (*file, pak->files[i].filepos, SEEK_SET);
1482 else
1483 {
1484 Sys_Error("open failed: %d", 0);
1485 }
1486 }
1487 com_filesize = pak->files[i].filelen;
1488 return com_filesize;
1489 }
1490 }
1491 else
1492 {
1493 // check a file in the directory tree
1494 if (!static_registered)
1495 { // if not a registered version, don't ever go beyond base
1496 if ( strchr (filename, '/') || strchr (filename,'\\'))
1497 continue;
1498 }
1499
1500 sprintf (netpath, "%s/%s",search->filename, filename);
1501
1502 findtime = Sys_FileTime (netpath);
1503 if (findtime == -1)
1504 continue;
1505
1506 // see if the file needs to be updated in the cache
1507 if (!com_cachedir[0])
1508 strcpy (cachepath, netpath);
1509 else
1510 {
Franklin Wei5d05b9d2018-02-11 15:34:30 -05001511 sprintf (cachepath,"%s/%s", com_cachedir, netpath);
Franklin Wei5d05b9d2018-02-11 15:34:30 -05001512
1513 cachetime = Sys_FileTime (cachepath);
1514
1515 if (cachetime < findtime)
1516 COM_CopyFile (netpath, cachepath);
1517 strcpy (netpath, cachepath);
1518 }
1519
1520 Sys_Printf ("FindFile: %s\n",netpath);
1521 com_filesize = Sys_FileOpenRead (netpath, &i);
1522 if (handle)
1523 *handle = i;
1524 else
1525 {
1526 Sys_FileClose (i);
1527 *file = fopen (netpath, "rb");
1528 }
1529 return com_filesize;
1530 }
1531
1532 }
1533
1534 Sys_Printf ("FindFile: can't find %s\n", filename);
1535
1536 if (handle)
1537 *handle = -1;
1538 else
1539 *file = NULL;
1540 com_filesize = -1;
1541 return -1;
1542}
1543
1544
1545/*
1546===========
1547COM_OpenFile
1548
1549filename never has a leading slash, but may contain directory walks
1550returns a handle and a length
1551it may actually be inside a pak file
1552===========
1553*/
1554int COM_OpenFile (char *filename, int *handle)
1555{
1556 return COM_FindFile (filename, handle, NULL);
1557}
1558
1559/*
1560===========
1561COM_FOpenFile
1562
1563If the requested file is inside a packfile, a new FILE * will be opened
1564into the file.
1565===========
1566*/
1567int COM_FOpenFile (char *filename, FILE **file)
1568{
1569 return COM_FindFile (filename, NULL, file);
1570}
1571
1572/*
1573============
1574COM_CloseFile
1575
1576If it is a pak file handle, don't really close it
1577============
1578*/
1579void COM_CloseFile (int h)
1580{
1581 searchpath_t *s;
1582
1583 for (s = com_searchpaths ; s ; s=s->next)
1584 if (s->pack && s->pack->handle == h)
1585 return;
1586
1587 Sys_FileClose (h);
1588}
1589
1590
1591/*
1592============
1593COM_LoadFile
1594
1595Filename are reletive to the quake directory.
1596Allways appends a 0 byte.
1597============
1598*/
1599cache_user_t *loadcache;
1600byte *loadbuf;
1601int loadsize;
1602byte *COM_LoadFile (char *path, int usehunk)
1603{
1604 //printf("COM_LoadFile(%s)", path);
1605 int h;
1606 byte *buf;
1607 char base[32];
1608 int len;
1609
1610 buf = NULL; // quiet compiler warning
1611
1612// look for it in the filesystem or pack files
1613 len = COM_OpenFile (path, &h);
1614 if (h == -1)
1615 return NULL;
1616
1617 check_ptr = &h;
1618
1619 //printf("handle %d", h);
1620// extract the filename base name for hunk tag
1621 /* BUG IS HERE */
1622 COM_FileBase (path, base);
1623
1624 //printf("handle %d base \"%s\"", h, base);
1625 //printf("");
1626
1627 if (usehunk == 1)
1628 buf = Hunk_AllocName (len+1, base);
1629 else if (usehunk == 2)
1630 buf = Hunk_TempAlloc (len+1);
1631 else if (usehunk == 0)
1632 buf = Z_Malloc (len+1);
1633 else if (usehunk == 3)
1634 buf = Cache_Alloc (loadcache, len+1, base);
1635 else if (usehunk == 4)
1636 {
1637 if (len+1 > loadsize)
1638 buf = Hunk_TempAlloc (len+1);
1639 else
1640 buf = loadbuf;
1641 }
1642 else
1643 Sys_Error ("COM_LoadFile: bad usehunk");
1644
1645 if (!buf)
1646 Sys_Error ("COM_LoadFile: not enough space for %s", path);
1647
1648 ((byte *)buf)[len] = 0;
1649
1650 Draw_BeginDisc ();
1651 Sys_FileRead (h, buf, len);
1652 COM_CloseFile (h);
1653 Draw_EndDisc ();
1654
1655 return buf;
1656}
1657
1658byte *COM_LoadHunkFile (char *path)
1659{
1660 return COM_LoadFile (path, 1);
1661}
1662
1663byte *COM_LoadTempFile (char *path)
1664{
1665 return COM_LoadFile (path, 2);
1666}
1667
1668void COM_LoadCacheFile (char *path, struct cache_user_s *cu)
1669{
1670 loadcache = cu;
1671 COM_LoadFile (path, 3);
1672}
1673
1674// uses temp hunk if larger than bufsize
1675byte *COM_LoadStackFile (char *path, void *buffer, int bufsize)
1676{
1677 byte *buf;
1678
1679 loadbuf = (byte *)buffer;
1680 loadsize = bufsize;
1681 buf = COM_LoadFile (path, 4);
1682
1683 return buf;
1684}
1685
1686/*
1687=================
1688COM_LoadPackFile
1689
1690Takes an explicit (not game tree related) path to a pak file.
1691
1692Loads the header and directory, adding the files at the beginning
1693of the list so they override previous pack files.
1694=================
1695*/
1696pack_t *COM_LoadPackFile (char *packfile)
1697{
1698 dpackheader_t header;
1699 int i;
1700 packfile_t *newfiles;
1701 int numpackfiles;
1702 pack_t *pack;
1703 int packhandle;
1704 dpackfile_t info[MAX_FILES_IN_PACK];
1705 unsigned short crc;
1706
1707 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1708 {
1709// Con_Printf ("Couldn't open %s\n", packfile);
1710 return NULL;
1711 }
1712 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1713 if (header.id[0] != 'P' || header.id[1] != 'A'
1714 || header.id[2] != 'C' || header.id[3] != 'K')
1715 Sys_Error ("%s is not a packfile", packfile);
1716 header.dirofs = LittleLong (header.dirofs);
1717 header.dirlen = LittleLong (header.dirlen);
1718
1719 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1720
1721 if (numpackfiles > MAX_FILES_IN_PACK)
1722 Sys_Error ("%s has %i files", packfile, numpackfiles);
1723
1724 if (numpackfiles != PAK0_COUNT)
1725 com_modified = true; // not the original file
1726
1727 newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "packfile");
1728
1729 Sys_FileSeek (packhandle, header.dirofs);
1730 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1731
1732// crc the directory to check for modifications
1733 CRC_Init (&crc);
1734 for (i=0 ; i<header.dirlen ; i++)
1735 CRC_ProcessByte (&crc, ((byte *)info)[i]);
1736 if (crc != PAK0_CRC)
1737 com_modified = true;
1738
1739// parse the directory
1740 for (i=0 ; i<numpackfiles ; i++)
1741 {
1742 strcpy (newfiles[i].name, info[i].name);
1743 newfiles[i].filepos = LittleLong(info[i].filepos);
1744 newfiles[i].filelen = LittleLong(info[i].filelen);
1745 }
1746
1747 pack = Hunk_Alloc (sizeof (pack_t));
1748 strcpy (pack->filename, packfile);
1749 pack->handle = packhandle;
1750 pack->numfiles = numpackfiles;
1751 pack->files = newfiles;
1752
1753 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1754 return pack;
1755}
1756
1757
1758/*
1759================
1760COM_AddGameDirectory
1761
1762Sets com_gamedir, adds the directory to the head of the path,
1763then loads and adds pak1.pak pak2.pak ...
1764================
1765*/
1766void COM_AddGameDirectory (char *dir)
1767{
1768 int i;
1769 searchpath_t *search;
1770 pack_t *pak;
1771 char pakfile[MAX_OSPATH];
1772
1773 strcpy (com_gamedir, dir);
1774
1775//
1776// add the directory to the search path
1777//
1778 search = Hunk_Alloc (sizeof(searchpath_t));
1779 strcpy (search->filename, dir);
1780 search->next = com_searchpaths;
1781 com_searchpaths = search;
1782
1783//
1784// add any pak files in the format pak0.pak pak1.pak, ...
1785//
1786 for (i=0 ; ; i++)
1787 {
1788 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1789 pak = COM_LoadPackFile (pakfile);
1790 if (!pak)
1791 break;
1792 search = Hunk_Alloc (sizeof(searchpath_t));
1793 search->pack = pak;
1794 search->next = com_searchpaths;
1795 com_searchpaths = search;
1796 }
1797
1798//
1799// add the contents of the parms.txt file to the end of the command line
1800//
1801
1802}
1803
1804/*
1805================
1806COM_InitFilesystem
1807================
1808*/
1809void COM_InitFilesystem (void)
1810{
1811 int i, j;
1812 char basedir[MAX_OSPATH];
1813 searchpath_t *search;
1814
1815//
1816// -basedir <path>
1817// Overrides the system supplied base directory (under GAMENAME)
1818//
1819 i = COM_CheckParm ("-basedir");
1820 if (i && i < com_argc-1)
1821 strcpy (basedir, com_argv[i+1]);
1822 else
1823 strcpy (basedir, host_parms.basedir);
1824
1825 j = strlen (basedir);
1826
1827 if (j > 0)
1828 {
1829 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1830 basedir[j-1] = 0;
1831 }
1832
1833//
1834// -cachedir <path>
1835// Overrides the system supplied cache directory (NULL or /qcache)
1836// -cachedir - will disable caching.
1837//
1838 i = COM_CheckParm ("-cachedir");
1839 if (i && i < com_argc-1)
1840 {
1841 if (com_argv[i+1][0] == '-')
1842 com_cachedir[0] = 0;
1843 else
1844 strcpy (com_cachedir, com_argv[i+1]);
1845 }
1846 else if (host_parms.cachedir)
1847 strcpy (com_cachedir, host_parms.cachedir);
1848 else
1849 com_cachedir[0] = 0;
1850
1851//
1852// start up with GAMENAME by default (id1)
1853//
1854 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1855
1856 if (COM_CheckParm ("-rogue"))
1857 COM_AddGameDirectory (va("%s/rogue", basedir) );
1858 if (COM_CheckParm ("-hipnotic"))
1859 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1860
1861//
1862// -game <gamedir>
1863// Adds basedir/gamedir as an override game
1864//
1865 i = COM_CheckParm ("-game");
1866 if (i && i < com_argc-1)
1867 {
1868 com_modified = true;
1869 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1870 }
1871
1872//
1873// -path <dir or packfile> [<dir or packfile>] ...
1874// Fully specifies the exact serach path, overriding the generated one
1875//
1876 i = COM_CheckParm ("-path");
1877 if (i)
1878 {
1879 com_modified = true;
1880 com_searchpaths = NULL;
1881 while (++i < com_argc)
1882 {
1883 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1884 break;
1885
1886 search = Hunk_Alloc (sizeof(searchpath_t));
1887 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1888 {
1889 search->pack = COM_LoadPackFile (com_argv[i]);
1890 if (!search->pack)
1891 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1892 }
1893 else
1894 strcpy (search->filename, com_argv[i]);
1895 search->next = com_searchpaths;
1896 com_searchpaths = search;
1897 }
1898 }
1899
1900 if (COM_CheckParm ("-proghack"))
1901 proghack = true;
1902}
1903
1904