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