]> icculus.org git repositories - divverent/darkplaces.git/blob - common.c
fixed monster interpolation in normal quake, which makes dpcrush not lerped unfortunately
[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         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1248         static char string[8][1024], *s;
1249         static int stringindex = 0;
1250
1251         s = string[stringindex];
1252         stringindex = (stringindex + 1) & 7;
1253         va_start (argptr, format);
1254         vsprintf (s, format,argptr);
1255         va_end (argptr);
1256
1257         return s;
1258 }
1259
1260
1261 /// just for debugging
1262 int     memsearch (byte *start, int count, int search)
1263 {
1264         int             i;
1265
1266         for (i=0 ; i<count ; i++)
1267                 if (start[i] == search)
1268                         return i;
1269         return -1;
1270 }
1271
1272 /*
1273 =============================================================================
1274
1275 QUAKE FILESYSTEM
1276
1277 =============================================================================
1278 */
1279
1280 int     com_filesize;
1281
1282
1283 //
1284 // in memory
1285 //
1286
1287 typedef struct
1288 {
1289         char name[MAX_QPATH];
1290         int filepos, filelen;
1291 } packfile_t;
1292
1293 typedef struct pack_s
1294 {
1295         char filename[MAX_OSPATH];
1296         int handle;
1297         int numfiles;
1298         packfile_t *files;
1299         mempool_t *mempool;
1300         struct pack_s *next;
1301 } pack_t;
1302
1303 //
1304 // on disk
1305 //
1306 typedef struct
1307 {
1308         char name[56];
1309         int filepos, filelen;
1310 } dpackfile_t;
1311
1312 typedef struct
1313 {
1314         char id[4];
1315         int dirofs;
1316         int dirlen;
1317 } dpackheader_t;
1318
1319 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
1320 #define MAX_FILES_IN_PACK       65536
1321
1322 pack_t  *packlist = NULL;
1323
1324 #if CACHEENABLE
1325 char    com_cachedir[MAX_OSPATH];
1326 #endif
1327 char    com_gamedir[MAX_OSPATH];
1328
1329 typedef struct searchpath_s
1330 {
1331         char filename[MAX_OSPATH];
1332         pack_t *pack;          // only one of filename / pack will be used
1333         struct searchpath_s *next;
1334 } searchpath_t;
1335
1336 searchpath_t    *com_searchpaths;
1337
1338 /*
1339 ============
1340 COM_Path_f
1341
1342 ============
1343 */
1344 void COM_Path_f (void)
1345 {
1346         searchpath_t    *s;
1347
1348         Con_Printf ("Current search path:\n");
1349         for (s=com_searchpaths ; s ; s=s->next)
1350         {
1351                 if (s->pack)
1352                 {
1353                         Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1354                 }
1355                 else
1356                         Con_Printf ("%s\n", s->filename);
1357         }
1358 }
1359
1360 /*
1361 ============
1362 COM_CreatePath
1363
1364 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1365 ============
1366 */
1367 void    COM_CreatePath (char *path)
1368 {
1369         char    *ofs, save;
1370
1371         for (ofs = path+1 ; *ofs ; ofs++)
1372         {
1373                 if (*ofs == '/' || *ofs == '\\' || *ofs == ':')
1374                 {       // create the directory
1375                         save = *ofs;
1376                         *ofs = 0;
1377                         Sys_mkdir (path);
1378                         *ofs = save;
1379                 }
1380         }
1381 }
1382
1383
1384 /*
1385 ============
1386 COM_WriteFile
1387
1388 The filename will be prefixed by the current game directory
1389 ============
1390 */
1391 void COM_WriteFile (char *filename, void *data, int len)
1392 {
1393         int             handle;
1394         char    name[MAX_OSPATH];
1395
1396         sprintf (name, "%s/%s", com_gamedir, filename);
1397
1398         // LordHavoc: added this
1399         COM_CreatePath (name); // create directories up to the file
1400
1401         handle = Sys_FileOpenWrite (name);
1402         if (handle == -1)
1403         {
1404                 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1405                 return;
1406         }
1407
1408         Con_Printf ("COM_WriteFile: %s\n", name);
1409         Sys_FileWrite (handle, data, len);
1410         Sys_FileClose (handle);
1411 }
1412
1413
1414 /*
1415 ===========
1416 COM_CopyFile
1417
1418 Copies a file over from the net to the local cache, creating any directories
1419 needed.  This is for the convenience of developers using ISDN from home.
1420 ===========
1421 */
1422 void COM_CopyFile (char *netpath, char *cachepath)
1423 {
1424         int             in, out;
1425         int             remaining, count;
1426         char    buf[4096];
1427
1428         remaining = Sys_FileOpenRead (netpath, &in);            
1429         COM_CreatePath (cachepath);     // create directories up to the cache file
1430         out = Sys_FileOpenWrite (cachepath);
1431         
1432         while (remaining)
1433         {
1434                 if (remaining < sizeof(buf))
1435                         count = remaining;
1436                 else
1437                         count = sizeof(buf);
1438                 Sys_FileRead (in, buf, count);
1439                 Sys_FileWrite (out, buf, count);
1440                 remaining -= count;
1441         }
1442
1443         Sys_FileClose (in);
1444         Sys_FileClose (out);    
1445 }
1446
1447 /*
1448 ===========
1449 COM_OpenRead
1450 ===========
1451 */
1452 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1453 {
1454         int                             fd = open (path, O_RDONLY);
1455         unsigned char   id[2];
1456         unsigned char   len_bytes[4];
1457
1458         if (fd == -1)
1459         {
1460                 Sys_Error ("Couldn't open %s", path);
1461                 return 0;
1462         }
1463         if (offs < 0 || len < 0)
1464         {
1465                 // normal file
1466                 offs = 0;
1467                 len = lseek (fd, 0, SEEK_END);
1468                 lseek (fd, 0, SEEK_SET);
1469         }
1470         lseek (fd, offs, SEEK_SET);
1471         if (zip)
1472         {
1473                 read (fd, id, 2);
1474                 if (id[0] == 0x1f && id[1] == 0x8b)
1475                 {
1476                         lseek (fd, offs + len - 4, SEEK_SET);
1477                         read (fd, len_bytes, 4);
1478                         len = ((len_bytes[3] << 24)
1479                                    | (len_bytes[2] << 16)
1480                                    | (len_bytes[1] << 8)
1481                                    | (len_bytes[0]));
1482                 }
1483         }
1484         lseek (fd, offs, SEEK_SET);
1485         com_filesize = len;
1486
1487 #ifdef WIN32
1488         setmode (fd, O_BINARY);
1489 #endif
1490         if (zip)
1491                 return Qdopen (fd, "rbz");
1492         else
1493                 return Qdopen (fd, "rb");
1494 }
1495
1496 /*
1497 ===========
1498 COM_FindFile
1499
1500 Finds the file in the search path.
1501 Sets com_filesize and one of handle or file
1502 ===========
1503 */
1504 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1505 {
1506         searchpath_t    *search;
1507         char                    netpath[MAX_OSPATH];
1508 #if CACHEENABLE
1509         char                    cachepath[MAX_OSPATH];
1510         int                             cachetime;
1511 #endif
1512         pack_t                  *pak;
1513         int                             i;
1514         int                             findtime;
1515         char                    gzfilename[MAX_OSPATH];
1516         int                             filenamelen;
1517
1518         filenamelen = strlen (filename);
1519         sprintf (gzfilename, "%s.gz", filename);
1520
1521         if (!file)
1522                 Sys_Error ("COM_FindFile: file not set");
1523                 
1524 //
1525 // search through the path, one element at a time
1526 //
1527         search = com_searchpaths;
1528 //      if (proghack)
1529 //      {       // gross hack to use quake 1 progs with quake 2 maps
1530 //              if (!strcmp(filename, "progs.dat"))
1531 //                      search = search->next;
1532 //      }
1533
1534         for ( ; search ; search = search->next)
1535         {
1536         // is the element a pak file?
1537                 if (search->pack)
1538                 {
1539                 // look through all the pak file elements
1540                         pak = search->pack;
1541                         for (i=0 ; i<pak->numfiles ; i++)
1542                                 if (!strcmp (pak->files[i].name, filename)
1543                                     || !strcmp (pak->files[i].name, gzfilename))
1544                                 {       // found it!
1545                                         if (!quiet)
1546                                                 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1547                                         // open a new file on the pakfile
1548                                         *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1549                                         return com_filesize;
1550                                 }
1551                 }
1552                 else
1553                 {               
1554         // check a file in the directory tree
1555 //                      if (!static_registered)
1556 //                      {       // if not a registered version, don't ever go beyond base
1557 //                              if ( strchr (filename, '/') || strchr (filename,'\\'))
1558 //                                      continue;
1559 //                      }
1560                         
1561                         sprintf (netpath, "%s/%s",search->filename, filename);
1562                         
1563                         findtime = Sys_FileTime (netpath);
1564                         if (findtime == -1)
1565                                 continue;
1566                                 
1567 #if CACHEENABLE
1568                         // see if the file needs to be updated in the cache
1569                         if (com_cachedir[0])
1570                         {       
1571 #if defined(_WIN32)
1572                                 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1573                                         sprintf (cachepath,"%s%s", com_cachedir, netpath);
1574                                 else
1575                                         sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1576 #else
1577                                 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1578 #endif
1579
1580                                 cachetime = Sys_FileTime (cachepath);
1581                         
1582                                 if (cachetime < findtime)
1583                                         COM_CopyFile (netpath, cachepath);
1584                                 strcpy (netpath, cachepath);
1585                         }       
1586 #endif
1587
1588                         if (!quiet)
1589                                 Sys_Printf ("FindFile: %s\n",netpath);
1590                         *file = COM_OpenRead (netpath, -1, -1, zip);
1591                         return com_filesize;
1592                 }
1593                 
1594         }
1595         
1596         if (!quiet)
1597                 Sys_Printf ("FindFile: can't find %s\n", filename);
1598         
1599         *file = NULL;
1600         com_filesize = -1;
1601         return -1;
1602 }
1603
1604
1605 /*
1606 ===========
1607 COM_FOpenFile
1608
1609 If the requested file is inside a packfile, a new QFile * will be opened
1610 into the file.
1611 ===========
1612 */
1613 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1614 {
1615         return COM_FindFile (filename, file, quiet, zip);
1616 }
1617
1618
1619 /*
1620 ============
1621 COM_LoadFile
1622
1623 Filename are reletive to the quake directory.
1624 Always appends a 0 byte.
1625 ============
1626 */
1627 byte                    *loadbuf;
1628 int                             loadsize;
1629 byte *COM_LoadFile (char *path, qboolean quiet)
1630 {
1631         QFile             *h;
1632         byte    *buf;
1633         char    base[1024];
1634         int             len;
1635
1636         buf = NULL;     // quiet compiler warning
1637         loadsize = 0;
1638
1639 // look for it in the filesystem or pack files
1640         len = COM_FOpenFile (path, &h, quiet, true);
1641         if (!h)
1642                 return NULL;
1643
1644         loadsize = len;
1645
1646 // extract the filename base name for hunk tag
1647         COM_FileBase (path, base);
1648
1649         buf = Mem_Alloc(tempmempool, len+1);
1650         if (!buf)
1651                 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1652
1653         ((byte *)buf)[len] = 0;
1654
1655         Qread (h, buf, len);
1656         Qclose (h);
1657
1658         return buf;
1659 }
1660
1661 /*
1662 =================
1663 COM_LoadPackFile
1664
1665 Takes an explicit (not game tree related) path to a pak file.
1666
1667 Loads the header and directory, adding the files at the beginning
1668 of the list so they override previous pack files.
1669 =================
1670 */
1671 pack_t *COM_LoadPackFile (char *packfile)
1672 {
1673         dpackheader_t   header;
1674         int                             i;
1675         int                             numpackfiles;
1676         pack_t                  *pack;
1677         int                             packhandle;
1678         // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1679         dpackfile_t             *info;
1680
1681         if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1682         {
1683                 //Con_Printf ("Couldn't open %s\n", packfile);
1684                 return NULL;
1685         }
1686         Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1687         if (memcmp(header.id, "PACK", 4))
1688                 Sys_Error ("%s is not a packfile", packfile);
1689         header.dirofs = LittleLong (header.dirofs);
1690         header.dirlen = LittleLong (header.dirlen);
1691
1692         if (header.dirlen % sizeof(dpackfile_t))
1693                 Sys_Error ("%s has an invalid directory size", packfile);
1694
1695         numpackfiles = header.dirlen / sizeof(dpackfile_t);
1696
1697         if (numpackfiles > MAX_FILES_IN_PACK)
1698                 Sys_Error ("%s has %i files", packfile, numpackfiles);
1699
1700         pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1701         strcpy (pack->filename, packfile);
1702         pack->handle = packhandle;
1703         pack->numfiles = numpackfiles;
1704         pack->mempool = Mem_AllocPool(packfile);
1705         pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1706         pack->next = packlist;
1707         packlist = pack;
1708
1709         info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1710         Sys_FileSeek (packhandle, header.dirofs);
1711         Sys_FileRead (packhandle, (void *)info, header.dirlen);
1712
1713 // parse the directory
1714         for (i = 0;i < numpackfiles;i++)
1715         {
1716                 strcpy (pack->files[i].name, info[i].name);
1717                 pack->files[i].filepos = LittleLong(info[i].filepos);
1718                 pack->files[i].filelen = LittleLong(info[i].filelen);
1719         }
1720
1721         Mem_Free(info);
1722
1723         Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1724         return pack;
1725 }
1726
1727
1728 /*
1729 ================
1730 COM_AddGameDirectory
1731
1732 Sets com_gamedir, adds the directory to the head of the path,
1733 then loads and adds pak1.pak pak2.pak ...
1734 ================
1735 */
1736 void COM_AddGameDirectory (char *dir)
1737 {
1738         int                             i;
1739         searchpath_t    *search;
1740         pack_t                  *pak;
1741         char                    pakfile[MAX_OSPATH];
1742
1743         strcpy (com_gamedir, dir);
1744
1745 //
1746 // add the directory to the search path
1747 //
1748         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1749         strcpy (search->filename, dir);
1750         search->next = com_searchpaths;
1751         com_searchpaths = search;
1752
1753 //
1754 // add any pak files in the format pak0.pak pak1.pak, ...
1755 //
1756         for (i=0 ; ; i++)
1757         {
1758                 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1759                 pak = COM_LoadPackFile (pakfile);
1760                 if (!pak)
1761                         break;
1762                 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1763                 search->pack = pak;
1764                 search->next = com_searchpaths;
1765                 com_searchpaths = search;
1766         }
1767
1768 //
1769 // add the contents of the parms.txt file to the end of the command line
1770 //
1771
1772 }
1773
1774 /*
1775 ================
1776 COM_InitFilesystem
1777 ================
1778 */
1779 void COM_InitFilesystem (void)
1780 {
1781         int             i, j;
1782         char    basedir[MAX_OSPATH];
1783         searchpath_t    *search;
1784
1785 //
1786 // -basedir <path>
1787 // Overrides the system supplied base directory (under GAMENAME)
1788 //
1789         i = COM_CheckParm ("-basedir");
1790         if (i && i < com_argc-1)
1791                 strcpy (basedir, com_argv[i+1]);
1792         else
1793                 strcpy (basedir, host_parms.basedir);
1794
1795         j = strlen (basedir);
1796
1797         if (j > 0)
1798         {
1799                 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1800                         basedir[j-1] = 0;
1801         }
1802
1803 #if CACHEENABLE
1804 //
1805 // -cachedir <path>
1806 // Overrides the system supplied cache directory (NULL or /qcache)
1807 // -cachedir - will disable caching.
1808 //
1809         i = COM_CheckParm ("-cachedir");
1810         if (i && i < com_argc-1)
1811         {
1812                 if (com_argv[i+1][0] == '-')
1813                         com_cachedir[0] = 0;
1814                 else
1815                         strcpy (com_cachedir, com_argv[i+1]);
1816         }
1817         else if (host_parms.cachedir)
1818                 strcpy (com_cachedir, host_parms.cachedir);
1819         else
1820                 com_cachedir[0] = 0;
1821 #endif
1822
1823 // start up with GAMENAME by default (id1)
1824         COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1825
1826         switch(gamemode)
1827         {
1828         case GAME_NORMAL:
1829                 break;
1830         case GAME_HIPNOTIC:
1831                 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1832                 break;
1833         case GAME_ROGUE:
1834                 COM_AddGameDirectory (va("%s/rogue", basedir) );
1835                 break;
1836         case GAME_NEHAHRA:
1837                 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1838                 break;
1839         case GAME_FIENDARENA:
1840                 COM_AddGameDirectory (va("%s/fiendarena", basedir) );
1841                 break;
1842         case GAME_ZYMOTIC:
1843                 COM_AddGameDirectory (va("%s/zymotic", basedir) );
1844                 break;
1845         default:
1846                 Sys_Error("COM_InitFilesystem: unknown gamemode %i\n", gamemode);
1847                 break;
1848         }
1849
1850 //
1851 // -game <gamedir>
1852 // Adds basedir/gamedir as an override game
1853 //
1854         i = COM_CheckParm ("-game");
1855         if (i && i < com_argc-1)
1856         {
1857                 com_modified = true;
1858                 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1859         }
1860
1861 //
1862 // -path <dir or packfile> [<dir or packfile>] ...
1863 // Fully specifies the exact search path, overriding the generated one
1864 //
1865         i = COM_CheckParm ("-path");
1866         if (i)
1867         {
1868                 com_modified = true;
1869                 com_searchpaths = NULL;
1870                 while (++i < com_argc)
1871                 {
1872                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1873                                 break;
1874
1875                         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1876                         if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1877                         {
1878                                 search->pack = COM_LoadPackFile (com_argv[i]);
1879                                 if (!search->pack)
1880                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1881                         }
1882                         else
1883                                 strcpy (search->filename, com_argv[i]);
1884                         search->next = com_searchpaths;
1885                         com_searchpaths = search;
1886                 }
1887         }
1888
1889 //      if (COM_CheckParm ("-proghack"))
1890 //              proghack = true;
1891 }
1892
1893 int COM_FileExists(char *filename)
1894 {
1895         searchpath_t    *search;
1896         char                    netpath[MAX_OSPATH];
1897         pack_t                  *pak;
1898         int                             i;
1899         int                             findtime;
1900
1901         for (search = com_searchpaths;search;search = search->next)
1902         {
1903                 if (search->pack)
1904                 {
1905                         pak = search->pack;
1906                         for (i = 0;i < pak->numfiles;i++)
1907                                 if (!strcmp (pak->files[i].name, filename))
1908                                         return true;
1909                 }
1910                 else
1911                 {
1912                         sprintf (netpath, "%s/%s",search->filename, filename);
1913                         findtime = Sys_FileTime (netpath);
1914                         if (findtime != -1)
1915                                 return true;
1916                 }               
1917         }
1918
1919         return false;
1920 }
1921
1922
1923 //======================================
1924 // LordHavoc: added these because they are useful
1925
1926 void COM_ToLowerString(char *in, char *out)
1927 {
1928         while (*in)
1929         {
1930                 if (*in >= 'A' && *in <= 'Z')
1931                         *out++ = *in++ + 'a' - 'A';
1932                 else
1933                         *out++ = *in++;
1934         }
1935 }
1936
1937 void COM_ToUpperString(char *in, char *out)
1938 {
1939         while (*in)
1940         {
1941                 if (*in >= 'a' && *in <= 'z')
1942                         *out++ = *in++ + 'A' - 'a';
1943                 else
1944                         *out++ = *in++;
1945         }
1946 }