speedups to R_WorldNode, and some shrinkage on the surface struct
[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         if (dpprotocol)
589                 MSG_WriteFloat(sb, f);
590         else
591                 MSG_WriteShort (sb, (int)(f*8.0f + 0.5f));
592 }
593
594 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
595 {
596         MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
597 }
598
599 // LordHavoc: round to nearest value, rather than rounding down, fixes crosshair problem
600 void MSG_WriteAngle (sizebuf_t *sb, float f)
601 {
602         MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
603 }
604
605 //
606 // reading functions
607 //
608 int                     msg_readcount;
609 qboolean        msg_badread;
610
611 void MSG_BeginReading (void)
612 {
613         msg_readcount = 0;
614         msg_badread = false;
615 }
616
617 /*
618 // returns -1 and sets msg_badread if no more characters are available
619 int MSG_ReadChar (void)
620 {
621         int     c;
622         
623         // LordHavoc: minor optimization
624         if (msg_readcount >= net_message.cursize)
625 //      if (msg_readcount+1 > net_message.cursize)
626         {
627                 msg_badread = true;
628                 return -1;
629         }
630                 
631         c = (signed char)net_message.data[msg_readcount];
632         msg_readcount++;
633         
634         return c;
635 }
636
637 int MSG_ReadByte (void)
638 {
639         int     c;
640         
641         // LordHavoc: minor optimization
642         if (msg_readcount >= net_message.cursize)
643 //      if (msg_readcount+1 > net_message.cursize)
644         {
645                 msg_badread = true;
646                 return -1;
647         }
648                 
649         c = (unsigned char)net_message.data[msg_readcount];
650         msg_readcount++;
651         
652         return c;
653 }
654 */
655
656 int MSG_ReadShort (void)
657 {
658         int     c;
659         
660         if (msg_readcount+2 > net_message.cursize)
661         {
662                 msg_badread = true;
663                 return -1;
664         }
665                 
666         c = (short)(net_message.data[msg_readcount]
667         + (net_message.data[msg_readcount+1]<<8));
668         
669         msg_readcount += 2;
670         
671         return c;
672 }
673
674 int MSG_ReadLong (void)
675 {
676         int     c;
677         
678         if (msg_readcount+4 > net_message.cursize)
679         {
680                 msg_badread = true;
681                 return -1;
682         }
683                 
684         c = net_message.data[msg_readcount]
685         + (net_message.data[msg_readcount+1]<<8)
686         + (net_message.data[msg_readcount+2]<<16)
687         + (net_message.data[msg_readcount+3]<<24);
688         
689         msg_readcount += 4;
690         
691         return c;
692 }
693
694 float MSG_ReadFloat (void)
695 {
696         union
697         {
698                 byte    b[4];
699                 float   f;
700                 int     l;
701         } dat;
702         
703         dat.b[0] =      net_message.data[msg_readcount];
704         dat.b[1] =      net_message.data[msg_readcount+1];
705         dat.b[2] =      net_message.data[msg_readcount+2];
706         dat.b[3] =      net_message.data[msg_readcount+3];
707         msg_readcount += 4;
708         
709         dat.l = LittleLong (dat.l);
710
711         return dat.f;   
712 }
713
714 char *MSG_ReadString (void)
715 {
716         static char     string[2048];
717         int             l,c;
718         
719         l = 0;
720         do
721         {
722                 c = MSG_ReadChar ();
723                 if (c == -1 || c == 0)
724                         break;
725                 string[l] = c;
726                 l++;
727         } while (l < sizeof(string)-1);
728         
729         string[l] = 0;
730         
731         return string;
732 }
733
734 float MSG_ReadCoord (void)
735 {
736         if (dpprotocol)
737                 return MSG_ReadFloat();
738         else
739                 return MSG_ReadShort() * (1.0f/8.0f);
740 }
741
742 /*
743 float MSG_ReadCoord (void)
744 {
745         return MSG_ReadShort() * (1.0f/8.0f);
746 }
747
748 float MSG_ReadAngle (void)
749 {
750         return MSG_ReadChar() * (360.0f/256.0f);
751 }
752
753 float MSG_ReadPreciseAngle (void)
754 {
755         return MSG_ReadShort() * (360.0f/65536);
756 }
757 */
758
759
760 //===========================================================================
761
762 void SZ_Alloc (sizebuf_t *buf, int startsize)
763 {
764         if (startsize < 256)
765                 startsize = 256;
766         buf->data = Hunk_AllocName (startsize, "sizebuf");
767         buf->maxsize = startsize;
768         buf->cursize = 0;
769 }
770
771
772 void SZ_Free (sizebuf_t *buf)
773 {
774 //      Z_Free (buf->data);
775 //      buf->data = NULL;
776 //      buf->maxsize = 0;
777         buf->cursize = 0;
778 }
779
780 void SZ_Clear (sizebuf_t *buf)
781 {
782         buf->cursize = 0;
783 }
784
785 void *SZ_GetSpace (sizebuf_t *buf, int length)
786 {
787         void    *data;
788         
789         if (buf->cursize + length > buf->maxsize)
790         {
791                 if (!buf->allowoverflow)
792                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set - use -zone on the commandline for more zone memory, default: 512k (quake original default was 48k)");
793                 
794                 if (length > buf->maxsize)
795                         Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
796                         
797                 buf->overflowed = true;
798                 Con_Printf ("SZ_GetSpace: overflow");
799                 SZ_Clear (buf); 
800         }
801
802         data = buf->data + buf->cursize;
803         buf->cursize += length;
804         
805         return data;
806 }
807
808 void SZ_Write (sizebuf_t *buf, void *data, int length)
809 {
810         memcpy (SZ_GetSpace(buf,length),data,length);         
811 }
812
813 void SZ_Print (sizebuf_t *buf, char *data)
814 {
815         int             len;
816         
817         len = strlen(data)+1;
818
819 // byte * cast to keep VC++ happy
820         if (buf->data[buf->cursize-1])
821                 memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
822         else
823                 memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
824 }
825
826
827 //============================================================================
828
829
830 /*
831 ============
832 COM_SkipPath
833 ============
834 */
835 char *COM_SkipPath (char *pathname)
836 {
837         char    *last;
838         
839         last = pathname;
840         while (*pathname)
841         {
842                 if (*pathname=='/')
843                         last = pathname+1;
844                 pathname++;
845         }
846         return last;
847 }
848
849 /*
850 ============
851 COM_StripExtension
852 ============
853 */
854 void COM_StripExtension (char *in, char *out)
855 {
856         while (*in && *in != '.')
857                 *out++ = *in++;
858         *out = 0;
859 }
860
861 /*
862 ============
863 COM_FileExtension
864 ============
865 */
866 char *COM_FileExtension (char *in)
867 {
868         static char exten[8];
869         int             i;
870
871         while (*in && *in != '.')
872                 in++;
873         if (!*in)
874                 return "";
875         in++;
876         for (i=0 ; i<7 && *in ; i++,in++)
877                 exten[i] = *in;
878         exten[i] = 0;
879         return exten;
880 }
881
882 /*
883 ============
884 COM_FileBase
885 ============
886 */
887 void COM_FileBase (char *in, char *out)
888 {
889         char *s, *s2;
890         
891         s = in + strlen(in) - 1;
892         
893         while (s != in && *s != '.')
894                 s--;
895         
896         for (s2 = s ; *s2 && *s2 != '/' ; s2--)
897         ;
898         
899         if (s-s2 < 2)
900                 strcpy (out,"?model?");
901         else
902         {
903                 s--;
904                 strncpy (out,s2+1, s-s2);
905                 out[s-s2] = 0;
906         }
907 }
908
909
910 /*
911 ==================
912 COM_DefaultExtension
913 ==================
914 */
915 void COM_DefaultExtension (char *path, char *extension)
916 {
917         char    *src;
918 //
919 // if path doesn't have a .EXT, append extension
920 // (extension should include the .)
921 //
922         src = path + strlen(path) - 1;
923
924         while (*src != '/' && src != path)
925         {
926                 if (*src == '.')
927                         return;                 // it has an extension
928                 src--;
929         }
930
931         strcat (path, extension);
932 }
933
934
935 /*
936 ==============
937 COM_Parse
938
939 Parse a token out of a string
940 ==============
941 */
942 char *COM_Parse (char *data)
943 {
944         int             c;
945         int             len;
946         
947         len = 0;
948         com_token[0] = 0;
949         
950         if (!data)
951                 return NULL;
952                 
953 // skip whitespace
954 skipwhite:
955         while ( (c = *data) <= ' ')
956         {
957                 if (c == 0)
958                         return NULL;                    // end of file;
959                 data++;
960         }
961         
962 // skip // comments
963         if (c=='/' && data[1] == '/')
964         {
965                 while (*data && *data != '\n')
966                         data++;
967                 goto skipwhite;
968         }
969         
970
971 // handle quoted strings specially
972         if (c == '\"')
973         {
974                 data++;
975                 while (1)
976                 {
977                         c = *data++;
978                         if (c=='\"' || !c)
979                         {
980                                 com_token[len] = 0;
981                                 return data;
982                         }
983                         com_token[len] = c;
984                         len++;
985                 }
986         }
987
988 // parse single characters
989         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
990         {
991                 com_token[len] = c;
992                 len++;
993                 com_token[len] = 0;
994                 return data+1;
995         }
996
997 // parse a regular word
998         do
999         {
1000                 com_token[len] = c;
1001                 data++;
1002                 len++;
1003                 c = *data;
1004         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1005                         break;
1006         } while (c>32);
1007         
1008         com_token[len] = 0;
1009         return data;
1010 }
1011
1012
1013 /*
1014 ================
1015 COM_CheckParm
1016
1017 Returns the position (1 to argc-1) in the program's argument list
1018 where the given parameter apears, or 0 if not present
1019 ================
1020 */
1021 int COM_CheckParm (char *parm)
1022 {
1023         int             i;
1024         
1025         for (i=1 ; i<com_argc ; i++)
1026         {
1027                 if (!com_argv[i])
1028                         continue;               // NEXTSTEP sometimes clears appkit vars.
1029                 if (!strcmp (parm,com_argv[i]))
1030                         return i;
1031         }
1032                 
1033         return 0;
1034 }
1035
1036 /*
1037 ================
1038 COM_CheckRegistered
1039
1040 Looks for the pop.txt file and verifies it.
1041 Sets the "registered" cvar.
1042 Immediately exits out if an alternate game was attempted to be started without
1043 being registered.
1044 ================
1045 */
1046 void COM_CheckRegistered (void)
1047 {
1048         int             h;
1049         unsigned short  check[128];
1050         int                     i;
1051
1052         Cvar_Set ("cmdline", com_cmdline);
1053
1054         COM_OpenFile("gfx/pop.lmp", &h, false);
1055         static_registered = 0;
1056
1057         if (h == -1)
1058         {
1059                 if (com_modified)
1060                         Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1061                 else
1062                         Con_Printf ("Playing shareware version.\n");
1063 //#if WINDED
1064 //      Sys_Error ("This dedicated server requires a full registered copy of Quake");
1065 //#endif
1066 //              Con_Printf ("Playing shareware version.\n");
1067 //              if (com_modified)
1068 //                      Sys_Error ("You must have the registered version to use modified games");
1069                 return;
1070         }
1071
1072         Sys_FileRead (h, check, sizeof(check));
1073         COM_CloseFile (h);
1074         
1075         for (i=0 ; i<128 ; i++)
1076                 if (pop[i] != (unsigned short)BigShort (check[i]))
1077                         Sys_Error ("Corrupted data file.");
1078         
1079 //      Cvar_Set ("cmdline", com_cmdline);
1080         Cvar_Set ("registered", "1");
1081         static_registered = 1;
1082         Con_Printf ("Playing registered version.\n");
1083 }
1084
1085
1086 void COM_Path_f (void);
1087
1088
1089 /*
1090 ================
1091 COM_InitArgv
1092 ================
1093 */
1094 void COM_InitArgv (int argc, char **argv)
1095 {
1096         qboolean        safe;
1097         int             i, j, n;
1098
1099 // reconstitute the command line for the cmdline externally visible cvar
1100         n = 0;
1101
1102         for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1103         {
1104                 i = 0;
1105
1106                 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1107                 {
1108                         com_cmdline[n++] = argv[j][i++];
1109                 }
1110
1111                 if (n < (CMDLINE_LENGTH - 1))
1112                         com_cmdline[n++] = ' ';
1113                 else
1114                         break;
1115         }
1116
1117         com_cmdline[n] = 0;
1118
1119         safe = false;
1120
1121         for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1122                  com_argc++)
1123         {
1124                 largv[com_argc] = argv[com_argc];
1125                 if (!strcmp ("-safe", argv[com_argc]))
1126                         safe = true;
1127         }
1128
1129         if (safe)
1130         {
1131         // force all the safe-mode switches. Note that we reserved extra space in
1132         // case we need to add these, so we don't need an overflow check
1133                 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1134                 {
1135                         largv[com_argc] = safeargvs[i];
1136                         com_argc++;
1137                 }
1138         }
1139
1140         largv[com_argc] = argvdummy;
1141         com_argv = largv;
1142
1143 #ifdef NEHAHRA
1144         nehahra = true;
1145         standard_quake = false;
1146 #else
1147         if (COM_CheckParm ("-rogue"))
1148         {
1149                 rogue = true;
1150                 standard_quake = false;
1151         }
1152
1153         if (COM_CheckParm ("-hipnotic"))
1154         {
1155                 hipnotic = true;
1156                 standard_quake = false;
1157         }
1158
1159         if (COM_CheckParm ("-nehahra"))
1160         {
1161                 nehahra = true;
1162                 standard_quake = false;
1163         }
1164 #endif
1165 }
1166
1167
1168 /*
1169 ================
1170 COM_Init
1171 ================
1172 */
1173 void COM_Init (char *basedir)
1174 {
1175 #ifndef WIN32
1176         byte    swaptest[2] = {1,0};
1177
1178 // set the byte swapping variables in a portable manner 
1179         if ( *(short *)swaptest == 1)
1180         {
1181                 BigShort = ShortSwap;
1182                 LittleShort = ShortNoSwap;
1183                 BigLong = LongSwap;
1184                 LittleLong = LongNoSwap;
1185                 BigFloat = FloatSwap;
1186                 LittleFloat = FloatNoSwap;
1187         }
1188         else
1189         {
1190                 BigShort = ShortNoSwap;
1191                 LittleShort = ShortSwap;
1192                 BigLong = LongNoSwap;
1193                 LittleLong = LongSwap;
1194                 BigFloat = FloatNoSwap;
1195                 LittleFloat = FloatSwap;
1196         }
1197 #endif
1198
1199         Cvar_RegisterVariable (&registered);
1200         Cvar_RegisterVariable (&cmdline);
1201         Cmd_AddCommand ("path", COM_Path_f);
1202
1203         COM_InitFilesystem ();
1204         COM_CheckRegistered ();
1205 }
1206
1207
1208 /*
1209 ============
1210 va
1211
1212 does a varargs printf into a temp buffer, so I don't need to have
1213 varargs versions of all text functions.
1214 FIXME: make this buffer size safe someday
1215 ============
1216 */
1217 char    *va(char *format, ...)
1218 {
1219         va_list         argptr;
1220         static char             string[1024];
1221         
1222         va_start (argptr, format);
1223         vsprintf (string, format,argptr);
1224         va_end (argptr);
1225
1226         return string;  
1227 }
1228
1229
1230 /// just for debugging
1231 int     memsearch (byte *start, int count, int search)
1232 {
1233         int             i;
1234         
1235         for (i=0 ; i<count ; i++)
1236                 if (start[i] == search)
1237                         return i;
1238         return -1;
1239 }
1240
1241 /*
1242 =============================================================================
1243
1244 QUAKE FILESYSTEM
1245
1246 =============================================================================
1247 */
1248
1249 int     com_filesize;
1250
1251
1252 //
1253 // in memory
1254 //
1255
1256 typedef struct
1257 {
1258         char    name[MAX_QPATH];
1259         int             filepos, filelen;
1260 } packfile_t;
1261
1262 typedef struct pack_s
1263 {
1264         char    filename[MAX_OSPATH];
1265         int             handle;
1266         int             numfiles;
1267         packfile_t      *files;
1268 } pack_t;
1269
1270 //
1271 // on disk
1272 //
1273 typedef struct
1274 {
1275         char    name[56];
1276         int             filepos, filelen;
1277 } dpackfile_t;
1278
1279 typedef struct
1280 {
1281         char    id[4];
1282         int             dirofs;
1283         int             dirlen;
1284 } dpackheader_t;
1285
1286 // LordHavoc: was 2048, increased to 16384 and changed info[MAX_PACK_FILES] to a temporary malloc to avoid stack overflows
1287 #define MAX_FILES_IN_PACK       16384
1288
1289 char    com_cachedir[MAX_OSPATH];
1290 char    com_gamedir[MAX_OSPATH];
1291
1292 typedef struct searchpath_s
1293 {
1294         char    filename[MAX_OSPATH];
1295         pack_t  *pack;          // only one of filename / pack will be used
1296         struct searchpath_s *next;
1297 } searchpath_t;
1298
1299 searchpath_t    *com_searchpaths;
1300
1301 /*
1302 ============
1303 COM_Path_f
1304
1305 ============
1306 */
1307 void COM_Path_f (void)
1308 {
1309         searchpath_t    *s;
1310         
1311         Con_Printf ("Current search path:\n");
1312         for (s=com_searchpaths ; s ; s=s->next)
1313         {
1314                 if (s->pack)
1315                 {
1316                         Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1317                 }
1318                 else
1319                         Con_Printf ("%s\n", s->filename);
1320         }
1321 }
1322
1323 /*
1324 ============
1325 COM_WriteFile
1326
1327 The filename will be prefixed by the current game directory
1328 ============
1329 */
1330 void COM_WriteFile (char *filename, void *data, int len)
1331 {
1332         int             handle;
1333         char    name[MAX_OSPATH];
1334         
1335         sprintf (name, "%s/%s", com_gamedir, filename);
1336
1337         handle = Sys_FileOpenWrite (name);
1338         if (handle == -1)
1339         {
1340                 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1341                 return;
1342         }
1343         
1344         Sys_Printf ("COM_WriteFile: %s\n", name);
1345         Sys_FileWrite (handle, data, len);
1346         Sys_FileClose (handle);
1347 }
1348
1349
1350 /*
1351 ============
1352 COM_CreatePath
1353
1354 Only used for CopyFile
1355 ============
1356 */
1357 void    COM_CreatePath (char *path)
1358 {
1359         char    *ofs;
1360         
1361         for (ofs = path+1 ; *ofs ; ofs++)
1362         {
1363                 if (*ofs == '/')
1364                 {       // create the directory
1365                         *ofs = 0;
1366                         Sys_mkdir (path);
1367                         *ofs = '/';
1368                 }
1369         }
1370 }
1371
1372
1373 /*
1374 ===========
1375 COM_CopyFile
1376
1377 Copies a file over from the net to the local cache, creating any directories
1378 needed.  This is for the convenience of developers using ISDN from home.
1379 ===========
1380 */
1381 void COM_CopyFile (char *netpath, char *cachepath)
1382 {
1383         int             in, out;
1384         int             remaining, count;
1385         char    buf[4096];
1386         
1387         remaining = Sys_FileOpenRead (netpath, &in);            
1388         COM_CreatePath (cachepath);     // create directories up to the cache file
1389         out = Sys_FileOpenWrite (cachepath);
1390         
1391         while (remaining)
1392         {
1393                 if (remaining < sizeof(buf))
1394                         count = remaining;
1395                 else
1396                         count = sizeof(buf);
1397                 Sys_FileRead (in, buf, count);
1398                 Sys_FileWrite (out, buf, count);
1399                 remaining -= count;
1400         }
1401
1402         Sys_FileClose (in);
1403         Sys_FileClose (out);    
1404 }
1405
1406 /*
1407 ===========
1408 COM_FindFile
1409
1410 Finds the file in the search path.
1411 Sets com_filesize and one of handle or file
1412 ===========
1413 */
1414 int COM_FindFile (char *filename, int *handle, FILE **file, qboolean quiet)
1415 {
1416         searchpath_t    *search;
1417         char            netpath[MAX_OSPATH];
1418         char            cachepath[MAX_OSPATH];
1419         pack_t          *pak;
1420         int                     i;
1421         int                     findtime, cachetime;
1422
1423         if (file && handle)
1424                 Sys_Error ("COM_FindFile: both handle and file set");
1425         if (!file && !handle)
1426                 Sys_Error ("COM_FindFile: neither handle or file set");
1427                 
1428 //
1429 // search through the path, one element at a time
1430 //
1431         search = com_searchpaths;
1432         if (proghack)
1433         {       // gross hack to use quake 1 progs with quake 2 maps
1434                 if (!strcmp(filename, "progs.dat"))
1435                         search = search->next;
1436         }
1437
1438         for ( ; search ; search = search->next)
1439         {
1440         // is the element a pak file?
1441                 if (search->pack)
1442                 {
1443                 // look through all the pak file elements
1444                         pak = search->pack;
1445                         for (i=0 ; i<pak->numfiles ; i++)
1446                                 if (!strcmp (pak->files[i].name, filename))
1447                                 {       // found it!
1448                                         if (!quiet)
1449                                                 Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename);
1450                                         if (handle)
1451                                         {
1452                                                 *handle = pak->handle;
1453                                                 Sys_FileSeek (pak->handle, pak->files[i].filepos);
1454                                         }
1455                                         else
1456                                         {       // open a new file on the pakfile
1457                                                 *file = fopen (pak->filename, "rb");
1458                                                 if (*file)
1459                                                         fseek (*file, pak->files[i].filepos, SEEK_SET);
1460                                         }
1461                                         com_filesize = pak->files[i].filelen;
1462                                         return com_filesize;
1463                                 }
1464                 }
1465                 else
1466                 {               
1467         // check a file in the directory tree
1468 //                      if (!static_registered)
1469 //                      {       // if not a registered version, don't ever go beyond base
1470 //                              if ( strchr (filename, '/') || strchr (filename,'\\'))
1471 //                                      continue;
1472 //                      }
1473                         
1474                         sprintf (netpath, "%s/%s",search->filename, filename);
1475                         
1476                         findtime = Sys_FileTime (netpath);
1477                         if (findtime == -1)
1478                                 continue;
1479                                 
1480                 // see if the file needs to be updated in the cache
1481                         if (!com_cachedir[0])
1482                                 strcpy (cachepath, netpath);
1483                         else
1484                         {       
1485 #if defined(_WIN32)
1486                                 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1487                                         sprintf (cachepath,"%s%s", com_cachedir, netpath);
1488                                 else
1489                                         sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1490 #else
1491                                 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1492 #endif
1493
1494                                 cachetime = Sys_FileTime (cachepath);
1495                         
1496                                 if (cachetime < findtime)
1497                                         COM_CopyFile (netpath, cachepath);
1498                                 strcpy (netpath, cachepath);
1499                         }       
1500
1501                         if (!quiet)
1502                                 Sys_Printf ("FindFile: %s\n",netpath);
1503                         com_filesize = Sys_FileOpenRead (netpath, &i);
1504                         if (handle)
1505                                 *handle = i;
1506                         else
1507                         {
1508                                 Sys_FileClose (i);
1509                                 *file = fopen (netpath, "rb");
1510                         }
1511                         return com_filesize;
1512                 }
1513                 
1514         }
1515         
1516         if (!quiet)
1517                 Sys_Printf ("FindFile: can't find %s\n", filename);
1518         
1519         if (handle)
1520                 *handle = -1;
1521         else
1522                 *file = NULL;
1523         com_filesize = -1;
1524         return -1;
1525 }
1526
1527
1528 /*
1529 ===========
1530 COM_OpenFile
1531
1532 filename never has a leading slash, but may contain directory walks
1533 returns a handle and a length
1534 it may actually be inside a pak file
1535 ===========
1536 */
1537 int COM_OpenFile (char *filename, int *handle, qboolean quiet)
1538 {
1539         return COM_FindFile (filename, handle, NULL, quiet);
1540 }
1541
1542 /*
1543 ===========
1544 COM_FOpenFile
1545
1546 If the requested file is inside a packfile, a new FILE * will be opened
1547 into the file.
1548 ===========
1549 */
1550 int COM_FOpenFile (char *filename, FILE **file, qboolean quiet)
1551 {
1552         return COM_FindFile (filename, NULL, file, quiet);
1553 }
1554
1555 /*
1556 ============
1557 COM_CloseFile
1558
1559 If it is a pak file handle, don't really close it
1560 ============
1561 */
1562 void COM_CloseFile (int h)
1563 {
1564         searchpath_t    *s;
1565         
1566         for (s = com_searchpaths ; s ; s=s->next)
1567                 if (s->pack && s->pack->handle == h)
1568                         return;
1569                         
1570         Sys_FileClose (h);
1571 }
1572
1573
1574 /*
1575 ============
1576 COM_LoadFile
1577
1578 Filename are reletive to the quake directory.
1579 Allways appends a 0 byte.
1580 ============
1581 */
1582 cache_user_t *loadcache;
1583 byte    *loadbuf;
1584 int             loadsize;
1585 byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
1586 {
1587         int             h;
1588         byte    *buf;
1589         char    base[32];
1590         int             len;
1591
1592         buf = NULL;     // quiet compiler warning
1593
1594 // look for it in the filesystem or pack files
1595         len = COM_OpenFile (path, &h, quiet);
1596         if (h == -1)
1597                 return NULL;
1598         
1599 // extract the filename base name for hunk tag
1600         COM_FileBase (path, base);
1601         
1602         if (usehunk == 1)
1603                 buf = Hunk_AllocName (len+1, base);
1604         else if (usehunk == 2)
1605                 buf = Hunk_TempAlloc (len+1);
1606         else if (usehunk == 0)
1607                 buf = Z_Malloc (len+1);
1608         else if (usehunk == 3)
1609                 buf = Cache_Alloc (loadcache, len+1, base);
1610         else if (usehunk == 4)
1611         {
1612                 if (len+1 > loadsize)
1613                         buf = Hunk_TempAlloc (len+1);
1614                 else
1615                         buf = loadbuf;
1616         }
1617         else if (usehunk == 5)
1618                 buf = malloc (len+1);
1619         else
1620                 Sys_Error ("COM_LoadFile: bad usehunk");
1621
1622         if (!buf)
1623                 Sys_Error ("COM_LoadFile: not enough space for %s", path);
1624                 
1625         ((byte *)buf)[len] = 0;
1626
1627         Sys_FileRead (h, buf, len);                     
1628         COM_CloseFile (h);
1629
1630         return buf;
1631 }
1632
1633 byte *COM_LoadHunkFile (char *path, qboolean quiet)
1634 {
1635         return COM_LoadFile (path, 1, quiet);
1636 }
1637
1638 byte *COM_LoadTempFile (char *path, qboolean quiet)
1639 {
1640         return COM_LoadFile (path, 2, quiet);
1641 }
1642
1643 // LordHavoc: returns malloc'd memory
1644 byte *COM_LoadMallocFile (char *path, qboolean quiet)
1645 {
1646         return COM_LoadFile (path, 5, quiet);
1647 }
1648
1649 void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
1650 {
1651         loadcache = cu;
1652         COM_LoadFile (path, 3, quiet);
1653 }
1654
1655 // uses temp hunk if larger than bufsize
1656 byte *COM_LoadStackFile (char *path, void *buffer, int bufsize, qboolean quiet)
1657 {
1658         byte    *buf;
1659         
1660         loadbuf = (byte *)buffer;
1661         loadsize = bufsize;
1662         buf = COM_LoadFile (path, 4, quiet);
1663         
1664         return buf;
1665 }
1666
1667 /*
1668 =================
1669 COM_LoadPackFile
1670
1671 Takes an explicit (not game tree related) path to a pak file.
1672
1673 Loads the header and directory, adding the files at the beginning
1674 of the list so they override previous pack files.
1675 =================
1676 */
1677 pack_t *COM_LoadPackFile (char *packfile)
1678 {
1679         dpackheader_t   header;
1680         int                             i;
1681         packfile_t              *newfiles;
1682         int                             numpackfiles;
1683         pack_t                  *pack;
1684         int                             packhandle;
1685         // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
1686         dpackfile_t             *info;
1687         unsigned short          crc;
1688
1689         if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1690         {
1691 //              Con_Printf ("Couldn't open %s\n", packfile);
1692                 return NULL;
1693         }
1694         Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1695         if (header.id[0] != 'P' || header.id[1] != 'A'
1696         || header.id[2] != 'C' || header.id[3] != 'K')
1697                 Sys_Error ("%s is not a packfile", packfile);
1698         header.dirofs = LittleLong (header.dirofs);
1699         header.dirlen = LittleLong (header.dirlen);
1700
1701         numpackfiles = header.dirlen / sizeof(dpackfile_t);
1702
1703         if (numpackfiles > MAX_FILES_IN_PACK)
1704                 Sys_Error ("%s has %i files", packfile, numpackfiles);
1705
1706         if (numpackfiles != PAK0_COUNT)
1707                 com_modified = true;    // not the original file
1708
1709         newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "packfile");
1710
1711         info = malloc(sizeof(*info)*MAX_FILES_IN_PACK);
1712         Sys_FileSeek (packhandle, header.dirofs);
1713         Sys_FileRead (packhandle, (void *)info, header.dirlen);
1714
1715 // crc the directory to check for modifications
1716         CRC_Init (&crc);
1717         // LordHavoc: speedup
1718         CRC_ProcessBytes(&crc, (byte *)info, header.dirlen);
1719 //      for (i=0 ; i<header.dirlen ; i++)
1720 //              CRC_ProcessByte (&crc, ((byte *)info)[i]);
1721         if (crc != PAK0_CRC)
1722                 com_modified = true;
1723
1724 // parse the directory
1725         for (i=0 ; i<numpackfiles ; i++)
1726         {
1727                 strcpy (newfiles[i].name, info[i].name);
1728                 newfiles[i].filepos = LittleLong(info[i].filepos);
1729                 newfiles[i].filelen = LittleLong(info[i].filelen);
1730         }
1731         free(info);
1732
1733         pack = Hunk_Alloc (sizeof (pack_t));
1734         strcpy (pack->filename, packfile);
1735         pack->handle = packhandle;
1736         pack->numfiles = numpackfiles;
1737         pack->files = newfiles;
1738         
1739         Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1740         return pack;
1741 }
1742
1743
1744 /*
1745 ================
1746 COM_AddGameDirectory
1747
1748 Sets com_gamedir, adds the directory to the head of the path,
1749 then loads and adds pak1.pak pak2.pak ... 
1750 ================
1751 */
1752 void COM_AddGameDirectory (char *dir)
1753 {
1754         int                             i;
1755         searchpath_t    *search;
1756         pack_t                  *pak;
1757         char                    pakfile[MAX_OSPATH];
1758
1759         strcpy (com_gamedir, dir);
1760
1761 //
1762 // add the directory to the search path
1763 //
1764         search = Hunk_Alloc (sizeof(searchpath_t));
1765         strcpy (search->filename, dir);
1766         search->next = com_searchpaths;
1767         com_searchpaths = search;
1768
1769 //
1770 // add any pak files in the format pak0.pak pak1.pak, ...
1771 //
1772         for (i=0 ; ; i++)
1773         {
1774                 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1775                 pak = COM_LoadPackFile (pakfile);
1776                 if (!pak)
1777                         break;
1778                 search = Hunk_Alloc (sizeof(searchpath_t));
1779                 search->pack = pak;
1780                 search->next = com_searchpaths;
1781                 com_searchpaths = search;               
1782         }
1783
1784 //
1785 // add the contents of the parms.txt file to the end of the command line
1786 //
1787
1788 }
1789
1790 /*
1791 ================
1792 COM_InitFilesystem
1793 ================
1794 */
1795 void COM_InitFilesystem (void)
1796 {
1797         int             i, j;
1798         char    basedir[MAX_OSPATH];
1799         searchpath_t    *search;
1800
1801 //
1802 // -basedir <path>
1803 // Overrides the system supplied base directory (under GAMENAME)
1804 //
1805         i = COM_CheckParm ("-basedir");
1806         if (i && i < com_argc-1)
1807                 strcpy (basedir, com_argv[i+1]);
1808         else
1809                 strcpy (basedir, host_parms.basedir);
1810
1811         j = strlen (basedir);
1812
1813         if (j > 0)
1814         {
1815                 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1816                         basedir[j-1] = 0;
1817         }
1818
1819 //
1820 // -cachedir <path>
1821 // Overrides the system supplied cache directory (NULL or /qcache)
1822 // -cachedir - will disable caching.
1823 //
1824         i = COM_CheckParm ("-cachedir");
1825         if (i && i < com_argc-1)
1826         {
1827                 if (com_argv[i+1][0] == '-')
1828                         com_cachedir[0] = 0;
1829                 else
1830                         strcpy (com_cachedir, com_argv[i+1]);
1831         }
1832         else if (host_parms.cachedir)
1833                 strcpy (com_cachedir, host_parms.cachedir);
1834         else
1835                 com_cachedir[0] = 0;
1836
1837 //
1838 // start up with GAMENAME by default (id1)
1839 //
1840         COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1841
1842 #ifdef NEHAHRA
1843         COM_AddGameDirectory (va("%s/nehahra", basedir) );
1844 #else
1845         if (COM_CheckParm ("-rogue"))
1846                 COM_AddGameDirectory (va("%s/rogue", basedir) );
1847         if (COM_CheckParm ("-hipnotic"))
1848                 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1849         if (COM_CheckParm ("-nehahra"))
1850                 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1851 #endif
1852
1853 //
1854 // -game <gamedir>
1855 // Adds basedir/gamedir as an override game
1856 //
1857         i = COM_CheckParm ("-game");
1858         if (i && i < com_argc-1)
1859         {
1860                 com_modified = true;
1861                 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1862         }
1863
1864 //
1865 // -path <dir or packfile> [<dir or packfile>] ...
1866 // Fully specifies the exact serach path, overriding the generated one
1867 //
1868         i = COM_CheckParm ("-path");
1869         if (i)
1870         {
1871                 com_modified = true;
1872                 com_searchpaths = NULL;
1873                 while (++i < com_argc)
1874                 {
1875                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1876                                 break;
1877                         
1878                         search = Hunk_Alloc (sizeof(searchpath_t));
1879                         if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1880                         {
1881                                 search->pack = COM_LoadPackFile (com_argv[i]);
1882                                 if (!search->pack)
1883                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1884                         }
1885                         else
1886                                 strcpy (search->filename, com_argv[i]);
1887                         search->next = com_searchpaths;
1888                         com_searchpaths = search;
1889                 }
1890         }
1891
1892         if (COM_CheckParm ("-proghack"))
1893                 proghack = true;
1894 }
1895
1896 int COM_FileExists(char *filename)
1897 {
1898         searchpath_t    *search;
1899         char                    netpath[MAX_OSPATH];
1900         pack_t                  *pak;
1901         int                             i;
1902         int                             findtime;
1903
1904         for (search = com_searchpaths;search;search = search->next)
1905         {
1906                 if (search->pack)
1907                 {
1908                         pak = search->pack;
1909                         for (i = 0;i < pak->numfiles;i++)
1910                                 if (!strcmp (pak->files[i].name, filename))
1911                                         return true;
1912                 }
1913                 else
1914                 {
1915                         sprintf (netpath, "%s/%s",search->filename, filename);               
1916                         findtime = Sys_FileTime (netpath);
1917                         if (findtime != -1)
1918                                 return true;
1919                 }               
1920         }
1921
1922         return false;
1923 }
1924