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