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