]> icculus.org git repositories - divverent/darkplaces.git/blob - common.c
decals added back due to popular demand, currently not at all optimized (they're...
[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 <stdlib.h>
23 #include <fcntl.h>
24 #ifdef WIN32
25 #include <io.h>
26 #else
27 #include <unistd.h>
28 #endif
29
30 #include "quakedef.h"
31
32 cvar_t registered = {0, "registered","0"};
33 cvar_t cmdline = {0, "cmdline","0"};
34
35 mempool_t *pak_mempool;
36
37 qboolean com_modified;   // set true if using non-id files
38
39 void COM_InitFilesystem (void);
40
41 char com_token[1024];
42 char com_basedir[MAX_OSPATH];
43 int com_argc;
44 const char **com_argv;
45
46 // LordHavoc: made commandline 1024 characters instead of 256
47 #define CMDLINE_LENGTH  1024
48 char com_cmdline[CMDLINE_LENGTH];
49
50 int gamemode;
51 char *gamename;
52 char *gamedirname;
53 char com_modname[MAX_OSPATH];
54
55 /*
56
57
58 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.
59
60 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
61 only used during filesystem initialization.
62
63 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.
64
65 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines.  If there is a cache directory
66 specified, when a file is found by the normal search path, it will be mirrored
67 into the cache directory, then opened there.
68
69
70
71 FIXME:
72 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.
73
74 */
75
76 //============================================================================
77
78
79 /*
80 ============================================================================
81
82                                         LIBRARY REPLACEMENT FUNCTIONS
83
84 ============================================================================
85 */
86
87 int Q_strncasecmp (const char *s1, const char *s2, int n)
88 {
89         int             c1, c2;
90
91         while (1)
92         {
93                 c1 = *s1++;
94                 c2 = *s2++;
95
96                 if (!n--)
97                         return 0;               // strings are equal until end point
98
99                 if (c1 != c2)
100                 {
101                         if (c1 >= 'a' && c1 <= 'z')
102                                 c1 -= ('a' - 'A');
103                         if (c2 >= 'a' && c2 <= 'z')
104                                 c2 -= ('a' - 'A');
105                         if (c1 != c2)
106                                 return -1;              // strings not equal
107                 }
108                 if (!c1)
109                         return 0;               // strings are equal
110         }
111
112         return -1;
113 }
114
115 int Q_strcasecmp (const char *s1, const char *s2)
116 {
117         return Q_strncasecmp (s1, s2, 99999);
118 }
119
120 /*
121 ============================================================================
122
123                                         BYTE ORDER FUNCTIONS
124
125 ============================================================================
126 */
127
128 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
129 short   (*BigShort) (short l);
130 short   (*LittleShort) (short l);
131 int     (*BigLong) (int l);
132 int     (*LittleLong) (int l);
133 float   (*BigFloat) (float l);
134 float   (*LittleFloat) (float l);
135 #endif
136
137 short   ShortSwap (short l)
138 {
139         qbyte    b1,b2;
140
141         b1 = l&255;
142         b2 = (l>>8)&255;
143
144         return (b1<<8) + b2;
145 }
146
147 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
148 short   ShortNoSwap (short l)
149 {
150         return l;
151 }
152 #endif
153
154 int    LongSwap (int l)
155 {
156         qbyte    b1,b2,b3,b4;
157
158         b1 = l&255;
159         b2 = (l>>8)&255;
160         b3 = (l>>16)&255;
161         b4 = (l>>24)&255;
162
163         return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
164 }
165
166 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
167 int     LongNoSwap (int l)
168 {
169         return l;
170 }
171 #endif
172
173 float FloatSwap (float f)
174 {
175         union
176         {
177                 float   f;
178                 qbyte    b[4];
179         } dat1, dat2;
180
181
182         dat1.f = f;
183         dat2.b[0] = dat1.b[3];
184         dat2.b[1] = dat1.b[2];
185         dat2.b[2] = dat1.b[1];
186         dat2.b[3] = dat1.b[0];
187         return dat2.f;
188 }
189
190 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
191 float FloatNoSwap (float f)
192 {
193         return f;
194 }
195 #endif
196
197 /*
198 ==============================================================================
199
200                         MESSAGE IO FUNCTIONS
201
202 Handles byte ordering and avoids alignment errors
203 ==============================================================================
204 */
205
206 //
207 // writing functions
208 //
209
210 void MSG_WriteChar (sizebuf_t *sb, int c)
211 {
212         qbyte    *buf;
213         
214         buf = SZ_GetSpace (sb, 1);
215         buf[0] = c;
216 }
217
218 void MSG_WriteByte (sizebuf_t *sb, int c)
219 {
220         qbyte    *buf;
221         
222         buf = SZ_GetSpace (sb, 1);
223         buf[0] = c;
224 }
225
226 void MSG_WriteShort (sizebuf_t *sb, int c)
227 {
228         qbyte    *buf;
229
230         buf = SZ_GetSpace (sb, 2);
231         buf[0] = c&0xff;
232         buf[1] = c>>8;
233 }
234
235 void MSG_WriteLong (sizebuf_t *sb, int c)
236 {
237         qbyte    *buf;
238
239         buf = SZ_GetSpace (sb, 4);
240         buf[0] = c&0xff;
241         buf[1] = (c>>8)&0xff;
242         buf[2] = (c>>16)&0xff;
243         buf[3] = c>>24;
244 }
245
246 void MSG_WriteFloat (sizebuf_t *sb, float f)
247 {
248         union
249         {
250                 float   f;
251                 int     l;
252         } dat;
253
254
255         dat.f = f;
256         dat.l = LittleLong (dat.l);
257
258         SZ_Write (sb, &dat.l, 4);
259 }
260
261 void MSG_WriteString (sizebuf_t *sb, const char *s)
262 {
263         if (!s)
264                 SZ_Write (sb, "", 1);
265         else
266                 SZ_Write (sb, s, strlen(s)+1);
267 }
268
269 // used by server (always latest dpprotocol)
270 void MSG_WriteDPCoord (sizebuf_t *sb, float f)
271 {
272         if (f >= 0)
273                 MSG_WriteShort (sb, (int)(f + 0.5f));
274         else
275                 MSG_WriteShort (sb, (int)(f - 0.5f));
276 }
277
278 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
279 {
280         if (f >= 0)
281                 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
282         else
283                 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
284 }
285
286 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
287 void MSG_WriteAngle (sizebuf_t *sb, float f)
288 {
289         if (f >= 0)
290                 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
291         else
292                 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
293 }
294
295 //
296 // reading functions
297 //
298 int msg_readcount;
299 qboolean msg_badread;
300
301 void MSG_BeginReading (void)
302 {
303         msg_readcount = 0;
304         msg_badread = false;
305 }
306
307 int MSG_ReadShort (void)
308 {
309         int c;
310
311         if (msg_readcount+2 > net_message.cursize)
312         {
313                 msg_badread = true;
314                 return -1;
315         }
316
317         c = (short)(net_message.data[msg_readcount]
318         + (net_message.data[msg_readcount+1]<<8));
319
320         msg_readcount += 2;
321
322         return c;
323 }
324
325 int MSG_ReadLong (void)
326 {
327         int c;
328
329         if (msg_readcount+4 > net_message.cursize)
330         {
331                 msg_badread = true;
332                 return -1;
333         }
334
335         c = net_message.data[msg_readcount]
336         + (net_message.data[msg_readcount+1]<<8)
337         + (net_message.data[msg_readcount+2]<<16)
338         + (net_message.data[msg_readcount+3]<<24);
339
340         msg_readcount += 4;
341
342         return c;
343 }
344
345 float MSG_ReadFloat (void)
346 {
347         union
348         {
349                 qbyte b[4];
350                 float f;
351                 int l;
352         } dat;
353
354         dat.b[0] =      net_message.data[msg_readcount];
355         dat.b[1] =      net_message.data[msg_readcount+1];
356         dat.b[2] =      net_message.data[msg_readcount+2];
357         dat.b[3] =      net_message.data[msg_readcount+3];
358         msg_readcount += 4;
359
360         dat.l = LittleLong (dat.l);
361
362         return dat.f;
363 }
364
365 char *MSG_ReadString (void)
366 {
367         static char string[2048];
368         int l,c;
369
370         l = 0;
371         do
372         {
373                 c = MSG_ReadChar ();
374                 if (c == -1 || c == 0)
375                         break;
376                 string[l] = c;
377                 l++;
378         } while (l < (int)sizeof(string)-1);
379
380         string[l] = 0;
381
382         return string;
383 }
384
385 // used by server (always latest dpprotocol)
386 float MSG_ReadDPCoord (void)
387 {
388         return (signed short) MSG_ReadShort();
389 }
390
391 // used by client
392 float MSG_ReadCoord (void)
393 {
394         if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
395                 return (signed short) MSG_ReadShort();
396         else if (dpprotocol == DPPROTOCOL_VERSION1)
397                 return MSG_ReadFloat();
398         else
399                 return MSG_ReadShort() * (1.0f/8.0f);
400 }
401
402
403 //===========================================================================
404
405 void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name)
406 {
407         if (startsize < 256)
408                 startsize = 256;
409         buf->mempool = Mem_AllocPool(name);
410         buf->data = Mem_Alloc(buf->mempool, startsize);
411         buf->maxsize = startsize;
412         buf->cursize = 0;
413 }
414
415
416 void SZ_Free (sizebuf_t *buf)
417 {
418         Mem_FreePool(&buf->mempool);
419         buf->data = NULL;
420         buf->maxsize = 0;
421         buf->cursize = 0;
422 }
423
424 void SZ_Clear (sizebuf_t *buf)
425 {
426         buf->cursize = 0;
427 }
428
429 void *SZ_GetSpace (sizebuf_t *buf, int length)
430 {
431         void *data;
432
433         if (buf->cursize + length > buf->maxsize)
434         {
435                 if (!buf->allowoverflow)
436                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
437
438                 if (length > buf->maxsize)
439                         Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
440
441                 buf->overflowed = true;
442                 Con_Printf ("SZ_GetSpace: overflow\n");
443                 SZ_Clear (buf);
444         }
445
446         data = buf->data + buf->cursize;
447         buf->cursize += length;
448
449         return data;
450 }
451
452 void SZ_Write (sizebuf_t *buf, const void *data, int length)
453 {
454         memcpy (SZ_GetSpace(buf,length),data,length);
455 }
456
457 void SZ_Print (sizebuf_t *buf, const char *data)
458 {
459         int len;
460         len = strlen(data)+1;
461
462 // byte * cast to keep VC++ happy
463         if (buf->data[buf->cursize-1])
464                 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
465         else
466                 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
467 }
468
469 static char *hexchar = "0123456789ABCDEF";
470 void SZ_HexDumpToConsole(const sizebuf_t *buf)
471 {
472         int i;
473         char text[1024];
474         char *cur, *flushpointer;
475         cur = text;
476         flushpointer = text + 512;
477         for (i = 0;i < buf->cursize;i++)
478         {
479                 if ((i & 15) == 0)
480                 {
481                         *cur++ = hexchar[(i >> 12) & 15];
482                         *cur++ = hexchar[(i >>  8) & 15];
483                         *cur++ = hexchar[(i >>  4) & 15];
484                         *cur++ = hexchar[(i >>  0) & 15];
485                         *cur++ = ':';
486                         *cur++ = ' ';
487                 }
488                 else if ((i & 15) == 15)
489                         *cur++ = '\n';
490                 else
491                         *cur++ = ' ';
492                 if (i & 1)
493                 {
494                         *cur++ = hexchar[(buf->data[i] >> 4) & 15] | 0x80;
495                         *cur++ = hexchar[(buf->data[i] >> 0) & 15] | 0x80;
496                 }
497                 else
498                 {
499                         *cur++ = hexchar[(buf->data[i] >> 4) & 15];
500                         *cur++ = hexchar[(buf->data[i] >> 0) & 15];
501                 }
502                 if (cur >= flushpointer)
503                 {
504                         *cur++ = 0;
505                         Con_Printf("%s", text);
506                         cur = text;
507                 }
508         }
509         if ((i & 15) != 0)
510                 *cur++ = '\n';
511         if (cur > text)
512         {
513                 *cur++ = 0;
514                 Con_Printf("%s", text);
515         }
516 }
517
518
519 //============================================================================
520
521
522 /*
523 ============
524 COM_StripExtension
525 ============
526 */
527 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
528 void COM_StripExtension (const char *in, char *out)
529 {
530         char *last = NULL;
531         while (*in)
532         {
533                 if (*in == '.')
534                         last = out;
535                 else if (*in == '/' || *in == '\\' || *in == ':')
536                         last = NULL;
537                 *out++ = *in++;
538         }
539         if (last)
540                 *last = 0;
541         else
542                 *out = 0;
543 }
544
545 /*
546 ============
547 COM_FileExtension
548 ============
549 */
550 char *COM_FileExtension (const char *in)
551 {
552         static char exten[8];
553         int i;
554
555         while (*in && *in != '.')
556                 in++;
557         if (!*in)
558                 return "";
559         in++;
560         for (i=0 ; i<7 && *in ; i++,in++)
561                 exten[i] = *in;
562         exten[i] = 0;
563         return exten;
564 }
565
566 /*
567 ============
568 COM_FileBase
569 ============
570 */
571 void COM_FileBase (const char *in, char *out)
572 {
573         const char *slash, *dot, *s;
574
575         slash = in;
576         dot = NULL;
577         s = in;
578         while(*s)
579         {
580                 if (*s == '/')
581                         slash = s + 1;
582                 if (*s == '.')
583                         dot = s;
584                 s++;
585         }
586         if (dot == NULL)
587                 dot = s;
588         if (dot - slash < 2)
589                 strcpy (out,"?model?");
590         else
591         {
592                 while (slash < dot)
593                         *out++ = *slash++;
594                 *out++ = 0;
595         }
596 }
597
598
599 /*
600 ==================
601 COM_DefaultExtension
602 ==================
603 */
604 void COM_DefaultExtension (char *path, const char *extension)
605 {
606         const char *src;
607 //
608 // if path doesn't have a .EXT, append extension
609 // (extension should include the .)
610 //
611         src = path + strlen(path) - 1;
612
613         while (*src != '/' && src != path)
614         {
615                 if (*src == '.')
616                         return;                 // it has an extension
617                 src--;
618         }
619
620         strcat (path, extension);
621 }
622
623
624 /*
625 ==============
626 COM_ParseToken
627
628 Parse a token out of a string
629 ==============
630 */
631 int COM_ParseToken (const char **datapointer)
632 {
633         int c;
634         int len;
635         const char *data = *datapointer;
636
637         len = 0;
638         com_token[0] = 0;
639
640         if (!data)
641         {
642                 *datapointer = NULL;
643                 return false;
644         }
645
646 // skip whitespace
647 skipwhite:
648         while ((c = *data) <= ' ')
649         {
650                 if (c == 0)
651                 {
652                         // end of file
653                         *datapointer = NULL;
654                         return false;
655                 }
656                 data++;
657         }
658
659 // skip // comments
660         if (c=='/' && data[1] == '/')
661         {
662                 while (*data && *data != '\n')
663                         data++;
664                 goto skipwhite;
665         }
666
667
668 // handle quoted strings specially
669         if (c == '\"')
670         {
671                 data++;
672                 while (1)
673                 {
674                         c = *data++;
675                         if (c=='\"' || !c)
676                         {
677                                 com_token[len] = 0;
678                                 *datapointer = data;
679                                 return true;
680                         }
681                         com_token[len] = c;
682                         len++;
683                 }
684         }
685
686 // parse single characters
687         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
688         {
689                 com_token[len] = c;
690                 len++;
691                 com_token[len] = 0;
692                 *datapointer = data+1;
693                 return true;
694         }
695
696 // parse a regular word
697         do
698         {
699                 com_token[len] = c;
700                 data++;
701                 len++;
702                 c = *data;
703                 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
704                         break;
705         } while (c>32);
706
707         com_token[len] = 0;
708         *datapointer = data;
709         return true;
710 }
711
712
713 /*
714 ================
715 COM_CheckParm
716
717 Returns the position (1 to argc-1) in the program's argument list
718 where the given parameter apears, or 0 if not present
719 ================
720 */
721 int COM_CheckParm (const char *parm)
722 {
723         int i;
724
725         for (i=1 ; i<com_argc ; i++)
726         {
727                 if (!com_argv[i])
728                         continue;               // NEXTSTEP sometimes clears appkit vars.
729                 if (!strcmp (parm,com_argv[i]))
730                         return i;
731         }
732
733         return 0;
734 }
735
736 /*
737 ================
738 COM_CheckRegistered
739
740 Looks for the pop.txt file and verifies it.
741 Sets the "registered" cvar.
742 Immediately exits out if an alternate game was attempted to be started without
743 being registered.
744 ================
745 */
746 void COM_CheckRegistered (void)
747 {
748         Cvar_Set ("cmdline", com_cmdline);
749
750         if (!Sys_FileTime("gfx/pop.lmp"))
751         {
752                 if (com_modified)
753                         Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
754                 else
755                         Con_Printf ("Playing shareware version.\n");
756                 return;
757         }
758
759         Cvar_Set ("registered", "1");
760         Con_Printf ("Playing registered version.\n");
761 }
762
763
764 void COM_Path_f (void);
765
766
767 /*
768 ================
769 COM_InitArgv
770 ================
771 */
772 void COM_InitArgv (void)
773 {
774         int i, j, n;
775         // reconstitute the command line for the cmdline externally visible cvar
776         n = 0;
777         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
778         {
779                 i = 0;
780                 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
781                         com_cmdline[n++] = com_argv[j][i++];
782                 if (n < (CMDLINE_LENGTH - 1))
783                         com_cmdline[n++] = ' ';
784                 else
785                         break;
786         }
787         com_cmdline[n] = 0;
788 }
789
790 void COM_InitGameType (void)
791 {
792         char name[MAX_OSPATH];
793         COM_StripExtension(com_argv[0], name);
794         COM_ToLowerString(name, name);
795
796         if (strstr(name, "transfusion"))
797                 gamemode = GAME_TRANSFUSION;
798         else if (strstr(name, "nexiuz"))
799                 gamemode = GAME_NEXIUZ;
800         else if (strstr(name, "nehahra"))
801                 gamemode = GAME_NEHAHRA;
802         else if (strstr(name, "hipnotic"))
803                 gamemode = GAME_HIPNOTIC;
804         else if (strstr(name, "rogue"))
805                 gamemode = GAME_ROGUE;
806         else
807                 gamemode = GAME_NORMAL;
808
809         if (COM_CheckParm ("-transfusion"))
810                 gamemode = GAME_TRANSFUSION;
811         else if (COM_CheckParm ("-nexiuz"))
812                 gamemode = GAME_NEXIUZ;
813         else if (COM_CheckParm ("-nehahra"))
814                 gamemode = GAME_NEHAHRA;
815         else if (COM_CheckParm ("-hipnotic"))
816                 gamemode = GAME_HIPNOTIC;
817         else if (COM_CheckParm ("-rogue"))
818                 gamemode = GAME_ROGUE;
819         else if (COM_CheckParm ("-quake"))
820                 gamemode = GAME_NORMAL;
821
822         switch(gamemode)
823         {
824         case GAME_NORMAL:
825                 gamename = "DarkPlaces-Quake";
826                 gamedirname = "";
827                 break;
828         case GAME_HIPNOTIC:
829                 gamename = "Darkplaces-Hipnotic";
830                 gamedirname = "hipnotic";
831                 break;
832         case GAME_ROGUE:
833                 gamename = "Darkplaces-Rogue";
834                 gamedirname = "rogue";
835                 break;
836         case GAME_NEHAHRA:
837                 gamename = "DarkPlaces-Nehahra";
838                 gamedirname = "nehahra";
839                 break;
840         case GAME_NEXIUZ:
841                 gamename = "Nexiuz";
842                 gamedirname = "data";
843                 break;
844         case GAME_TRANSFUSION:
845                 gamename = "Transfusion";
846                 gamedirname = "transfusion";
847                 break;
848         default:
849                 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
850                 break;
851         }
852 }
853
854
855 extern void Mathlib_Init(void);
856
857 /*
858 ================
859 COM_Init
860 ================
861 */
862 void COM_Init (void)
863 {
864 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
865         qbyte swaptest[2] = {1,0};
866
867 // set the byte swapping variables in a portable manner
868         if ( *(short *)swaptest == 1)
869         {
870                 BigShort = ShortSwap;
871                 LittleShort = ShortNoSwap;
872                 BigLong = LongSwap;
873                 LittleLong = LongNoSwap;
874                 BigFloat = FloatSwap;
875                 LittleFloat = FloatNoSwap;
876         }
877         else
878         {
879                 BigShort = ShortNoSwap;
880                 LittleShort = ShortSwap;
881                 BigLong = LongNoSwap;
882                 LittleLong = LongSwap;
883                 BigFloat = FloatNoSwap;
884                 LittleFloat = FloatSwap;
885         }
886 #endif
887
888         pak_mempool = Mem_AllocPool("paks");
889
890         Cvar_RegisterVariable (&registered);
891         Cvar_RegisterVariable (&cmdline);
892         Cmd_AddCommand ("path", COM_Path_f);
893
894         Mathlib_Init();
895
896         COM_InitFilesystem ();
897         COM_CheckRegistered ();
898
899         COM_InitGameType();
900 }
901
902
903 /*
904 ============
905 va
906
907 does a varargs printf into a temp buffer, so I don't need to have
908 varargs versions of all text functions.
909 FIXME: make this buffer size safe someday
910 ============
911 */
912 char *va(const char *format, ...)
913 {
914         va_list argptr;
915         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
916         static char string[8][1024], *s;
917         static int stringindex = 0;
918
919         s = string[stringindex];
920         stringindex = (stringindex + 1) & 7;
921         va_start (argptr, format);
922         vsprintf (s, format,argptr);
923         va_end (argptr);
924
925         return s;
926 }
927
928
929 /*
930 =============================================================================
931
932 QUAKE FILESYSTEM
933
934 =============================================================================
935 */
936
937 int com_filesize;
938
939
940 //
941 // in memory
942 //
943
944 typedef struct
945 {
946         char name[MAX_QPATH];
947         int filepos, filelen;
948 } packfile_t;
949
950 typedef struct pack_s
951 {
952         char filename[MAX_OSPATH];
953         int handle;
954         int numfiles;
955         packfile_t *files;
956         mempool_t *mempool;
957         struct pack_s *next;
958 } pack_t;
959
960 //
961 // on disk
962 //
963 typedef struct
964 {
965         char name[56];
966         int filepos, filelen;
967 } dpackfile_t;
968
969 typedef struct
970 {
971         char id[4];
972         int dirofs;
973         int dirlen;
974 } dpackheader_t;
975
976 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
977 #define MAX_FILES_IN_PACK       65536
978
979 pack_t *packlist = NULL;
980
981 #if CACHEENABLE
982 char com_cachedir[MAX_OSPATH];
983 #endif
984 char com_gamedir[MAX_OSPATH];
985
986 typedef struct searchpath_s
987 {
988         // only one of filename / pack will be used
989         char filename[MAX_OSPATH];
990         pack_t *pack;
991         struct searchpath_s *next;
992 } searchpath_t;
993
994 searchpath_t *com_searchpaths;
995
996 /*
997 ============
998 COM_Path_f
999
1000 ============
1001 */
1002 void COM_Path_f (void)
1003 {
1004         searchpath_t *s;
1005
1006         Con_Printf ("Current search path:\n");
1007         for (s=com_searchpaths ; s ; s=s->next)
1008         {
1009                 if (s->pack)
1010                 {
1011                         Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1012                 }
1013                 else
1014                         Con_Printf ("%s\n", s->filename);
1015         }
1016 }
1017
1018 /*
1019 ============
1020 COM_CreatePath
1021
1022 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1023 ============
1024 */
1025 void    COM_CreatePath (char *path)
1026 {
1027         char *ofs, save;
1028
1029         for (ofs = path+1 ; *ofs ; ofs++)
1030         {
1031                 if (*ofs == '/' || *ofs == '\\')
1032                 {
1033                         // create the directory
1034                         save = *ofs;
1035                         *ofs = 0;
1036                         Sys_mkdir (path);
1037                         *ofs = save;
1038                 }
1039         }
1040 }
1041
1042
1043 /*
1044 ============
1045 COM_WriteFile
1046
1047 The filename will be prefixed by the current game directory
1048 ============
1049 */
1050 qboolean COM_WriteFile (const char *filename, void *data, int len)
1051 {
1052         int handle;
1053         char name[MAX_OSPATH];
1054
1055         sprintf (name, "%s/%s", com_gamedir, filename);
1056
1057         // LordHavoc: added this
1058         // create directories up to the file
1059         COM_CreatePath (name);
1060
1061         handle = Sys_FileOpenWrite (name);
1062         if (handle == -1)
1063         {
1064                 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1065                 return false;
1066         }
1067
1068         Con_DPrintf ("COM_WriteFile: %s\n", name);
1069         Sys_FileWrite (handle, data, len);
1070         Sys_FileClose (handle);
1071         return true;
1072 }
1073
1074
1075 /*
1076 ===========
1077 COM_CopyFile
1078
1079 Copies a file over from the net to the local cache, creating any directories
1080 needed.  This is for the convenience of developers using ISDN from home.
1081 ===========
1082 */
1083 void COM_CopyFile (char *netpath, char *cachepath)
1084 {
1085         int in, out, remaining, count;
1086         char buf[4096];
1087
1088         remaining = Sys_FileOpenRead (netpath, &in);
1089         COM_CreatePath (cachepath);     // create directories up to the cache file
1090         out = Sys_FileOpenWrite (cachepath);
1091
1092         while (remaining)
1093         {
1094                 if (remaining < (int)sizeof(buf))
1095                         count = remaining;
1096                 else
1097                         count = sizeof(buf);
1098                 Sys_FileRead (in, buf, count);
1099                 Sys_FileWrite (out, buf, count);
1100                 remaining -= count;
1101         }
1102
1103         Sys_FileClose (in);
1104         Sys_FileClose (out);
1105 }
1106
1107 /*
1108 ===========
1109 COM_OpenRead
1110 ===========
1111 */
1112 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1113 {
1114         int fd = open (path, O_RDONLY);
1115         unsigned char id[2], len_bytes[4];
1116
1117         if (fd == -1)
1118         {
1119                 Sys_Error ("Couldn't open %s", path);
1120                 return 0;
1121         }
1122         if (offs < 0 || len < 0)
1123         {
1124                 // normal file
1125                 offs = 0;
1126                 len = lseek (fd, 0, SEEK_END);
1127                 lseek (fd, 0, SEEK_SET);
1128         }
1129         lseek (fd, offs, SEEK_SET);
1130         if (zip)
1131         {
1132                 read (fd, id, 2);
1133                 if (id[0] == 0x1f && id[1] == 0x8b)
1134                 {
1135                         lseek (fd, offs + len - 4, SEEK_SET);
1136                         read (fd, len_bytes, 4);
1137                         len = ((len_bytes[3] << 24)
1138                                    | (len_bytes[2] << 16)
1139                                    | (len_bytes[1] << 8)
1140                                    | (len_bytes[0]));
1141                 }
1142         }
1143         lseek (fd, offs, SEEK_SET);
1144         com_filesize = len;
1145
1146 #ifdef WIN32
1147         setmode (fd, O_BINARY);
1148 #endif
1149         if (zip)
1150                 return Qdopen (fd, "rbz");
1151         else
1152                 return Qdopen (fd, "rb");
1153 }
1154
1155 /*
1156 ===========
1157 COM_FindFile
1158
1159 Finds the file in the search path.
1160 Sets com_filesize and one of handle or file
1161 ===========
1162 */
1163 int COM_FindFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1164 {
1165         searchpath_t *search;
1166         char netpath[MAX_OSPATH];
1167 #if CACHEENABLE
1168         char cachepath[MAX_OSPATH];
1169         int cachetime;
1170 #endif
1171         pack_t *pak;
1172         int i, findtime, filenamelen;
1173         char gzfilename[MAX_OSPATH];
1174
1175         filenamelen = strlen (filename);
1176         sprintf (gzfilename, "%s.gz", filename);
1177
1178         if (!file)
1179                 Sys_Error ("COM_FindFile: file not set");
1180
1181 //
1182 // search through the path, one element at a time
1183 //
1184         search = com_searchpaths;
1185
1186         for ( ; search ; search = search->next)
1187         {
1188         // is the element a pak file?
1189                 if (search->pack)
1190                 {
1191                 // look through all the pak file elements
1192                         pak = search->pack;
1193                         for (i=0 ; i<pak->numfiles ; i++)
1194                                 if (!strcmp (pak->files[i].name, filename)
1195                                     || !strcmp (pak->files[i].name, gzfilename))
1196                                 {       // found it!
1197                                         if (!quiet)
1198                                                 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1199                                         // open a new file on the pakfile
1200                                         *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1201                                         return com_filesize;
1202                                 }
1203                 }
1204                 else
1205                 {
1206                         sprintf (netpath, "%s/%s",search->filename, filename);
1207
1208                         findtime = Sys_FileTime (netpath);
1209                         if (findtime == -1)
1210                                 continue;
1211
1212 #if CACHEENABLE
1213                         // see if the file needs to be updated in the cache
1214                         if (com_cachedir[0])
1215                         {
1216 #if defined(_WIN32)
1217                                 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1218                                         sprintf (cachepath,"%s%s", com_cachedir, netpath);
1219                                 else
1220                                         sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1221 #else
1222                                 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1223 #endif
1224
1225                                 cachetime = Sys_FileTime (cachepath);
1226
1227                                 if (cachetime < findtime)
1228                                         COM_CopyFile (netpath, cachepath);
1229                                 strcpy (netpath, cachepath);
1230                         }
1231 #endif
1232                         if (!quiet)
1233                                 Sys_Printf ("FindFile: %s\n",netpath);
1234                         *file = COM_OpenRead (netpath, -1, -1, zip);
1235                         return com_filesize;
1236                 }
1237         }
1238
1239         if (!quiet)
1240                 Sys_Printf ("FindFile: can't find %s\n", filename);
1241
1242         *file = NULL;
1243         com_filesize = -1;
1244         return -1;
1245 }
1246
1247
1248 /*
1249 ===========
1250 COM_FOpenFile
1251
1252 If the requested file is inside a packfile, a new QFile * will be opened
1253 into the file.
1254 ===========
1255 */
1256 int COM_FOpenFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1257 {
1258         return COM_FindFile (filename, file, quiet, zip);
1259 }
1260
1261
1262 /*
1263 ============
1264 COM_LoadFile
1265
1266 Filename are reletive to the quake directory.
1267 Always appends a 0 byte.
1268 ============
1269 */
1270 qbyte *loadbuf;
1271 int loadsize;
1272 qbyte *COM_LoadFile (const char *path, qboolean quiet)
1273 {
1274         QFile *h;
1275         qbyte *buf;
1276         char base[1024];
1277         int len;
1278
1279         buf = NULL;     // quiet compiler warning
1280         loadsize = 0;
1281
1282 // look for it in the filesystem or pack files
1283         len = COM_FOpenFile (path, &h, quiet, true);
1284         if (!h)
1285                 return NULL;
1286
1287         loadsize = len;
1288
1289 // extract the filename base name for hunk tag
1290         COM_FileBase (path, base);
1291
1292         buf = Mem_Alloc(tempmempool, len+1);
1293         if (!buf)
1294                 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1295
1296         ((qbyte *)buf)[len] = 0;
1297
1298         Qread (h, buf, len);
1299         Qclose (h);
1300
1301         return buf;
1302 }
1303
1304 /*
1305 =================
1306 COM_LoadPackFile
1307
1308 Takes an explicit (not game tree related) path to a pak file.
1309
1310 Loads the header and directory, adding the files at the beginning
1311 of the list so they override previous pack files.
1312 =================
1313 */
1314 pack_t *COM_LoadPackFile (const char *packfile)
1315 {
1316         dpackheader_t header;
1317         int i, numpackfiles, packhandle;
1318         pack_t *pack;
1319         // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1320         dpackfile_t *info;
1321
1322         if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1323                 return NULL;
1324
1325         Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1326         if (memcmp(header.id, "PACK", 4))
1327                 Sys_Error ("%s is not a packfile", packfile);
1328         header.dirofs = LittleLong (header.dirofs);
1329         header.dirlen = LittleLong (header.dirlen);
1330
1331         if (header.dirlen % sizeof(dpackfile_t))
1332                 Sys_Error ("%s has an invalid directory size", packfile);
1333
1334         numpackfiles = header.dirlen / sizeof(dpackfile_t);
1335
1336         if (numpackfiles > MAX_FILES_IN_PACK)
1337                 Sys_Error ("%s has %i files", packfile, numpackfiles);
1338
1339         pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1340         strcpy (pack->filename, packfile);
1341         pack->handle = packhandle;
1342         pack->numfiles = numpackfiles;
1343         pack->mempool = Mem_AllocPool(packfile);
1344         pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1345         pack->next = packlist;
1346         packlist = pack;
1347
1348         info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1349         Sys_FileSeek (packhandle, header.dirofs);
1350         Sys_FileRead (packhandle, (void *)info, header.dirlen);
1351
1352 // parse the directory
1353         for (i = 0;i < numpackfiles;i++)
1354         {
1355                 strcpy (pack->files[i].name, info[i].name);
1356                 pack->files[i].filepos = LittleLong(info[i].filepos);
1357                 pack->files[i].filelen = LittleLong(info[i].filelen);
1358         }
1359
1360         Mem_Free(info);
1361
1362         Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1363         return pack;
1364 }
1365
1366
1367 /*
1368 ================
1369 COM_AddGameDirectory
1370
1371 Sets com_gamedir, adds the directory to the head of the path,
1372 then loads and adds pak1.pak pak2.pak ...
1373 ================
1374 */
1375 void COM_AddGameDirectory (char *dir)
1376 {
1377         stringlist_t *list, *current;
1378         searchpath_t *search;
1379         pack_t *pak;
1380         char pakfile[MAX_OSPATH];
1381
1382         strcpy (com_gamedir, dir);
1383
1384 //
1385 // add the directory to the search path
1386 //
1387         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1388         strcpy (search->filename, dir);
1389         search->next = com_searchpaths;
1390         com_searchpaths = search;
1391
1392         // add any paks in the directory
1393         list = listdirectory(dir);
1394         for (current = list;current;current = current->next)
1395         {
1396                 if (matchpattern(current->text, "*.pak", true))
1397                 {
1398                         sprintf (pakfile, "%s/%s", dir, current->text);
1399                         pak = COM_LoadPackFile (pakfile);
1400                         if (pak)
1401                         {
1402                                 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1403                                 search->pack = pak;
1404                                 search->next = com_searchpaths;
1405                                 com_searchpaths = search;
1406                         }
1407                         else
1408                                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1409                 }
1410         }
1411         freedirectory(list);
1412 }
1413
1414 /*
1415 ================
1416 COM_InitFilesystem
1417 ================
1418 */
1419 void COM_InitFilesystem (void)
1420 {
1421         int i;
1422         searchpath_t *search;
1423
1424         strcpy(com_basedir, ".");
1425
1426         // -basedir <path>
1427         // Overrides the system supplied base directory (under GAMENAME)
1428         i = COM_CheckParm ("-basedir");
1429         if (i && i < com_argc-1)
1430                 strcpy (com_basedir, com_argv[i+1]);
1431
1432         i = strlen (com_basedir);
1433         if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1434                 com_basedir[i-1] = 0;
1435
1436 // start up with GAMENAME by default (id1)
1437         strcpy(com_modname, GAMENAME);
1438         COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1439         if (gamedirname[0])
1440         {
1441                 com_modified = true;
1442                 strcpy(com_modname, gamedirname);
1443                 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1444         }
1445
1446         // -game <gamedir>
1447         // Adds basedir/gamedir as an override game
1448         i = COM_CheckParm ("-game");
1449         if (i && i < com_argc-1)
1450         {
1451                 com_modified = true;
1452                 strcpy(com_modname, com_argv[i+1]);
1453                 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1454         }
1455
1456         // -path <dir or packfile> [<dir or packfile>] ...
1457         // Fully specifies the exact search path, overriding the generated one
1458         i = COM_CheckParm ("-path");
1459         if (i)
1460         {
1461                 com_modified = true;
1462                 com_searchpaths = NULL;
1463                 while (++i < com_argc)
1464                 {
1465                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1466                                 break;
1467
1468                         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1469                         if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1470                         {
1471                                 search->pack = COM_LoadPackFile (com_argv[i]);
1472                                 if (!search->pack)
1473                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1474                         }
1475                         else
1476                                 strcpy (search->filename, com_argv[i]);
1477                         search->next = com_searchpaths;
1478                         com_searchpaths = search;
1479                 }
1480         }
1481 }
1482
1483 int COM_FileExists(const char *filename)
1484 {
1485         searchpath_t *search;
1486         char netpath[MAX_OSPATH];
1487         pack_t *pak;
1488         int i, findtime;
1489
1490         for (search = com_searchpaths;search;search = search->next)
1491         {
1492                 if (search->pack)
1493                 {
1494                         pak = search->pack;
1495                         for (i = 0;i < pak->numfiles;i++)
1496                                 if (!strcmp (pak->files[i].name, filename))
1497                                         return true;
1498                 }
1499                 else
1500                 {
1501                         sprintf (netpath, "%s/%s",search->filename, filename);
1502                         findtime = Sys_FileTime (netpath);
1503                         if (findtime != -1)
1504                                 return true;
1505                 }
1506         }
1507
1508         return false;
1509 }
1510
1511
1512 //======================================
1513 // LordHavoc: added these because they are useful
1514
1515 void COM_ToLowerString(const char *in, char *out)
1516 {
1517         while (*in)
1518         {
1519                 if (*in >= 'A' && *in <= 'Z')
1520                         *out++ = *in++ + 'a' - 'A';
1521                 else
1522                         *out++ = *in++;
1523         }
1524 }
1525
1526 void COM_ToUpperString(const char *in, char *out)
1527 {
1528         while (*in)
1529         {
1530                 if (*in >= 'a' && *in <= 'z')
1531                         *out++ = *in++ + 'A' - 'a';
1532                 else
1533                         *out++ = *in++;
1534         }
1535 }
1536
1537 int COM_StringBeginsWith(const char *s, const char *match)
1538 {
1539         for (;*s && *match;s++, match++)
1540                 if (*s != *match)
1541                         return false;
1542         return true;
1543 }