]> icculus.org git repositories - divverent/darkplaces.git/blob - common.c
changed protocol back to using float coordinates, unless the mod specifies EF_LOWPREC...
[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                         ((qbyte *)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                         ((qbyte *)dest)[i] = ((qbyte *)src)[i];
128 }
129
130 int Q_memcmp (void *m1, void *m2, int count)
131 {
132         while(count)
133         {
134                 count--;
135                 if (((qbyte *)m1)[count] != ((qbyte *)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         qbyte    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         qbyte    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                 qbyte    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         qbyte    *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         qbyte    *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         qbyte    *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         qbyte    *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 latest dpprotocol)
553 void MSG_WriteDPCoord (sizebuf_t *sb, float f)
554 {
555         if (f >= 0)
556                 MSG_WriteShort (sb, (int)(f + 0.5f));
557         else
558                 MSG_WriteShort (sb, (int)(f - 0.5f));
559 }
560
561 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
562 {
563         if (f >= 0)
564                 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
565         else
566                 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
567 }
568
569 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
570 void MSG_WriteAngle (sizebuf_t *sb, float f)
571 {
572         if (f >= 0)
573                 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
574         else
575                 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
576 }
577
578 //
579 // reading functions
580 //
581 int                     msg_readcount;
582 qboolean        msg_badread;
583
584 void MSG_BeginReading (void)
585 {
586         msg_readcount = 0;
587         msg_badread = false;
588 }
589
590 /*
591 // returns -1 and sets msg_badread if no more characters are available
592 int MSG_ReadChar (void)
593 {
594         int     c;
595
596         // LordHavoc: minor optimization
597         if (msg_readcount >= net_message.cursize)
598 //      if (msg_readcount+1 > net_message.cursize)
599         {
600                 msg_badread = true;
601                 return -1;
602         }
603
604         c = (signed char)net_message.data[msg_readcount];
605         msg_readcount++;
606
607         return c;
608 }
609
610 int MSG_ReadByte (void)
611 {
612         int     c;
613
614         // LordHavoc: minor optimization
615         if (msg_readcount >= net_message.cursize)
616 //      if (msg_readcount+1 > net_message.cursize)
617         {
618                 msg_badread = true;
619                 return -1;
620         }
621
622         c = (unsigned char)net_message.data[msg_readcount];
623         msg_readcount++;
624
625         return c;
626 }
627 */
628
629 int MSG_ReadShort (void)
630 {
631         int     c;
632
633         if (msg_readcount+2 > net_message.cursize)
634         {
635                 msg_badread = true;
636                 return -1;
637         }
638
639         c = (short)(net_message.data[msg_readcount]
640         + (net_message.data[msg_readcount+1]<<8));
641
642         msg_readcount += 2;
643
644         return c;
645 }
646
647 int MSG_ReadLong (void)
648 {
649         int     c;
650
651         if (msg_readcount+4 > net_message.cursize)
652         {
653                 msg_badread = true;
654                 return -1;
655         }
656
657         c = net_message.data[msg_readcount]
658         + (net_message.data[msg_readcount+1]<<8)
659         + (net_message.data[msg_readcount+2]<<16)
660         + (net_message.data[msg_readcount+3]<<24);
661
662         msg_readcount += 4;
663
664         return c;
665 }
666
667 float MSG_ReadFloat (void)
668 {
669         union
670         {
671                 qbyte    b[4];
672                 float   f;
673                 int     l;
674         } dat;
675
676         dat.b[0] =      net_message.data[msg_readcount];
677         dat.b[1] =      net_message.data[msg_readcount+1];
678         dat.b[2] =      net_message.data[msg_readcount+2];
679         dat.b[3] =      net_message.data[msg_readcount+3];
680         msg_readcount += 4;
681
682         dat.l = LittleLong (dat.l);
683
684         return dat.f;
685 }
686
687 char *MSG_ReadString (void)
688 {
689         static char     string[2048];
690         int             l,c;
691
692         l = 0;
693         do
694         {
695                 c = MSG_ReadChar ();
696                 if (c == -1 || c == 0)
697                         break;
698                 string[l] = c;
699                 l++;
700         } while (l < sizeof(string)-1);
701
702         string[l] = 0;
703
704         return string;
705 }
706
707 // used by server (always latest dpprotocol)
708 float MSG_ReadDPCoord (void)
709 {
710         return (signed short) MSG_ReadShort();
711 }
712
713 // used by client
714 float MSG_ReadCoord (void)
715 {
716         if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
717                 return (signed short) MSG_ReadShort();
718         else if (dpprotocol == DPPROTOCOL_VERSION1)
719                 return MSG_ReadFloat();
720         else
721                 return MSG_ReadShort() * (1.0f/8.0f);
722 }
723
724 /*
725 float MSG_ReadCoord (void)
726 {
727         return MSG_ReadShort() * (1.0f/8.0f);
728 }
729
730 float MSG_ReadAngle (void)
731 {
732         return MSG_ReadChar() * (360.0f/256.0f);
733 }
734
735 float MSG_ReadPreciseAngle (void)
736 {
737         return MSG_ReadShort() * (360.0f/65536);
738 }
739 */
740
741
742 //===========================================================================
743
744 void SZ_Alloc (sizebuf_t *buf, int startsize, char *name)
745 {
746         if (startsize < 256)
747                 startsize = 256;
748         buf->mempool = Mem_AllocPool(name);
749         buf->data = Mem_Alloc(buf->mempool, startsize);
750         buf->maxsize = startsize;
751         buf->cursize = 0;
752 }
753
754
755 void SZ_Free (sizebuf_t *buf)
756 {
757         Mem_FreePool(&buf->mempool);
758         buf->data = NULL;
759         buf->maxsize = 0;
760         buf->cursize = 0;
761 }
762
763 void SZ_Clear (sizebuf_t *buf)
764 {
765         buf->cursize = 0;
766 }
767
768 void *SZ_GetSpace (sizebuf_t *buf, int length)
769 {
770         void    *data;
771
772         if (buf->cursize + length > buf->maxsize)
773         {
774                 if (!buf->allowoverflow)
775                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
776
777                 if (length > buf->maxsize)
778                         Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
779
780                 buf->overflowed = true;
781                 Con_Printf ("SZ_GetSpace: overflow");
782                 SZ_Clear (buf);
783         }
784
785         data = buf->data + buf->cursize;
786         buf->cursize += length;
787         
788         return data;
789 }
790
791 void SZ_Write (sizebuf_t *buf, void *data, int length)
792 {
793         memcpy (SZ_GetSpace(buf,length),data,length);         
794 }
795
796 void SZ_Print (sizebuf_t *buf, char *data)
797 {
798         int             len;
799         
800         len = strlen(data)+1;
801
802 // byte * cast to keep VC++ happy
803         if (buf->data[buf->cursize-1])
804                 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
805         else
806                 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
807 }
808
809
810 //============================================================================
811
812
813 /*
814 ============
815 COM_SkipPath
816 ============
817 */
818 char *COM_SkipPath (char *pathname)
819 {
820         char    *last;
821
822         last = pathname;
823         while (*pathname)
824         {
825                 if (*pathname=='/')
826                         last = pathname+1;
827                 pathname++;
828         }
829         return last;
830 }
831
832 /*
833 ============
834 COM_StripExtension
835 ============
836 */
837 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
838 void COM_StripExtension (char *in, char *out)
839 {
840         char *last = NULL;
841         while (*in)
842         {
843                 if (*in == '.')
844                         last = in;
845                 if ((*in == '/') || (*in == '\\') || (*in == ':'))
846                         last = NULL;
847                 *out++ = *in++;
848         }
849         if (last)
850                 *last = 0;
851 }
852
853 /*
854 ============
855 COM_FileExtension
856 ============
857 */
858 char *COM_FileExtension (char *in)
859 {
860         static char exten[8];
861         int             i;
862
863         while (*in && *in != '.')
864                 in++;
865         if (!*in)
866                 return "";
867         in++;
868         for (i=0 ; i<7 && *in ; i++,in++)
869                 exten[i] = *in;
870         exten[i] = 0;
871         return exten;
872 }
873
874 /*
875 ============
876 COM_FileBase
877 ============
878 */
879 void COM_FileBase (char *in, char *out)
880 {
881         char *slash, *dot;
882         char *s;
883
884         slash = in;
885         dot = NULL;
886         s = in;
887         while(*s)
888         {
889                 if (*s == '/')
890                         slash = s + 1;
891                 if (*s == '.')
892                         dot = s;
893                 s++;
894         }
895         if (dot == NULL)
896                 dot = s;
897         if (dot - slash < 2)
898                 strcpy (out,"?model?");
899         else
900         {
901                 while (slash < dot)
902                         *out++ = *slash++;
903                 *out++ = 0;
904         }
905 }
906
907
908 /*
909 ==================
910 COM_DefaultExtension
911 ==================
912 */
913 void COM_DefaultExtension (char *path, char *extension)
914 {
915         char    *src;
916 //
917 // if path doesn't have a .EXT, append extension
918 // (extension should include the .)
919 //
920         src = path + strlen(path) - 1;
921
922         while (*src != '/' && src != path)
923         {
924                 if (*src == '.')
925                         return;                 // it has an extension
926                 src--;
927         }
928
929         strcat (path, extension);
930 }
931
932
933 /*
934 ==============
935 COM_Parse
936
937 Parse a token out of a string
938 ==============
939 */
940 char *COM_Parse (char *data)
941 {
942         int             c;
943         int             len;
944         
945         len = 0;
946         com_token[0] = 0;
947         
948         if (!data)
949                 return NULL;
950                 
951 // skip whitespace
952 skipwhite:
953         while ( (c = *data) <= ' ')
954         {
955                 if (c == 0)
956                         return NULL;                    // end of file;
957                 data++;
958         }
959         
960 // skip // comments
961         if (c=='/' && data[1] == '/')
962         {
963                 while (*data && *data != '\n')
964                         data++;
965                 goto skipwhite;
966         }
967         
968
969 // handle quoted strings specially
970         if (c == '\"')
971         {
972                 data++;
973                 while (1)
974                 {
975                         c = *data++;
976                         if (c=='\"' || !c)
977                         {
978                                 com_token[len] = 0;
979                                 return data;
980                         }
981                         com_token[len] = c;
982                         len++;
983                 }
984         }
985
986 // parse single characters
987         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
988         {
989                 com_token[len] = c;
990                 len++;
991                 com_token[len] = 0;
992                 return data+1;
993         }
994
995 // parse a regular word
996         do
997         {
998                 com_token[len] = c;
999                 data++;
1000                 len++;
1001                 c = *data;
1002         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1003                         break;
1004         } while (c>32);
1005         
1006         com_token[len] = 0;
1007         return data;
1008 }
1009
1010
1011 /*
1012 ================
1013 COM_CheckParm
1014
1015 Returns the position (1 to argc-1) in the program's argument list
1016 where the given parameter apears, or 0 if not present
1017 ================
1018 */
1019 int COM_CheckParm (char *parm)
1020 {
1021         int             i;
1022         
1023         for (i=1 ; i<com_argc ; i++)
1024         {
1025                 if (!com_argv[i])
1026                         continue;               // NEXTSTEP sometimes clears appkit vars.
1027                 if (!strcmp (parm,com_argv[i]))
1028                         return i;
1029         }
1030                 
1031         return 0;
1032 }
1033
1034 /*
1035 ================
1036 COM_CheckRegistered
1037
1038 Looks for the pop.txt file and verifies it.
1039 Sets the "registered" cvar.
1040 Immediately exits out if an alternate game was attempted to be started without
1041 being registered.
1042 ================
1043 */
1044 void COM_CheckRegistered (void)
1045 {
1046         Cvar_Set ("cmdline", com_cmdline);
1047
1048 //      static_registered = 0;
1049
1050         if (!Sys_FileTime("gfx/pop.lmp"))
1051         {
1052                 if (com_modified)
1053                         Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1054                 else
1055                         Con_Printf ("Playing shareware version.\n");
1056 //#if WINDED
1057 //      Sys_Error ("This dedicated server requires a full registered copy of Quake");
1058 //#endif
1059 //              Con_Printf ("Playing shareware version.\n");
1060 //              if (com_modified)
1061 //                      Sys_Error ("You must have the registered version to use modified games");
1062                 return;
1063         }
1064
1065 //      Cvar_Set ("cmdline", com_cmdline);
1066         Cvar_Set ("registered", "1");
1067 //      static_registered = 1;
1068         Con_Printf ("Playing registered version.\n");
1069 }
1070
1071
1072 void COM_Path_f (void);
1073
1074
1075 /*
1076 ================
1077 COM_InitArgv
1078 ================
1079 */
1080 void COM_InitArgv (int argc, char **argv)
1081 {
1082         qboolean        safe;
1083         int             i, j, n;
1084
1085 // reconstitute the command line for the cmdline externally visible cvar
1086         n = 0;
1087
1088         for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1089         {
1090                 i = 0;
1091
1092                 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1093                 {
1094                         com_cmdline[n++] = argv[j][i++];
1095                 }
1096
1097                 if (n < (CMDLINE_LENGTH - 1))
1098                         com_cmdline[n++] = ' ';
1099                 else
1100                         break;
1101         }
1102
1103         com_cmdline[n] = 0;
1104
1105         safe = false;
1106
1107         for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1108                  com_argc++)
1109         {
1110                 largv[com_argc] = argv[com_argc];
1111                 if (!strcmp ("-safe", argv[com_argc]))
1112                         safe = true;
1113         }
1114
1115         if (safe)
1116         {
1117         // force all the safe-mode switches. Note that we reserved extra space in
1118         // case we need to add these, so we don't need an overflow check
1119                 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1120                 {
1121                         largv[com_argc] = safeargvs[i];
1122                         com_argc++;
1123                 }
1124         }
1125
1126         largv[com_argc] = argvdummy;
1127         com_argv = largv;
1128
1129 #if ZYMOTIC
1130         gamemode = GAME_ZYMOTIC;
1131 #elif FIENDARENA
1132         gamemode = GAME_FIENDARENA;
1133 #elif NEHAHRA
1134         gamemode = GAME_NEHAHRA;
1135 #else
1136         if (COM_CheckParm ("-zymotic"))
1137                 gamemode = GAME_ZYMOTIC;
1138         else if (COM_CheckParm ("-fiendarena"))
1139                 gamemode = GAME_FIENDARENA;
1140         else if (COM_CheckParm ("-nehahra"))
1141                 gamemode = GAME_NEHAHRA;
1142         else if (COM_CheckParm ("-hipnotic"))
1143                 gamemode = GAME_HIPNOTIC;
1144         else if (COM_CheckParm ("-rogue"))
1145                 gamemode = GAME_ROGUE;
1146 #endif
1147         switch(gamemode)
1148         {
1149         case GAME_NORMAL:
1150                 gamename = "DarkPlaces";
1151                 break;
1152         case GAME_HIPNOTIC:
1153                 gamename = "Darkplaces-Hipnotic";
1154                 break;
1155         case GAME_ROGUE:
1156                 gamename = "Darkplaces-Rogue";
1157                 break;
1158         case GAME_NEHAHRA:
1159                 gamename = "DarkPlaces-Nehahra";
1160                 break;
1161         case GAME_FIENDARENA:
1162                 gamename = "FiendArena";
1163                 break;
1164         case GAME_ZYMOTIC:
1165                 gamename = "Zymotic";
1166                 break;
1167         default:
1168                 Sys_Error("COM_InitArgv: unknown gamemode %i\n", gamemode);
1169                 break;
1170         }
1171 }
1172
1173
1174 extern void Mathlib_Init(void);
1175
1176 /*
1177 ================
1178 COM_Init
1179 ================
1180 */
1181 void COM_Init (void)
1182 {
1183 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
1184         qbyte    swaptest[2] = {1,0};
1185
1186 // set the byte swapping variables in a portable manner
1187         if ( *(short *)swaptest == 1)
1188         {
1189                 BigShort = ShortSwap;
1190                 LittleShort = ShortNoSwap;
1191                 BigLong = LongSwap;
1192                 LittleLong = LongNoSwap;
1193                 BigFloat = FloatSwap;
1194                 LittleFloat = FloatNoSwap;
1195         }
1196         else
1197         {
1198                 BigShort = ShortNoSwap;
1199                 LittleShort = ShortSwap;
1200                 BigLong = LongNoSwap;
1201                 LittleLong = LongSwap;
1202                 BigFloat = FloatNoSwap;
1203                 LittleFloat = FloatSwap;
1204         }
1205 #endif
1206
1207         pak_mempool = Mem_AllocPool("paks");
1208
1209         Cvar_RegisterVariable (&registered);
1210         Cvar_RegisterVariable (&cmdline);
1211         Cmd_AddCommand ("path", COM_Path_f);
1212
1213         Mathlib_Init();
1214
1215         COM_InitFilesystem ();
1216         COM_CheckRegistered ();
1217 }
1218
1219
1220 /*
1221 ============
1222 va
1223
1224 does a varargs printf into a temp buffer, so I don't need to have
1225 varargs versions of all text functions.
1226 FIXME: make this buffer size safe someday
1227 ============
1228 */
1229 char    *va(char *format, ...)
1230 {
1231         va_list argptr;
1232         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1233         static char string[8][1024], *s;
1234         static int stringindex = 0;
1235
1236         s = string[stringindex];
1237         stringindex = (stringindex + 1) & 7;
1238         va_start (argptr, format);
1239         vsprintf (s, format,argptr);
1240         va_end (argptr);
1241
1242         return s;
1243 }
1244
1245
1246 /// just for debugging
1247 int     memsearch (qbyte *start, int count, int search)
1248 {
1249         int             i;
1250
1251         for (i=0 ; i<count ; i++)
1252                 if (start[i] == search)
1253                         return i;
1254         return -1;
1255 }
1256
1257 /*
1258 =============================================================================
1259
1260 QUAKE FILESYSTEM
1261
1262 =============================================================================
1263 */
1264
1265 int     com_filesize;
1266
1267
1268 //
1269 // in memory
1270 //
1271
1272 typedef struct
1273 {
1274         char name[MAX_QPATH];
1275         int filepos, filelen;
1276 } packfile_t;
1277
1278 typedef struct pack_s
1279 {
1280         char filename[MAX_OSPATH];
1281         int handle;
1282         int numfiles;
1283         packfile_t *files;
1284         mempool_t *mempool;
1285         struct pack_s *next;
1286 } pack_t;
1287
1288 //
1289 // on disk
1290 //
1291 typedef struct
1292 {
1293         char name[56];
1294         int filepos, filelen;
1295 } dpackfile_t;
1296
1297 typedef struct
1298 {
1299         char id[4];
1300         int dirofs;
1301         int dirlen;
1302 } dpackheader_t;
1303
1304 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
1305 #define MAX_FILES_IN_PACK       65536
1306
1307 pack_t  *packlist = NULL;
1308
1309 #if CACHEENABLE
1310 char    com_cachedir[MAX_OSPATH];
1311 #endif
1312 char    com_gamedir[MAX_OSPATH];
1313
1314 typedef struct searchpath_s
1315 {
1316         char filename[MAX_OSPATH];
1317         pack_t *pack;          // only one of filename / pack will be used
1318         struct searchpath_s *next;
1319 } searchpath_t;
1320
1321 searchpath_t    *com_searchpaths;
1322
1323 /*
1324 ============
1325 COM_Path_f
1326
1327 ============
1328 */
1329 void COM_Path_f (void)
1330 {
1331         searchpath_t    *s;
1332
1333         Con_Printf ("Current search path:\n");
1334         for (s=com_searchpaths ; s ; s=s->next)
1335         {
1336                 if (s->pack)
1337                 {
1338                         Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1339                 }
1340                 else
1341                         Con_Printf ("%s\n", s->filename);
1342         }
1343 }
1344
1345 /*
1346 ============
1347 COM_CreatePath
1348
1349 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1350 ============
1351 */
1352 void    COM_CreatePath (char *path)
1353 {
1354         char    *ofs, save;
1355
1356         for (ofs = path+1 ; *ofs ; ofs++)
1357         {
1358                 if (*ofs == '/' || *ofs == '\\' || *ofs == ':')
1359                 {       // create the directory
1360                         save = *ofs;
1361                         *ofs = 0;
1362                         Sys_mkdir (path);
1363                         *ofs = save;
1364                 }
1365         }
1366 }
1367
1368
1369 /*
1370 ============
1371 COM_WriteFile
1372
1373 The filename will be prefixed by the current game directory
1374 ============
1375 */
1376 void COM_WriteFile (char *filename, void *data, int len)
1377 {
1378         int             handle;
1379         char    name[MAX_OSPATH];
1380
1381         sprintf (name, "%s/%s", com_gamedir, filename);
1382
1383         // LordHavoc: added this
1384         COM_CreatePath (name); // create directories up to the file
1385
1386         handle = Sys_FileOpenWrite (name);
1387         if (handle == -1)
1388         {
1389                 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1390                 return;
1391         }
1392
1393         Con_Printf ("COM_WriteFile: %s\n", name);
1394         Sys_FileWrite (handle, data, len);
1395         Sys_FileClose (handle);
1396 }
1397
1398
1399 /*
1400 ===========
1401 COM_CopyFile
1402
1403 Copies a file over from the net to the local cache, creating any directories
1404 needed.  This is for the convenience of developers using ISDN from home.
1405 ===========
1406 */
1407 void COM_CopyFile (char *netpath, char *cachepath)
1408 {
1409         int             in, out;
1410         int             remaining, count;
1411         char    buf[4096];
1412
1413         remaining = Sys_FileOpenRead (netpath, &in);            
1414         COM_CreatePath (cachepath);     // create directories up to the cache file
1415         out = Sys_FileOpenWrite (cachepath);
1416         
1417         while (remaining)
1418         {
1419                 if (remaining < sizeof(buf))
1420                         count = remaining;
1421                 else
1422                         count = sizeof(buf);
1423                 Sys_FileRead (in, buf, count);
1424                 Sys_FileWrite (out, buf, count);
1425                 remaining -= count;
1426         }
1427
1428         Sys_FileClose (in);
1429         Sys_FileClose (out);    
1430 }
1431
1432 /*
1433 ===========
1434 COM_OpenRead
1435 ===========
1436 */
1437 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1438 {
1439         int                             fd = open (path, O_RDONLY);
1440         unsigned char   id[2];
1441         unsigned char   len_bytes[4];
1442
1443         if (fd == -1)
1444         {
1445                 Sys_Error ("Couldn't open %s", path);
1446                 return 0;
1447         }
1448         if (offs < 0 || len < 0)
1449         {
1450                 // normal file
1451                 offs = 0;
1452                 len = lseek (fd, 0, SEEK_END);
1453                 lseek (fd, 0, SEEK_SET);
1454         }
1455         lseek (fd, offs, SEEK_SET);
1456         if (zip)
1457         {
1458                 read (fd, id, 2);
1459                 if (id[0] == 0x1f && id[1] == 0x8b)
1460                 {
1461                         lseek (fd, offs + len - 4, SEEK_SET);
1462                         read (fd, len_bytes, 4);
1463                         len = ((len_bytes[3] << 24)
1464                                    | (len_bytes[2] << 16)
1465                                    | (len_bytes[1] << 8)
1466                                    | (len_bytes[0]));
1467                 }
1468         }
1469         lseek (fd, offs, SEEK_SET);
1470         com_filesize = len;
1471
1472 #ifdef WIN32
1473         setmode (fd, O_BINARY);
1474 #endif
1475         if (zip)
1476                 return Qdopen (fd, "rbz");
1477         else
1478                 return Qdopen (fd, "rb");
1479 }
1480
1481 /*
1482 ===========
1483 COM_FindFile
1484
1485 Finds the file in the search path.
1486 Sets com_filesize and one of handle or file
1487 ===========
1488 */
1489 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1490 {
1491         searchpath_t    *search;
1492         char                    netpath[MAX_OSPATH];
1493 #if CACHEENABLE
1494         char                    cachepath[MAX_OSPATH];
1495         int                             cachetime;
1496 #endif
1497         pack_t                  *pak;
1498         int                             i;
1499         int                             findtime;
1500         char                    gzfilename[MAX_OSPATH];
1501         int                             filenamelen;
1502
1503         filenamelen = strlen (filename);
1504         sprintf (gzfilename, "%s.gz", filename);
1505
1506         if (!file)
1507                 Sys_Error ("COM_FindFile: file not set");
1508                 
1509 //
1510 // search through the path, one element at a time
1511 //
1512         search = com_searchpaths;
1513 //      if (proghack)
1514 //      {       // gross hack to use quake 1 progs with quake 2 maps
1515 //              if (!strcmp(filename, "progs.dat"))
1516 //                      search = search->next;
1517 //      }
1518
1519         for ( ; search ; search = search->next)
1520         {
1521         // is the element a pak file?
1522                 if (search->pack)
1523                 {
1524                 // look through all the pak file elements
1525                         pak = search->pack;
1526                         for (i=0 ; i<pak->numfiles ; i++)
1527                                 if (!strcmp (pak->files[i].name, filename)
1528                                     || !strcmp (pak->files[i].name, gzfilename))
1529                                 {       // found it!
1530                                         if (!quiet)
1531                                                 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1532                                         // open a new file on the pakfile
1533                                         *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1534                                         return com_filesize;
1535                                 }
1536                 }
1537                 else
1538                 {               
1539         // check a file in the directory tree
1540 //                      if (!static_registered)
1541 //                      {       // if not a registered version, don't ever go beyond base
1542 //                              if ( strchr (filename, '/') || strchr (filename,'\\'))
1543 //                                      continue;
1544 //                      }
1545                         
1546                         sprintf (netpath, "%s/%s",search->filename, filename);
1547                         
1548                         findtime = Sys_FileTime (netpath);
1549                         if (findtime == -1)
1550                                 continue;
1551                                 
1552 #if CACHEENABLE
1553                         // see if the file needs to be updated in the cache
1554                         if (com_cachedir[0])
1555                         {       
1556 #if defined(_WIN32)
1557                                 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1558                                         sprintf (cachepath,"%s%s", com_cachedir, netpath);
1559                                 else
1560                                         sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1561 #else
1562                                 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1563 #endif
1564
1565                                 cachetime = Sys_FileTime (cachepath);
1566                         
1567                                 if (cachetime < findtime)
1568                                         COM_CopyFile (netpath, cachepath);
1569                                 strcpy (netpath, cachepath);
1570                         }       
1571 #endif
1572
1573                         if (!quiet)
1574                                 Sys_Printf ("FindFile: %s\n",netpath);
1575                         *file = COM_OpenRead (netpath, -1, -1, zip);
1576                         return com_filesize;
1577                 }
1578                 
1579         }
1580         
1581         if (!quiet)
1582                 Sys_Printf ("FindFile: can't find %s\n", filename);
1583         
1584         *file = NULL;
1585         com_filesize = -1;
1586         return -1;
1587 }
1588
1589
1590 /*
1591 ===========
1592 COM_FOpenFile
1593
1594 If the requested file is inside a packfile, a new QFile * will be opened
1595 into the file.
1596 ===========
1597 */
1598 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1599 {
1600         return COM_FindFile (filename, file, quiet, zip);
1601 }
1602
1603
1604 /*
1605 ============
1606 COM_LoadFile
1607
1608 Filename are reletive to the quake directory.
1609 Always appends a 0 byte.
1610 ============
1611 */
1612 qbyte *loadbuf;
1613 int loadsize;
1614 qbyte *COM_LoadFile (char *path, qboolean quiet)
1615 {
1616         QFile *h;
1617         qbyte *buf;
1618         char base[1024];
1619         int len;
1620
1621         buf = NULL;     // quiet compiler warning
1622         loadsize = 0;
1623
1624 // look for it in the filesystem or pack files
1625         len = COM_FOpenFile (path, &h, quiet, true);
1626         if (!h)
1627                 return NULL;
1628
1629         loadsize = len;
1630
1631 // extract the filename base name for hunk tag
1632         COM_FileBase (path, base);
1633
1634         buf = Mem_Alloc(tempmempool, len+1);
1635         if (!buf)
1636                 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1637
1638         ((qbyte *)buf)[len] = 0;
1639
1640         Qread (h, buf, len);
1641         Qclose (h);
1642
1643         return buf;
1644 }
1645
1646 /*
1647 =================
1648 COM_LoadPackFile
1649
1650 Takes an explicit (not game tree related) path to a pak file.
1651
1652 Loads the header and directory, adding the files at the beginning
1653 of the list so they override previous pack files.
1654 =================
1655 */
1656 pack_t *COM_LoadPackFile (char *packfile)
1657 {
1658         dpackheader_t   header;
1659         int                             i;
1660         int                             numpackfiles;
1661         pack_t                  *pack;
1662         int                             packhandle;
1663         // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1664         dpackfile_t             *info;
1665
1666         if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1667         {
1668                 //Con_Printf ("Couldn't open %s\n", packfile);
1669                 return NULL;
1670         }
1671         Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1672         if (memcmp(header.id, "PACK", 4))
1673                 Sys_Error ("%s is not a packfile", packfile);
1674         header.dirofs = LittleLong (header.dirofs);
1675         header.dirlen = LittleLong (header.dirlen);
1676
1677         if (header.dirlen % sizeof(dpackfile_t))
1678                 Sys_Error ("%s has an invalid directory size", packfile);
1679
1680         numpackfiles = header.dirlen / sizeof(dpackfile_t);
1681
1682         if (numpackfiles > MAX_FILES_IN_PACK)
1683                 Sys_Error ("%s has %i files", packfile, numpackfiles);
1684
1685         pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1686         strcpy (pack->filename, packfile);
1687         pack->handle = packhandle;
1688         pack->numfiles = numpackfiles;
1689         pack->mempool = Mem_AllocPool(packfile);
1690         pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1691         pack->next = packlist;
1692         packlist = pack;
1693
1694         info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1695         Sys_FileSeek (packhandle, header.dirofs);
1696         Sys_FileRead (packhandle, (void *)info, header.dirlen);
1697
1698 // parse the directory
1699         for (i = 0;i < numpackfiles;i++)
1700         {
1701                 strcpy (pack->files[i].name, info[i].name);
1702                 pack->files[i].filepos = LittleLong(info[i].filepos);
1703                 pack->files[i].filelen = LittleLong(info[i].filelen);
1704         }
1705
1706         Mem_Free(info);
1707
1708         Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1709         return pack;
1710 }
1711
1712
1713 /*
1714 ================
1715 COM_AddGameDirectory
1716
1717 Sets com_gamedir, adds the directory to the head of the path,
1718 then loads and adds pak1.pak pak2.pak ...
1719 ================
1720 */
1721 void COM_AddGameDirectory (char *dir)
1722 {
1723         int                             i;
1724         searchpath_t    *search;
1725         pack_t                  *pak;
1726         char                    pakfile[MAX_OSPATH];
1727
1728         strcpy (com_gamedir, dir);
1729
1730 //
1731 // add the directory to the search path
1732 //
1733         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1734         strcpy (search->filename, dir);
1735         search->next = com_searchpaths;
1736         com_searchpaths = search;
1737
1738 //
1739 // add any pak files in the format pak0.pak pak1.pak, ...
1740 //
1741         for (i=0 ; ; i++)
1742         {
1743                 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1744                 pak = COM_LoadPackFile (pakfile);
1745                 if (!pak)
1746                         break;
1747                 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1748                 search->pack = pak;
1749                 search->next = com_searchpaths;
1750                 com_searchpaths = search;
1751         }
1752
1753 //
1754 // add the contents of the parms.txt file to the end of the command line
1755 //
1756
1757 }
1758
1759 /*
1760 ================
1761 COM_InitFilesystem
1762 ================
1763 */
1764 void COM_InitFilesystem (void)
1765 {
1766         int             i, j;
1767         char    basedir[MAX_OSPATH];
1768         searchpath_t    *search;
1769
1770 //
1771 // -basedir <path>
1772 // Overrides the system supplied base directory (under GAMENAME)
1773 //
1774         i = COM_CheckParm ("-basedir");
1775         if (i && i < com_argc-1)
1776                 strcpy (basedir, com_argv[i+1]);
1777         else
1778                 strcpy (basedir, host_parms.basedir);
1779
1780         j = strlen (basedir);
1781
1782         if (j > 0)
1783         {
1784                 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1785                         basedir[j-1] = 0;
1786         }
1787
1788 #if CACHEENABLE
1789 //
1790 // -cachedir <path>
1791 // Overrides the system supplied cache directory (NULL or /qcache)
1792 // -cachedir - will disable caching.
1793 //
1794         i = COM_CheckParm ("-cachedir");
1795         if (i && i < com_argc-1)
1796         {
1797                 if (com_argv[i+1][0] == '-')
1798                         com_cachedir[0] = 0;
1799                 else
1800                         strcpy (com_cachedir, com_argv[i+1]);
1801         }
1802         else if (host_parms.cachedir)
1803                 strcpy (com_cachedir, host_parms.cachedir);
1804         else
1805                 com_cachedir[0] = 0;
1806 #endif
1807
1808 // start up with GAMENAME by default (id1)
1809         COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1810
1811         switch(gamemode)
1812         {
1813         case GAME_NORMAL:
1814                 break;
1815         case GAME_HIPNOTIC:
1816                 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1817                 break;
1818         case GAME_ROGUE:
1819                 COM_AddGameDirectory (va("%s/rogue", basedir) );
1820                 break;
1821         case GAME_NEHAHRA:
1822                 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1823                 break;
1824         case GAME_FIENDARENA:
1825                 COM_AddGameDirectory (va("%s/fiendarena", basedir) );
1826                 break;
1827         case GAME_ZYMOTIC:
1828                 COM_AddGameDirectory (va("%s/zymotic", basedir) );
1829                 break;
1830         default:
1831                 Sys_Error("COM_InitFilesystem: unknown gamemode %i\n", gamemode);
1832                 break;
1833         }
1834
1835 //
1836 // -game <gamedir>
1837 // Adds basedir/gamedir as an override game
1838 //
1839         i = COM_CheckParm ("-game");
1840         if (i && i < com_argc-1)
1841         {
1842                 com_modified = true;
1843                 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1844         }
1845
1846 //
1847 // -path <dir or packfile> [<dir or packfile>] ...
1848 // Fully specifies the exact search path, overriding the generated one
1849 //
1850         i = COM_CheckParm ("-path");
1851         if (i)
1852         {
1853                 com_modified = true;
1854                 com_searchpaths = NULL;
1855                 while (++i < com_argc)
1856                 {
1857                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1858                                 break;
1859
1860                         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1861                         if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1862                         {
1863                                 search->pack = COM_LoadPackFile (com_argv[i]);
1864                                 if (!search->pack)
1865                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1866                         }
1867                         else
1868                                 strcpy (search->filename, com_argv[i]);
1869                         search->next = com_searchpaths;
1870                         com_searchpaths = search;
1871                 }
1872         }
1873
1874 //      if (COM_CheckParm ("-proghack"))
1875 //              proghack = true;
1876 }
1877
1878 int COM_FileExists(char *filename)
1879 {
1880         searchpath_t    *search;
1881         char                    netpath[MAX_OSPATH];
1882         pack_t                  *pak;
1883         int                             i;
1884         int                             findtime;
1885
1886         for (search = com_searchpaths;search;search = search->next)
1887         {
1888                 if (search->pack)
1889                 {
1890                         pak = search->pack;
1891                         for (i = 0;i < pak->numfiles;i++)
1892                                 if (!strcmp (pak->files[i].name, filename))
1893                                         return true;
1894                 }
1895                 else
1896                 {
1897                         sprintf (netpath, "%s/%s",search->filename, filename);
1898                         findtime = Sys_FileTime (netpath);
1899                         if (findtime != -1)
1900                                 return true;
1901                 }               
1902         }
1903
1904         return false;
1905 }
1906
1907
1908 //======================================
1909 // LordHavoc: added these because they are useful
1910
1911 void COM_ToLowerString(char *in, char *out)
1912 {
1913         while (*in)
1914         {
1915                 if (*in >= 'A' && *in <= 'Z')
1916                         *out++ = *in++ + 'a' - 'A';
1917                 else
1918                         *out++ = *in++;
1919         }
1920 }
1921
1922 void COM_ToUpperString(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 }