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