a39cc5e512e57f592a01c865ed405b603f63d671
[xonotic/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                         {
1602                                 sprintf (netpath, "%s/%s",search->filename, gzfilename);
1603                                 findtime = Sys_FileTime (netpath);
1604                                 if (findtime == -1)
1605                                         continue;
1606                         }
1607                                 
1608 #if CACHEENABLE
1609                         // see if the file needs to be updated in the cache
1610                         if (com_cachedir[0])
1611                         {       
1612 #if defined(_WIN32)
1613                                 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1614                                         sprintf (cachepath,"%s%s", com_cachedir, netpath);
1615                                 else
1616                                         sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1617 #else
1618                                 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1619 #endif
1620
1621                                 cachetime = Sys_FileTime (cachepath);
1622                         
1623                                 if (cachetime < findtime)
1624                                         COM_CopyFile (netpath, cachepath);
1625                                 strcpy (netpath, cachepath);
1626                         }       
1627 #endif
1628
1629                         if (!quiet)
1630                                 Sys_Printf ("FindFile: %s\n",netpath);
1631                         *file = COM_OpenRead (netpath, -1, -1, zip);
1632                         return com_filesize;
1633                 }
1634                 
1635         }
1636         
1637         if (!quiet)
1638                 Sys_Printf ("FindFile: can't find %s\n", filename);
1639         
1640         *file = NULL;
1641         com_filesize = -1;
1642         return -1;
1643 }
1644
1645
1646 /*
1647 ===========
1648 COM_FOpenFile
1649
1650 If the requested file is inside a packfile, a new QFile * will be opened
1651 into the file.
1652 ===========
1653 */
1654 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1655 {
1656         return COM_FindFile (filename, file, quiet, zip);
1657 }
1658
1659
1660 /*
1661 ============
1662 COM_LoadFile
1663
1664 Filename are reletive to the quake directory.
1665 Always appends a 0 byte.
1666 ============
1667 */
1668 cache_user_t    *loadcache;
1669 byte                    *loadbuf;
1670 int                             loadsize;
1671 byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
1672 {
1673         QFile             *h;
1674         byte    *buf;
1675         char    base[1024];
1676         int             len;
1677
1678         buf = NULL;     // quiet compiler warning
1679         loadsize = 0;
1680
1681 // look for it in the filesystem or pack files
1682         len = COM_FOpenFile (path, &h, quiet, true);
1683         if (!h)
1684                 return NULL;
1685
1686         loadsize = len;
1687         
1688 // extract the filename base name for hunk tag
1689         COM_FileBase (path, base);
1690         
1691         switch (usehunk)
1692         {
1693         case 1:
1694                 buf = Hunk_AllocName (len+1, va("%s (file)", path));
1695                 if (!buf)
1696                         Sys_Error ("COM_LoadFile: not enough hunk space for %s (size %i)", path, len);
1697                 break;
1698 //      case 0:
1699 //              buf = Z_Malloc (len+1);
1700 //              if (!buf)
1701 //                      Sys_Error ("COM_LoadFile: not enough zone space for %s (size %i)", path, len);
1702 //              break;
1703 //      case 3:
1704 //              buf = Cache_Alloc (loadcache, len+1, base);
1705 //              if (!buf)
1706 //                      Sys_Error ("COM_LoadFile: not enough cache space for %s (size %i)", path, len);
1707 //              break;
1708         case 5:
1709                 buf = qmalloc (len+1);
1710                 if (!buf)
1711                         Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1712                 break;
1713         default:
1714                 Sys_Error ("COM_LoadFile: bad usehunk");
1715                 break;
1716         }
1717
1718         ((byte *)buf)[len] = 0;
1719
1720         Qread (h, buf, len);                     
1721         Qclose (h);
1722
1723         return buf;
1724 }
1725
1726 byte *COM_LoadHunkFile (char *path, qboolean quiet)
1727 {
1728         return COM_LoadFile (path, 1, quiet);
1729 }
1730
1731 // LordHavoc: returns malloc'd memory
1732 byte *COM_LoadMallocFile (char *path, qboolean quiet)
1733 {
1734         return COM_LoadFile (path, 5, quiet);
1735 }
1736
1737 /*
1738 void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
1739 {
1740         loadcache = cu;
1741         COM_LoadFile (path, 3, quiet);
1742 }
1743 */
1744
1745 /*
1746 =================
1747 COM_LoadPackFile
1748
1749 Takes an explicit (not game tree related) path to a pak file.
1750
1751 Loads the header and directory, adding the files at the beginning
1752 of the list so they override previous pack files.
1753 =================
1754 */
1755 pack_t *COM_LoadPackFile (char *packfile)
1756 {
1757         dpackheader_t   header;
1758         int                             i;
1759         packfile_t              *newfiles;
1760         int                             numpackfiles;
1761         pack_t                  *pack;
1762         int                             packhandle;
1763         // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
1764         dpackfile_t             *info;
1765         unsigned short          crc;
1766
1767         if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1768         {
1769 //              Con_Printf ("Couldn't open %s\n", packfile);
1770                 return NULL;
1771         }
1772         Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1773         if (header.id[0] != 'P' || header.id[1] != 'A'
1774         || header.id[2] != 'C' || header.id[3] != 'K')
1775                 Sys_Error ("%s is not a packfile", packfile);
1776         header.dirofs = LittleLong (header.dirofs);
1777         header.dirlen = LittleLong (header.dirlen);
1778
1779         numpackfiles = header.dirlen / sizeof(dpackfile_t);
1780
1781         if (numpackfiles > MAX_FILES_IN_PACK)
1782                 Sys_Error ("%s has %i files", packfile, numpackfiles);
1783
1784         if (numpackfiles != PAK0_COUNT)
1785                 com_modified = true;    // not the original file
1786
1787         newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "pack file-table");
1788
1789         info = qmalloc(sizeof(*info)*MAX_FILES_IN_PACK);
1790         Sys_FileSeek (packhandle, header.dirofs);
1791         Sys_FileRead (packhandle, (void *)info, header.dirlen);
1792
1793 // crc the directory to check for modifications
1794         CRC_Init (&crc);
1795         // LordHavoc: speedup
1796         CRC_ProcessBytes(&crc, (byte *)info, header.dirlen);
1797 //      for (i=0 ; i<header.dirlen ; i++)
1798 //              CRC_ProcessByte (&crc, ((byte *)info)[i]);
1799         if (crc != PAK0_CRC)
1800                 com_modified = true;
1801
1802 // parse the directory
1803         for (i=0 ; i<numpackfiles ; i++)
1804         {
1805                 strcpy (newfiles[i].name, info[i].name);
1806                 newfiles[i].filepos = LittleLong(info[i].filepos);
1807                 newfiles[i].filelen = LittleLong(info[i].filelen);
1808         }
1809         qfree(info);
1810
1811         pack = Hunk_AllocName (sizeof (pack_t), packfile);
1812         strcpy (pack->filename, packfile);
1813         pack->handle = packhandle;
1814         pack->numfiles = numpackfiles;
1815         pack->files = newfiles;
1816         
1817         Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1818         return pack;
1819 }
1820
1821
1822 /*
1823 ================
1824 COM_AddGameDirectory
1825
1826 Sets com_gamedir, adds the directory to the head of the path,
1827 then loads and adds pak1.pak pak2.pak ... 
1828 ================
1829 */
1830 void COM_AddGameDirectory (char *dir)
1831 {
1832         int                             i;
1833         searchpath_t    *search;
1834         pack_t                  *pak;
1835         char                    pakfile[MAX_OSPATH];
1836
1837         strcpy (com_gamedir, dir);
1838
1839 //
1840 // add the directory to the search path
1841 //
1842         search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1843         strcpy (search->filename, dir);
1844         search->next = com_searchpaths;
1845         com_searchpaths = search;
1846
1847 //
1848 // add any pak files in the format pak0.pak pak1.pak, ...
1849 //
1850         for (i=0 ; ; i++)
1851         {
1852                 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1853                 pak = COM_LoadPackFile (pakfile);
1854                 if (!pak)
1855                         break;
1856                 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1857                 search->pack = pak;
1858                 search->next = com_searchpaths;
1859                 com_searchpaths = search;
1860         }
1861
1862 //
1863 // add the contents of the parms.txt file to the end of the command line
1864 //
1865
1866 }
1867
1868 /*
1869 ================
1870 COM_InitFilesystem
1871 ================
1872 */
1873 void COM_InitFilesystem (void)
1874 {
1875         int             i, j;
1876         char    basedir[MAX_OSPATH];
1877         searchpath_t    *search;
1878
1879 //
1880 // -basedir <path>
1881 // Overrides the system supplied base directory (under GAMENAME)
1882 //
1883         i = COM_CheckParm ("-basedir");
1884         if (i && i < com_argc-1)
1885                 strcpy (basedir, com_argv[i+1]);
1886         else
1887                 strcpy (basedir, host_parms.basedir);
1888
1889         j = strlen (basedir);
1890
1891         if (j > 0)
1892         {
1893                 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1894                         basedir[j-1] = 0;
1895         }
1896
1897 #if CACHEENABLE
1898 //
1899 // -cachedir <path>
1900 // Overrides the system supplied cache directory (NULL or /qcache)
1901 // -cachedir - will disable caching.
1902 //
1903         i = COM_CheckParm ("-cachedir");
1904         if (i && i < com_argc-1)
1905         {
1906                 if (com_argv[i+1][0] == '-')
1907                         com_cachedir[0] = 0;
1908                 else
1909                         strcpy (com_cachedir, com_argv[i+1]);
1910         }
1911         else if (host_parms.cachedir)
1912                 strcpy (com_cachedir, host_parms.cachedir);
1913         else
1914                 com_cachedir[0] = 0;
1915 #endif
1916
1917 //
1918 // start up with GAMENAME by default (id1)
1919 //
1920         COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1921
1922 #ifdef NEHAHRA
1923         COM_AddGameDirectory (va("%s/nehahra", basedir) );
1924 #else
1925         if (COM_CheckParm ("-rogue"))
1926                 COM_AddGameDirectory (va("%s/rogue", basedir) );
1927         if (COM_CheckParm ("-hipnotic"))
1928                 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1929         if (COM_CheckParm ("-nehahra"))
1930                 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1931 #endif
1932
1933 //
1934 // -game <gamedir>
1935 // Adds basedir/gamedir as an override game
1936 //
1937         i = COM_CheckParm ("-game");
1938         if (i && i < com_argc-1)
1939         {
1940                 com_modified = true;
1941                 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1942         }
1943
1944 //
1945 // -path <dir or packfile> [<dir or packfile>] ...
1946 // Fully specifies the exact search path, overriding the generated one
1947 //
1948         i = COM_CheckParm ("-path");
1949         if (i)
1950         {
1951                 com_modified = true;
1952                 com_searchpaths = NULL;
1953                 while (++i < com_argc)
1954                 {
1955                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1956                                 break;
1957                         
1958                         search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1959                         if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1960                         {
1961                                 search->pack = COM_LoadPackFile (com_argv[i]);
1962                                 if (!search->pack)
1963                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1964                         }
1965                         else
1966                                 strcpy (search->filename, com_argv[i]);
1967                         search->next = com_searchpaths;
1968                         com_searchpaths = search;
1969                 }
1970         }
1971
1972         if (COM_CheckParm ("-proghack"))
1973                 proghack = true;
1974 }
1975
1976 int COM_FileExists(char *filename)
1977 {
1978         searchpath_t    *search;
1979         char                    netpath[MAX_OSPATH];
1980         pack_t                  *pak;
1981         int                             i;
1982         int                             findtime;
1983
1984         for (search = com_searchpaths;search;search = search->next)
1985         {
1986                 if (search->pack)
1987                 {
1988                         pak = search->pack;
1989                         for (i = 0;i < pak->numfiles;i++)
1990                                 if (!strcmp (pak->files[i].name, filename))
1991                                         return true;
1992                 }
1993                 else
1994                 {
1995                         sprintf (netpath, "%s/%s",search->filename, filename);               
1996                         findtime = Sys_FileTime (netpath);
1997                         if (findtime != -1)
1998                                 return true;
1999                 }               
2000         }
2001
2002         return false;
2003 }
2004
2005
2006 //======================================
2007 // LordHavoc: added these because they are useful
2008
2009 void COM_ToLowerString(char *in, char *out)
2010 {
2011         while (*in)
2012         {
2013                 if (*in >= 'A' && *in <= 'Z')
2014                         *out++ = *in++ + 'a' - 'A';
2015                 else
2016                         *out++ = *in++;
2017         }
2018 }
2019
2020 void COM_ToUpperString(char *in, char *out)
2021 {
2022         while (*in)
2023         {
2024                 if (*in >= 'a' && *in <= 'z')
2025                         *out++ = *in++ + 'A' - 'a';
2026                 else
2027                         *out++ = *in++;
2028         }
2029 }