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