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