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