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