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