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