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