improved TraceLine in chase.c to be more generally useful (should move it to another...
[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: 128k (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         // LordHavoc: EWW bug bug bug bug bug... added previously missing s2 != in (yes, could go hunting through mem for /)
897         for (s2 = s ; s2 != in && *s2 && *s2 != '/' ; s2--)
898         ;
899         
900         if (s-s2 < 2)
901                 strcpy (out,"?model?");
902         else
903         {
904                 // LordHavoc: FIXME: examine this
905                 s--;
906                 strncpy (out,s2+1, s-s2);
907                 out[s-s2] = 0;
908         }
909 }
910
911
912 /*
913 ==================
914 COM_DefaultExtension
915 ==================
916 */
917 void COM_DefaultExtension (char *path, char *extension)
918 {
919         char    *src;
920 //
921 // if path doesn't have a .EXT, append extension
922 // (extension should include the .)
923 //
924         src = path + strlen(path) - 1;
925
926         while (*src != '/' && src != path)
927         {
928                 if (*src == '.')
929                         return;                 // it has an extension
930                 src--;
931         }
932
933         strcat (path, extension);
934 }
935
936
937 /*
938 ==============
939 COM_Parse
940
941 Parse a token out of a string
942 ==============
943 */
944 char *COM_Parse (char *data)
945 {
946         int             c;
947         int             len;
948         
949         len = 0;
950         com_token[0] = 0;
951         
952         if (!data)
953                 return NULL;
954                 
955 // skip whitespace
956 skipwhite:
957         while ( (c = *data) <= ' ')
958         {
959                 if (c == 0)
960                         return NULL;                    // end of file;
961                 data++;
962         }
963         
964 // skip // comments
965         if (c=='/' && data[1] == '/')
966         {
967                 while (*data && *data != '\n')
968                         data++;
969                 goto skipwhite;
970         }
971         
972
973 // handle quoted strings specially
974         if (c == '\"')
975         {
976                 data++;
977                 while (1)
978                 {
979                         c = *data++;
980                         if (c=='\"' || !c)
981                         {
982                                 com_token[len] = 0;
983                                 return data;
984                         }
985                         com_token[len] = c;
986                         len++;
987                 }
988         }
989
990 // parse single characters
991         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
992         {
993                 com_token[len] = c;
994                 len++;
995                 com_token[len] = 0;
996                 return data+1;
997         }
998
999 // parse a regular word
1000         do
1001         {
1002                 com_token[len] = c;
1003                 data++;
1004                 len++;
1005                 c = *data;
1006         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1007                         break;
1008         } while (c>32);
1009         
1010         com_token[len] = 0;
1011         return data;
1012 }
1013
1014
1015 /*
1016 ================
1017 COM_CheckParm
1018
1019 Returns the position (1 to argc-1) in the program's argument list
1020 where the given parameter apears, or 0 if not present
1021 ================
1022 */
1023 int COM_CheckParm (char *parm)
1024 {
1025         int             i;
1026         
1027         for (i=1 ; i<com_argc ; i++)
1028         {
1029                 if (!com_argv[i])
1030                         continue;               // NEXTSTEP sometimes clears appkit vars.
1031                 if (!strcmp (parm,com_argv[i]))
1032                         return i;
1033         }
1034                 
1035         return 0;
1036 }
1037
1038 /*
1039 ================
1040 COM_CheckRegistered
1041
1042 Looks for the pop.txt file and verifies it.
1043 Sets the "registered" cvar.
1044 Immediately exits out if an alternate game was attempted to be started without
1045 being registered.
1046 ================
1047 */
1048 void COM_CheckRegistered (void)
1049 {
1050         int             h;
1051         unsigned short  check[128];
1052         int                     i;
1053
1054         Cvar_Set ("cmdline", com_cmdline);
1055
1056         COM_OpenFile("gfx/pop.lmp", &h, false);
1057         static_registered = 0;
1058
1059         if (h == -1)
1060         {
1061                 if (com_modified)
1062                         Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1063                 else
1064                         Con_Printf ("Playing shareware version.\n");
1065 //#if WINDED
1066 //      Sys_Error ("This dedicated server requires a full registered copy of Quake");
1067 //#endif
1068 //              Con_Printf ("Playing shareware version.\n");
1069 //              if (com_modified)
1070 //                      Sys_Error ("You must have the registered version to use modified games");
1071                 return;
1072         }
1073
1074         Sys_FileRead (h, check, sizeof(check));
1075         COM_CloseFile (h);
1076         
1077         for (i=0 ; i<128 ; i++)
1078                 if (pop[i] != (unsigned short)BigShort (check[i]))
1079                         Sys_Error ("Corrupted data file.");
1080         
1081 //      Cvar_Set ("cmdline", com_cmdline);
1082         Cvar_Set ("registered", "1");
1083         static_registered = 1;
1084         Con_Printf ("Playing registered version.\n");
1085 }
1086
1087
1088 void COM_Path_f (void);
1089
1090
1091 /*
1092 ================
1093 COM_InitArgv
1094 ================
1095 */
1096 void COM_InitArgv (int argc, char **argv)
1097 {
1098         qboolean        safe;
1099         int             i, j, n;
1100
1101 // reconstitute the command line for the cmdline externally visible cvar
1102         n = 0;
1103
1104         for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1105         {
1106                 i = 0;
1107
1108                 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1109                 {
1110                         com_cmdline[n++] = argv[j][i++];
1111                 }
1112
1113                 if (n < (CMDLINE_LENGTH - 1))
1114                         com_cmdline[n++] = ' ';
1115                 else
1116                         break;
1117         }
1118
1119         com_cmdline[n] = 0;
1120
1121         safe = false;
1122
1123         for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1124                  com_argc++)
1125         {
1126                 largv[com_argc] = argv[com_argc];
1127                 if (!strcmp ("-safe", argv[com_argc]))
1128                         safe = true;
1129         }
1130
1131         if (safe)
1132         {
1133         // force all the safe-mode switches. Note that we reserved extra space in
1134         // case we need to add these, so we don't need an overflow check
1135                 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1136                 {
1137                         largv[com_argc] = safeargvs[i];
1138                         com_argc++;
1139                 }
1140         }
1141
1142         largv[com_argc] = argvdummy;
1143         com_argv = largv;
1144
1145 #ifdef NEHAHRA
1146         nehahra = true;
1147         standard_quake = false;
1148 #else
1149         if (COM_CheckParm ("-rogue"))
1150         {
1151                 rogue = true;
1152                 standard_quake = false;
1153         }
1154
1155         if (COM_CheckParm ("-hipnotic"))
1156         {
1157                 hipnotic = true;
1158                 standard_quake = false;
1159         }
1160
1161         if (COM_CheckParm ("-nehahra"))
1162         {
1163                 nehahra = true;
1164                 standard_quake = false;
1165         }
1166 #endif
1167 }
1168
1169
1170 unsigned int qmalloctotal_alloc, qmalloctotal_alloccount, qmalloctotal_free, qmalloctotal_freecount;
1171
1172 void *qmalloc(unsigned int size)
1173 {
1174         unsigned int *mem;
1175         qmalloctotal_alloc += size;
1176         qmalloctotal_alloccount++;
1177         mem = malloc(size+sizeof(unsigned int));
1178         if (!mem)
1179                 return mem;
1180         *mem = size;
1181         return (void *)(mem + 1);
1182 }
1183
1184 void qfree(void *mem)
1185 {
1186         unsigned int *m;
1187         if (!mem)
1188                 return;
1189         m = mem;
1190         m--; // back up to size
1191         qmalloctotal_free += *m; // size
1192         qmalloctotal_freecount++;
1193         free(m);
1194 }
1195
1196 void GL_TextureStats_PrintTotal(void);
1197 extern int hunk_low_used, hunk_high_used, hunk_size;
1198 void COM_Memstats_f(void)
1199 {
1200         Con_Printf("%i malloc calls totalling %i bytes (%.4gMB)\n%i free calls totalling %i bytes (%.4gMB)\n%i bytes (%.4gMB) currently allocated\n", qmalloctotal_alloccount, qmalloctotal_alloc, qmalloctotal_alloc / 1048576.0, qmalloctotal_freecount, qmalloctotal_free, qmalloctotal_free / 1048576.0, qmalloctotal_alloc - qmalloctotal_free, (qmalloctotal_alloc - qmalloctotal_free) / 1048576.0);
1201         GL_TextureStats_PrintTotal();
1202         Con_Printf ("%i bytes (%.4gMB) of %.4gMB hunk in use\n", hunk_low_used + hunk_high_used, (hunk_low_used + hunk_high_used) / 1048576.0, hunk_size / 1048576.0);
1203 }
1204
1205
1206 /*
1207 ================
1208 COM_Init
1209 ================
1210 */
1211 void COM_Init (char *basedir)
1212 {
1213 #ifndef WIN32
1214         byte    swaptest[2] = {1,0};
1215
1216 // set the byte swapping variables in a portable manner 
1217         if ( *(short *)swaptest == 1)
1218         {
1219                 BigShort = ShortSwap;
1220                 LittleShort = ShortNoSwap;
1221                 BigLong = LongSwap;
1222                 LittleLong = LongNoSwap;
1223                 BigFloat = FloatSwap;
1224                 LittleFloat = FloatNoSwap;
1225         }
1226         else
1227         {
1228                 BigShort = ShortNoSwap;
1229                 LittleShort = ShortSwap;
1230                 BigLong = LongNoSwap;
1231                 LittleLong = LongSwap;
1232                 BigFloat = FloatNoSwap;
1233                 LittleFloat = FloatSwap;
1234         }
1235 #endif
1236
1237         Cvar_RegisterVariable (&registered);
1238         Cvar_RegisterVariable (&cmdline);
1239         Cmd_AddCommand ("path", COM_Path_f);
1240         Cmd_AddCommand ("memstats", COM_Memstats_f);
1241
1242         COM_InitFilesystem ();
1243         COM_CheckRegistered ();
1244 }
1245
1246
1247 /*
1248 ============
1249 va
1250
1251 does a varargs printf into a temp buffer, so I don't need to have
1252 varargs versions of all text functions.
1253 FIXME: make this buffer size safe someday
1254 ============
1255 */
1256 char    *va(char *format, ...)
1257 {
1258         va_list         argptr;
1259         static char             string[1024];
1260         
1261         va_start (argptr, format);
1262         vsprintf (string, format,argptr);
1263         va_end (argptr);
1264
1265         return string;  
1266 }
1267
1268
1269 /// just for debugging
1270 int     memsearch (byte *start, int count, int search)
1271 {
1272         int             i;
1273         
1274         for (i=0 ; i<count ; i++)
1275                 if (start[i] == search)
1276                         return i;
1277         return -1;
1278 }
1279
1280 /*
1281 =============================================================================
1282
1283 QUAKE FILESYSTEM
1284
1285 =============================================================================
1286 */
1287
1288 int     com_filesize;
1289
1290
1291 //
1292 // in memory
1293 //
1294
1295 typedef struct
1296 {
1297         char    name[MAX_QPATH];
1298         int             filepos, filelen;
1299 } packfile_t;
1300
1301 typedef struct pack_s
1302 {
1303         char    filename[MAX_OSPATH];
1304         int             handle;
1305         int             numfiles;
1306         packfile_t      *files;
1307 } pack_t;
1308
1309 //
1310 // on disk
1311 //
1312 typedef struct
1313 {
1314         char    name[56];
1315         int             filepos, filelen;
1316 } dpackfile_t;
1317
1318 typedef struct
1319 {
1320         char    id[4];
1321         int             dirofs;
1322         int             dirlen;
1323 } dpackheader_t;
1324
1325 // LordHavoc: was 2048, increased to 16384 and changed info[MAX_PACK_FILES] to a temporary malloc to avoid stack overflows
1326 #define MAX_FILES_IN_PACK       16384
1327
1328 char    com_cachedir[MAX_OSPATH];
1329 char    com_gamedir[MAX_OSPATH];
1330
1331 typedef struct searchpath_s
1332 {
1333         char    filename[MAX_OSPATH];
1334         pack_t  *pack;          // only one of filename / pack will be used
1335         struct searchpath_s *next;
1336 } searchpath_t;
1337
1338 searchpath_t    *com_searchpaths;
1339
1340 /*
1341 ============
1342 COM_Path_f
1343
1344 ============
1345 */
1346 void COM_Path_f (void)
1347 {
1348         searchpath_t    *s;
1349         
1350         Con_Printf ("Current search path:\n");
1351         for (s=com_searchpaths ; s ; s=s->next)
1352         {
1353                 if (s->pack)
1354                 {
1355                         Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1356                 }
1357                 else
1358                         Con_Printf ("%s\n", s->filename);
1359         }
1360 }
1361
1362 /*
1363 ============
1364 COM_WriteFile
1365
1366 The filename will be prefixed by the current game directory
1367 ============
1368 */
1369 void COM_WriteFile (char *filename, void *data, int len)
1370 {
1371         int             handle;
1372         char    name[MAX_OSPATH];
1373         
1374         sprintf (name, "%s/%s", com_gamedir, filename);
1375
1376         handle = Sys_FileOpenWrite (name);
1377         if (handle == -1)
1378         {
1379                 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1380                 return;
1381         }
1382         
1383         Sys_Printf ("COM_WriteFile: %s\n", name);
1384         Sys_FileWrite (handle, data, len);
1385         Sys_FileClose (handle);
1386 }
1387
1388
1389 /*
1390 ============
1391 COM_CreatePath
1392
1393 Only used for CopyFile
1394 ============
1395 */
1396 void    COM_CreatePath (char *path)
1397 {
1398         char    *ofs;
1399         
1400         for (ofs = path+1 ; *ofs ; ofs++)
1401         {
1402                 if (*ofs == '/')
1403                 {       // create the directory
1404                         *ofs = 0;
1405                         Sys_mkdir (path);
1406                         *ofs = '/';
1407                 }
1408         }
1409 }
1410
1411
1412 /*
1413 ===========
1414 COM_CopyFile
1415
1416 Copies a file over from the net to the local cache, creating any directories
1417 needed.  This is for the convenience of developers using ISDN from home.
1418 ===========
1419 */
1420 void COM_CopyFile (char *netpath, char *cachepath)
1421 {
1422         int             in, out;
1423         int             remaining, count;
1424         char    buf[4096];
1425         
1426         remaining = Sys_FileOpenRead (netpath, &in);            
1427         COM_CreatePath (cachepath);     // create directories up to the cache file
1428         out = Sys_FileOpenWrite (cachepath);
1429         
1430         while (remaining)
1431         {
1432                 if (remaining < sizeof(buf))
1433                         count = remaining;
1434                 else
1435                         count = sizeof(buf);
1436                 Sys_FileRead (in, buf, count);
1437                 Sys_FileWrite (out, buf, count);
1438                 remaining -= count;
1439         }
1440
1441         Sys_FileClose (in);
1442         Sys_FileClose (out);    
1443 }
1444
1445 /*
1446 ===========
1447 COM_FindFile
1448
1449 Finds the file in the search path.
1450 Sets com_filesize and one of handle or file
1451 ===========
1452 */
1453 int COM_FindFile (char *filename, int *handle, FILE **file, qboolean quiet)
1454 {
1455         searchpath_t    *search;
1456         char            netpath[MAX_OSPATH];
1457         char            cachepath[MAX_OSPATH];
1458         pack_t          *pak;
1459         int                     i;
1460         int                     findtime, cachetime;
1461
1462         if (file && handle)
1463                 Sys_Error ("COM_FindFile: both handle and file set");
1464         if (!file && !handle)
1465                 Sys_Error ("COM_FindFile: neither handle or file set");
1466                 
1467 //
1468 // search through the path, one element at a time
1469 //
1470         search = com_searchpaths;
1471         if (proghack)
1472         {       // gross hack to use quake 1 progs with quake 2 maps
1473                 if (!strcmp(filename, "progs.dat"))
1474                         search = search->next;
1475         }
1476
1477         for ( ; search ; search = search->next)
1478         {
1479         // is the element a pak file?
1480                 if (search->pack)
1481                 {
1482                 // look through all the pak file elements
1483                         pak = search->pack;
1484                         for (i=0 ; i<pak->numfiles ; i++)
1485                                 if (!strcmp (pak->files[i].name, filename))
1486                                 {       // found it!
1487                                         if (!quiet)
1488                                                 Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename);
1489                                         if (handle)
1490                                         {
1491                                                 *handle = pak->handle;
1492                                                 Sys_FileSeek (pak->handle, pak->files[i].filepos);
1493                                         }
1494                                         else
1495                                         {       // open a new file on the pakfile
1496                                                 *file = fopen (pak->filename, "rb");
1497                                                 if (*file)
1498                                                         fseek (*file, pak->files[i].filepos, SEEK_SET);
1499                                         }
1500                                         com_filesize = pak->files[i].filelen;
1501                                         return com_filesize;
1502                                 }
1503                 }
1504                 else
1505                 {               
1506         // check a file in the directory tree
1507 //                      if (!static_registered)
1508 //                      {       // if not a registered version, don't ever go beyond base
1509 //                              if ( strchr (filename, '/') || strchr (filename,'\\'))
1510 //                                      continue;
1511 //                      }
1512                         
1513                         sprintf (netpath, "%s/%s",search->filename, filename);
1514                         
1515                         findtime = Sys_FileTime (netpath);
1516                         if (findtime == -1)
1517                                 continue;
1518                                 
1519                 // see if the file needs to be updated in the cache
1520                         if (!com_cachedir[0])
1521                                 strcpy (cachepath, netpath);
1522                         else
1523                         {       
1524 #if defined(_WIN32)
1525                                 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1526                                         sprintf (cachepath,"%s%s", com_cachedir, netpath);
1527                                 else
1528                                         sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1529 #else
1530                                 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1531 #endif
1532
1533                                 cachetime = Sys_FileTime (cachepath);
1534                         
1535                                 if (cachetime < findtime)
1536                                         COM_CopyFile (netpath, cachepath);
1537                                 strcpy (netpath, cachepath);
1538                         }       
1539
1540                         if (!quiet)
1541                                 Sys_Printf ("FindFile: %s\n",netpath);
1542                         com_filesize = Sys_FileOpenRead (netpath, &i);
1543                         if (handle)
1544                                 *handle = i;
1545                         else
1546                         {
1547                                 Sys_FileClose (i);
1548                                 *file = fopen (netpath, "rb");
1549                         }
1550                         return com_filesize;
1551                 }
1552                 
1553         }
1554         
1555         if (!quiet)
1556                 Sys_Printf ("FindFile: can't find %s\n", filename);
1557         
1558         if (handle)
1559                 *handle = -1;
1560         else
1561                 *file = NULL;
1562         com_filesize = -1;
1563         return -1;
1564 }
1565
1566
1567 /*
1568 ===========
1569 COM_OpenFile
1570
1571 filename never has a leading slash, but may contain directory walks
1572 returns a handle and a length
1573 it may actually be inside a pak file
1574 ===========
1575 */
1576 int COM_OpenFile (char *filename, int *handle, qboolean quiet)
1577 {
1578         return COM_FindFile (filename, handle, NULL, quiet);
1579 }
1580
1581 /*
1582 ===========
1583 COM_FOpenFile
1584
1585 If the requested file is inside a packfile, a new FILE * will be opened
1586 into the file.
1587 ===========
1588 */
1589 int COM_FOpenFile (char *filename, FILE **file, qboolean quiet)
1590 {
1591         return COM_FindFile (filename, NULL, file, quiet);
1592 }
1593
1594 /*
1595 ============
1596 COM_CloseFile
1597
1598 If it is a pak file handle, don't really close it
1599 ============
1600 */
1601 void COM_CloseFile (int h)
1602 {
1603         searchpath_t    *s;
1604         
1605         for (s = com_searchpaths ; s ; s=s->next)
1606                 if (s->pack && s->pack->handle == h)
1607                         return;
1608                         
1609         Sys_FileClose (h);
1610 }
1611
1612
1613 /*
1614 ============
1615 COM_LoadFile
1616
1617 Filename are reletive to the quake directory.
1618 Allways appends a 0 byte.
1619 ============
1620 */
1621 cache_user_t *loadcache;
1622 byte    *loadbuf;
1623 int             loadsize;
1624 byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
1625 {
1626         int             h;
1627         byte    *buf;
1628         char    base[1024];
1629         int             len;
1630
1631         buf = NULL;     // quiet compiler warning
1632
1633 // look for it in the filesystem or pack files
1634         len = COM_OpenFile (path, &h, quiet);
1635         if (h == -1)
1636                 return NULL;
1637         
1638 // extract the filename base name for hunk tag
1639         COM_FileBase (path, base);
1640         
1641         switch (usehunk)
1642         {
1643         case 1:
1644                 buf = Hunk_AllocName (len+1, va("%s (file)", path));
1645                 if (!buf)
1646                         Sys_Error ("COM_LoadFile: not enough hunk space for %s (size %i)", path, len);
1647                 break;
1648 //      case 0:
1649 //              buf = Z_Malloc (len+1);
1650 //              if (!buf)
1651 //                      Sys_Error ("COM_LoadFile: not enough zone space for %s (size %i)", path, len);
1652 //              break;
1653         case 3:
1654                 buf = Cache_Alloc (loadcache, len+1, base);
1655                 if (!buf)
1656                         Sys_Error ("COM_LoadFile: not enough cache space for %s (size %i)", path, len);
1657                 break;
1658         case 5:
1659                 buf = qmalloc (len+1);
1660                 if (!buf)
1661                         Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1662                 break;
1663         default:
1664                 Sys_Error ("COM_LoadFile: bad usehunk");
1665                 break;
1666         }
1667
1668         ((byte *)buf)[len] = 0;
1669
1670         Sys_FileRead (h, buf, len);                     
1671         COM_CloseFile (h);
1672
1673         return buf;
1674 }
1675
1676 byte *COM_LoadHunkFile (char *path, qboolean quiet)
1677 {
1678         return COM_LoadFile (path, 1, quiet);
1679 }
1680
1681 // LordHavoc: returns malloc'd memory
1682 byte *COM_LoadMallocFile (char *path, qboolean quiet)
1683 {
1684         return COM_LoadFile (path, 5, quiet);
1685 }
1686
1687 void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
1688 {
1689         loadcache = cu;
1690         COM_LoadFile (path, 3, quiet);
1691 }
1692
1693 /*
1694 =================
1695 COM_LoadPackFile
1696
1697 Takes an explicit (not game tree related) path to a pak file.
1698
1699 Loads the header and directory, adding the files at the beginning
1700 of the list so they override previous pack files.
1701 =================
1702 */
1703 pack_t *COM_LoadPackFile (char *packfile)
1704 {
1705         dpackheader_t   header;
1706         int                             i;
1707         packfile_t              *newfiles;
1708         int                             numpackfiles;
1709         pack_t                  *pack;
1710         int                             packhandle;
1711         // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
1712         dpackfile_t             *info;
1713         unsigned short          crc;
1714
1715         if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1716         {
1717 //              Con_Printf ("Couldn't open %s\n", packfile);
1718                 return NULL;
1719         }
1720         Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1721         if (header.id[0] != 'P' || header.id[1] != 'A'
1722         || header.id[2] != 'C' || header.id[3] != 'K')
1723                 Sys_Error ("%s is not a packfile", packfile);
1724         header.dirofs = LittleLong (header.dirofs);
1725         header.dirlen = LittleLong (header.dirlen);
1726
1727         numpackfiles = header.dirlen / sizeof(dpackfile_t);
1728
1729         if (numpackfiles > MAX_FILES_IN_PACK)
1730                 Sys_Error ("%s has %i files", packfile, numpackfiles);
1731
1732         if (numpackfiles != PAK0_COUNT)
1733                 com_modified = true;    // not the original file
1734
1735         newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "pack file-table");
1736
1737         info = qmalloc(sizeof(*info)*MAX_FILES_IN_PACK);
1738         Sys_FileSeek (packhandle, header.dirofs);
1739         Sys_FileRead (packhandle, (void *)info, header.dirlen);
1740
1741 // crc the directory to check for modifications
1742         CRC_Init (&crc);
1743         // LordHavoc: speedup
1744         CRC_ProcessBytes(&crc, (byte *)info, header.dirlen);
1745 //      for (i=0 ; i<header.dirlen ; i++)
1746 //              CRC_ProcessByte (&crc, ((byte *)info)[i]);
1747         if (crc != PAK0_CRC)
1748                 com_modified = true;
1749
1750 // parse the directory
1751         for (i=0 ; i<numpackfiles ; i++)
1752         {
1753                 strcpy (newfiles[i].name, info[i].name);
1754                 newfiles[i].filepos = LittleLong(info[i].filepos);
1755                 newfiles[i].filelen = LittleLong(info[i].filelen);
1756         }
1757         qfree(info);
1758
1759         pack = Hunk_AllocName (sizeof (pack_t), packfile);
1760         strcpy (pack->filename, packfile);
1761         pack->handle = packhandle;
1762         pack->numfiles = numpackfiles;
1763         pack->files = newfiles;
1764         
1765         Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1766         return pack;
1767 }
1768
1769
1770 /*
1771 ================
1772 COM_AddGameDirectory
1773
1774 Sets com_gamedir, adds the directory to the head of the path,
1775 then loads and adds pak1.pak pak2.pak ... 
1776 ================
1777 */
1778 void COM_AddGameDirectory (char *dir)
1779 {
1780         int                             i;
1781         searchpath_t    *search;
1782         pack_t                  *pak;
1783         char                    pakfile[MAX_OSPATH];
1784
1785         strcpy (com_gamedir, dir);
1786
1787 //
1788 // add the directory to the search path
1789 //
1790         search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1791         strcpy (search->filename, dir);
1792         search->next = com_searchpaths;
1793         com_searchpaths = search;
1794
1795 //
1796 // add any pak files in the format pak0.pak pak1.pak, ...
1797 //
1798         for (i=0 ; ; i++)
1799         {
1800                 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1801                 pak = COM_LoadPackFile (pakfile);
1802                 if (!pak)
1803                         break;
1804                 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1805                 search->pack = pak;
1806                 search->next = com_searchpaths;
1807                 com_searchpaths = search;
1808         }
1809
1810 //
1811 // add the contents of the parms.txt file to the end of the command line
1812 //
1813
1814 }
1815
1816 /*
1817 ================
1818 COM_InitFilesystem
1819 ================
1820 */
1821 void COM_InitFilesystem (void)
1822 {
1823         int             i, j;
1824         char    basedir[MAX_OSPATH];
1825         searchpath_t    *search;
1826
1827 //
1828 // -basedir <path>
1829 // Overrides the system supplied base directory (under GAMENAME)
1830 //
1831         i = COM_CheckParm ("-basedir");
1832         if (i && i < com_argc-1)
1833                 strcpy (basedir, com_argv[i+1]);
1834         else
1835                 strcpy (basedir, host_parms.basedir);
1836
1837         j = strlen (basedir);
1838
1839         if (j > 0)
1840         {
1841                 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1842                         basedir[j-1] = 0;
1843         }
1844
1845 //
1846 // -cachedir <path>
1847 // Overrides the system supplied cache directory (NULL or /qcache)
1848 // -cachedir - will disable caching.
1849 //
1850         i = COM_CheckParm ("-cachedir");
1851         if (i && i < com_argc-1)
1852         {
1853                 if (com_argv[i+1][0] == '-')
1854                         com_cachedir[0] = 0;
1855                 else
1856                         strcpy (com_cachedir, com_argv[i+1]);
1857         }
1858         else if (host_parms.cachedir)
1859                 strcpy (com_cachedir, host_parms.cachedir);
1860         else
1861                 com_cachedir[0] = 0;
1862
1863 //
1864 // start up with GAMENAME by default (id1)
1865 //
1866         COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1867
1868 #ifdef NEHAHRA
1869         COM_AddGameDirectory (va("%s/nehahra", basedir) );
1870 #else
1871         if (COM_CheckParm ("-rogue"))
1872                 COM_AddGameDirectory (va("%s/rogue", basedir) );
1873         if (COM_CheckParm ("-hipnotic"))
1874                 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1875         if (COM_CheckParm ("-nehahra"))
1876                 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1877 #endif
1878
1879 //
1880 // -game <gamedir>
1881 // Adds basedir/gamedir as an override game
1882 //
1883         i = COM_CheckParm ("-game");
1884         if (i && i < com_argc-1)
1885         {
1886                 com_modified = true;
1887                 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1888         }
1889
1890 //
1891 // -path <dir or packfile> [<dir or packfile>] ...
1892 // Fully specifies the exact search path, overriding the generated one
1893 //
1894         i = COM_CheckParm ("-path");
1895         if (i)
1896         {
1897                 com_modified = true;
1898                 com_searchpaths = NULL;
1899                 while (++i < com_argc)
1900                 {
1901                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1902                                 break;
1903                         
1904                         search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1905                         if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1906                         {
1907                                 search->pack = COM_LoadPackFile (com_argv[i]);
1908                                 if (!search->pack)
1909                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1910                         }
1911                         else
1912                                 strcpy (search->filename, com_argv[i]);
1913                         search->next = com_searchpaths;
1914                         com_searchpaths = search;
1915                 }
1916         }
1917
1918         if (COM_CheckParm ("-proghack"))
1919                 proghack = true;
1920 }
1921
1922 int COM_FileExists(char *filename)
1923 {
1924         searchpath_t    *search;
1925         char                    netpath[MAX_OSPATH];
1926         pack_t                  *pak;
1927         int                             i;
1928         int                             findtime;
1929
1930         for (search = com_searchpaths;search;search = search->next)
1931         {
1932                 if (search->pack)
1933                 {
1934                         pak = search->pack;
1935                         for (i = 0;i < pak->numfiles;i++)
1936                                 if (!strcmp (pak->files[i].name, filename))
1937                                         return true;
1938                 }
1939                 else
1940                 {
1941                         sprintf (netpath, "%s/%s",search->filename, filename);               
1942                         findtime = Sys_FileTime (netpath);
1943                         if (findtime != -1)
1944                                 return true;
1945                 }               
1946         }
1947
1948         return false;
1949 }
1950
1951
1952 //======================================
1953 // LordHavoc: added these because they are useful
1954
1955 void COM_ToLowerString(char *in, char *out)
1956 {
1957         while (*in)
1958         {
1959                 if (*in >= 'A' && *in <= 'Z')
1960                         *out++ = *in++ + 'a' - 'A';
1961                 else
1962                         *out++ = *in++;
1963         }
1964 }
1965
1966 void COM_ToUpperString(char *in, char *out)
1967 {
1968         while (*in)
1969         {
1970                 if (*in >= 'a' && *in <= 'z')
1971                         *out++ = *in++ + 'A' - 'a';
1972                 else
1973                         *out++ = *in++;
1974         }
1975 }