]> icculus.org git repositories - divverent/darkplaces.git/blob - common.c
The gcc 2.95 problem was in fact OP_STORE_V doing nasty things to numbers that were...
[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 Com_HexDumpToConsole(const qbyte *data, int size)
471 {
472         int i;
473         char text[1024];
474         char *cur, *flushpointer;
475         cur = text;
476         flushpointer = text + 512;
477         for (i = 0;i < size;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[(data[i] >> 4) & 15] | 0x80;
495                         *cur++ = hexchar[(data[i] >> 0) & 15] | 0x80;
496                 }
497                 else
498                 {
499                         *cur++ = hexchar[(data[i] >> 4) & 15];
500                         *cur++ = hexchar[(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 void SZ_HexDumpToConsole(const sizebuf_t *buf)
519 {
520         Com_HexDumpToConsole(buf->data, buf->cursize);
521 }
522
523
524 //============================================================================
525
526
527 /*
528 ============
529 COM_StripExtension
530 ============
531 */
532 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
533 void COM_StripExtension (const char *in, char *out)
534 {
535         char *last = NULL;
536         while (*in)
537         {
538                 if (*in == '.')
539                         last = out;
540                 else if (*in == '/' || *in == '\\' || *in == ':')
541                         last = NULL;
542                 *out++ = *in++;
543         }
544         if (last)
545                 *last = 0;
546         else
547                 *out = 0;
548 }
549
550 /*
551 ============
552 COM_FileExtension
553 ============
554 */
555 char *COM_FileExtension (const char *in)
556 {
557         static char exten[8];
558         int i;
559
560         while (*in && *in != '.')
561                 in++;
562         if (!*in)
563                 return "";
564         in++;
565         for (i=0 ; i<7 && *in ; i++,in++)
566                 exten[i] = *in;
567         exten[i] = 0;
568         return exten;
569 }
570
571 /*
572 ============
573 COM_FileBase
574 ============
575 */
576 void COM_FileBase (const char *in, char *out)
577 {
578         const char *slash, *dot, *s;
579
580         slash = in;
581         dot = NULL;
582         s = in;
583         while(*s)
584         {
585                 if (*s == '/')
586                         slash = s + 1;
587                 if (*s == '.')
588                         dot = s;
589                 s++;
590         }
591         if (dot == NULL)
592                 dot = s;
593         if (dot - slash < 2)
594                 strcpy (out,"?model?");
595         else
596         {
597                 while (slash < dot)
598                         *out++ = *slash++;
599                 *out++ = 0;
600         }
601 }
602
603
604 /*
605 ==================
606 COM_DefaultExtension
607 ==================
608 */
609 void COM_DefaultExtension (char *path, const char *extension)
610 {
611         const char *src;
612 //
613 // if path doesn't have a .EXT, append extension
614 // (extension should include the .)
615 //
616         src = path + strlen(path) - 1;
617
618         while (*src != '/' && src != path)
619         {
620                 if (*src == '.')
621                         return;                 // it has an extension
622                 src--;
623         }
624
625         strcat (path, extension);
626 }
627
628
629 /*
630 ==============
631 COM_ParseToken
632
633 Parse a token out of a string
634 ==============
635 */
636 int COM_ParseToken (const char **datapointer)
637 {
638         int c;
639         int len;
640         const char *data = *datapointer;
641
642         len = 0;
643         com_token[0] = 0;
644
645         if (!data)
646         {
647                 *datapointer = NULL;
648                 return false;
649         }
650
651 // skip whitespace
652 skipwhite:
653         while ((c = *data) <= ' ')
654         {
655                 if (c == 0)
656                 {
657                         // end of file
658                         *datapointer = NULL;
659                         return false;
660                 }
661                 data++;
662         }
663
664 // skip // comments
665         if (c=='/' && data[1] == '/')
666         {
667                 while (*data && *data != '\n')
668                         data++;
669                 goto skipwhite;
670         }
671
672
673 // handle quoted strings specially
674         if (c == '\"')
675         {
676                 data++;
677                 while (1)
678                 {
679                         c = *data++;
680                         if (c=='\"' || !c)
681                         {
682                                 com_token[len] = 0;
683                                 *datapointer = data;
684                                 return true;
685                         }
686                         com_token[len] = c;
687                         len++;
688                 }
689         }
690
691 // parse single characters
692         if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
693         {
694                 com_token[len] = c;
695                 len++;
696                 com_token[len] = 0;
697                 *datapointer = data+1;
698                 return true;
699         }
700
701 // parse a regular word
702         do
703         {
704                 com_token[len] = c;
705                 data++;
706                 len++;
707                 c = *data;
708                 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
709                         break;
710         } while (c>32);
711
712         com_token[len] = 0;
713         *datapointer = data;
714         return true;
715 }
716
717
718 /*
719 ================
720 COM_CheckParm
721
722 Returns the position (1 to argc-1) in the program's argument list
723 where the given parameter apears, or 0 if not present
724 ================
725 */
726 int COM_CheckParm (const char *parm)
727 {
728         int i;
729
730         for (i=1 ; i<com_argc ; i++)
731         {
732                 if (!com_argv[i])
733                         continue;               // NEXTSTEP sometimes clears appkit vars.
734                 if (!strcmp (parm,com_argv[i]))
735                         return i;
736         }
737
738         return 0;
739 }
740
741 /*
742 ================
743 COM_CheckRegistered
744
745 Looks for the pop.txt file and verifies it.
746 Sets the "registered" cvar.
747 Immediately exits out if an alternate game was attempted to be started without
748 being registered.
749 ================
750 */
751 void COM_CheckRegistered (void)
752 {
753         Cvar_Set ("cmdline", com_cmdline);
754
755         if (!Sys_FileTime("gfx/pop.lmp"))
756         {
757                 if (com_modified)
758                         Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
759                 else
760                         Con_Printf ("Playing shareware version.\n");
761                 return;
762         }
763
764         Cvar_Set ("registered", "1");
765         Con_Printf ("Playing registered version.\n");
766 }
767
768
769 void COM_Path_f (void);
770
771
772 /*
773 ================
774 COM_InitArgv
775 ================
776 */
777 void COM_InitArgv (void)
778 {
779         int i, j, n;
780         // reconstitute the command line for the cmdline externally visible cvar
781         n = 0;
782         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
783         {
784                 i = 0;
785                 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
786                         com_cmdline[n++] = com_argv[j][i++];
787                 if (n < (CMDLINE_LENGTH - 1))
788                         com_cmdline[n++] = ' ';
789                 else
790                         break;
791         }
792         com_cmdline[n] = 0;
793 }
794
795 void COM_InitGameType (void)
796 {
797         char name[MAX_OSPATH];
798         COM_StripExtension(com_argv[0], name);
799         COM_ToLowerString(name, name);
800
801         if (strstr(name, "transfusion"))
802                 gamemode = GAME_TRANSFUSION;
803         else if (strstr(name, "nexiuz"))
804                 gamemode = GAME_NEXIUZ;
805         else if (strstr(name, "nehahra"))
806                 gamemode = GAME_NEHAHRA;
807         else if (strstr(name, "hipnotic"))
808                 gamemode = GAME_HIPNOTIC;
809         else if (strstr(name, "rogue"))
810                 gamemode = GAME_ROGUE;
811         else
812                 gamemode = GAME_NORMAL;
813
814         if (COM_CheckParm ("-transfusion"))
815                 gamemode = GAME_TRANSFUSION;
816         else if (COM_CheckParm ("-nexiuz"))
817                 gamemode = GAME_NEXIUZ;
818         else if (COM_CheckParm ("-nehahra"))
819                 gamemode = GAME_NEHAHRA;
820         else if (COM_CheckParm ("-hipnotic"))
821                 gamemode = GAME_HIPNOTIC;
822         else if (COM_CheckParm ("-rogue"))
823                 gamemode = GAME_ROGUE;
824         else if (COM_CheckParm ("-quake"))
825                 gamemode = GAME_NORMAL;
826
827         switch(gamemode)
828         {
829         case GAME_NORMAL:
830                 gamename = "DarkPlaces-Quake";
831                 gamedirname = "";
832                 break;
833         case GAME_HIPNOTIC:
834                 gamename = "Darkplaces-Hipnotic";
835                 gamedirname = "hipnotic";
836                 break;
837         case GAME_ROGUE:
838                 gamename = "Darkplaces-Rogue";
839                 gamedirname = "rogue";
840                 break;
841         case GAME_NEHAHRA:
842                 gamename = "DarkPlaces-Nehahra";
843                 gamedirname = "nehahra";
844                 break;
845         case GAME_NEXIUZ:
846                 gamename = "Nexiuz";
847                 gamedirname = "data";
848                 break;
849         case GAME_TRANSFUSION:
850                 gamename = "Transfusion";
851                 gamedirname = "transfusion";
852                 break;
853         default:
854                 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
855                 break;
856         }
857 }
858
859
860 extern void Mathlib_Init(void);
861
862 /*
863 ================
864 COM_Init
865 ================
866 */
867 void COM_Init (void)
868 {
869 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
870         qbyte swaptest[2] = {1,0};
871
872 // set the byte swapping variables in a portable manner
873         if ( *(short *)swaptest == 1)
874         {
875                 BigShort = ShortSwap;
876                 LittleShort = ShortNoSwap;
877                 BigLong = LongSwap;
878                 LittleLong = LongNoSwap;
879                 BigFloat = FloatSwap;
880                 LittleFloat = FloatNoSwap;
881         }
882         else
883         {
884                 BigShort = ShortNoSwap;
885                 LittleShort = ShortSwap;
886                 BigLong = LongNoSwap;
887                 LittleLong = LongSwap;
888                 BigFloat = FloatNoSwap;
889                 LittleFloat = FloatSwap;
890         }
891 #endif
892
893         pak_mempool = Mem_AllocPool("paks");
894
895         Cvar_RegisterVariable (&registered);
896         Cvar_RegisterVariable (&cmdline);
897         Cmd_AddCommand ("path", COM_Path_f);
898
899         Mathlib_Init();
900
901         COM_InitFilesystem ();
902         COM_CheckRegistered ();
903
904         COM_InitGameType();
905 }
906
907
908 /*
909 ============
910 va
911
912 does a varargs printf into a temp buffer, so I don't need to have
913 varargs versions of all text functions.
914 FIXME: make this buffer size safe someday
915 ============
916 */
917 char *va(const char *format, ...)
918 {
919         va_list argptr;
920         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
921         static char string[8][1024], *s;
922         static int stringindex = 0;
923
924         s = string[stringindex];
925         stringindex = (stringindex + 1) & 7;
926         va_start (argptr, format);
927         vsprintf (s, format,argptr);
928         va_end (argptr);
929
930         return s;
931 }
932
933
934 /*
935 =============================================================================
936
937 QUAKE FILESYSTEM
938
939 =============================================================================
940 */
941
942 int com_filesize;
943
944
945 //
946 // in memory
947 //
948
949 typedef struct
950 {
951         char name[MAX_QPATH];
952         int filepos, filelen;
953 } packfile_t;
954
955 typedef struct pack_s
956 {
957         char filename[MAX_OSPATH];
958         int handle;
959         int numfiles;
960         packfile_t *files;
961         mempool_t *mempool;
962         struct pack_s *next;
963 } pack_t;
964
965 //
966 // on disk
967 //
968 typedef struct
969 {
970         char name[56];
971         int filepos, filelen;
972 } dpackfile_t;
973
974 typedef struct
975 {
976         char id[4];
977         int dirofs;
978         int dirlen;
979 } dpackheader_t;
980
981 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
982 #define MAX_FILES_IN_PACK       65536
983
984 pack_t *packlist = NULL;
985
986 #if CACHEENABLE
987 char com_cachedir[MAX_OSPATH];
988 #endif
989 char com_gamedir[MAX_OSPATH];
990
991 typedef struct searchpath_s
992 {
993         // only one of filename / pack will be used
994         char filename[MAX_OSPATH];
995         pack_t *pack;
996         struct searchpath_s *next;
997 } searchpath_t;
998
999 searchpath_t *com_searchpaths;
1000
1001 /*
1002 ============
1003 COM_Path_f
1004
1005 ============
1006 */
1007 void COM_Path_f (void)
1008 {
1009         searchpath_t *s;
1010
1011         Con_Printf ("Current search path:\n");
1012         for (s=com_searchpaths ; s ; s=s->next)
1013         {
1014                 if (s->pack)
1015                 {
1016                         Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1017                 }
1018                 else
1019                         Con_Printf ("%s\n", s->filename);
1020         }
1021 }
1022
1023 /*
1024 ============
1025 COM_CreatePath
1026
1027 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1028 ============
1029 */
1030 void    COM_CreatePath (char *path)
1031 {
1032         char *ofs, save;
1033
1034         for (ofs = path+1 ; *ofs ; ofs++)
1035         {
1036                 if (*ofs == '/' || *ofs == '\\')
1037                 {
1038                         // create the directory
1039                         save = *ofs;
1040                         *ofs = 0;
1041                         Sys_mkdir (path);
1042                         *ofs = save;
1043                 }
1044         }
1045 }
1046
1047
1048 /*
1049 ============
1050 COM_WriteFile
1051
1052 The filename will be prefixed by the current game directory
1053 ============
1054 */
1055 qboolean COM_WriteFile (const char *filename, void *data, int len)
1056 {
1057         int handle;
1058         char name[MAX_OSPATH];
1059
1060         sprintf (name, "%s/%s", com_gamedir, filename);
1061
1062         // LordHavoc: added this
1063         // create directories up to the file
1064         COM_CreatePath (name);
1065
1066         handle = Sys_FileOpenWrite (name);
1067         if (handle == -1)
1068         {
1069                 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1070                 return false;
1071         }
1072
1073         Con_DPrintf ("COM_WriteFile: %s\n", name);
1074         Sys_FileWrite (handle, data, len);
1075         Sys_FileClose (handle);
1076         return true;
1077 }
1078
1079
1080 /*
1081 ===========
1082 COM_CopyFile
1083
1084 Copies a file over from the net to the local cache, creating any directories
1085 needed.  This is for the convenience of developers using ISDN from home.
1086 ===========
1087 */
1088 void COM_CopyFile (char *netpath, char *cachepath)
1089 {
1090         int in, out, remaining, count;
1091         char buf[4096];
1092
1093         remaining = Sys_FileOpenRead (netpath, &in);
1094         COM_CreatePath (cachepath);     // create directories up to the cache file
1095         out = Sys_FileOpenWrite (cachepath);
1096
1097         while (remaining)
1098         {
1099                 if (remaining < (int)sizeof(buf))
1100                         count = remaining;
1101                 else
1102                         count = sizeof(buf);
1103                 Sys_FileRead (in, buf, count);
1104                 Sys_FileWrite (out, buf, count);
1105                 remaining -= count;
1106         }
1107
1108         Sys_FileClose (in);
1109         Sys_FileClose (out);
1110 }
1111
1112 /*
1113 ===========
1114 COM_OpenRead
1115 ===========
1116 */
1117 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1118 {
1119         int fd = open (path, O_RDONLY);
1120         unsigned char id[2], len_bytes[4];
1121
1122         if (fd == -1)
1123         {
1124                 Sys_Error ("Couldn't open %s", path);
1125                 return 0;
1126         }
1127         if (offs < 0 || len < 0)
1128         {
1129                 // normal file
1130                 offs = 0;
1131                 len = lseek (fd, 0, SEEK_END);
1132                 lseek (fd, 0, SEEK_SET);
1133         }
1134         lseek (fd, offs, SEEK_SET);
1135         if (zip)
1136         {
1137                 read (fd, id, 2);
1138                 if (id[0] == 0x1f && id[1] == 0x8b)
1139                 {
1140                         lseek (fd, offs + len - 4, SEEK_SET);
1141                         read (fd, len_bytes, 4);
1142                         len = ((len_bytes[3] << 24)
1143                                    | (len_bytes[2] << 16)
1144                                    | (len_bytes[1] << 8)
1145                                    | (len_bytes[0]));
1146                 }
1147         }
1148         lseek (fd, offs, SEEK_SET);
1149         com_filesize = len;
1150
1151 #ifdef WIN32
1152         setmode (fd, O_BINARY);
1153 #endif
1154         if (zip)
1155                 return Qdopen (fd, "rbz");
1156         else
1157                 return Qdopen (fd, "rb");
1158 }
1159
1160 /*
1161 ===========
1162 COM_FindFile
1163
1164 Finds the file in the search path.
1165 Sets com_filesize and one of handle or file
1166 ===========
1167 */
1168 int COM_FindFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1169 {
1170         searchpath_t *search;
1171         char netpath[MAX_OSPATH];
1172 #if CACHEENABLE
1173         char cachepath[MAX_OSPATH];
1174         int cachetime;
1175 #endif
1176         pack_t *pak;
1177         int i, findtime, filenamelen;
1178         char gzfilename[MAX_OSPATH];
1179
1180         filenamelen = strlen (filename);
1181         sprintf (gzfilename, "%s.gz", filename);
1182
1183         if (!file)
1184                 Sys_Error ("COM_FindFile: file not set");
1185
1186 //
1187 // search through the path, one element at a time
1188 //
1189         search = com_searchpaths;
1190
1191         for ( ; search ; search = search->next)
1192         {
1193         // is the element a pak file?
1194                 if (search->pack)
1195                 {
1196                 // look through all the pak file elements
1197                         pak = search->pack;
1198                         for (i=0 ; i<pak->numfiles ; i++)
1199                                 if (!strcmp (pak->files[i].name, filename)
1200                                     || !strcmp (pak->files[i].name, gzfilename))
1201                                 {       // found it!
1202                                         if (!quiet)
1203                                                 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1204                                         // open a new file on the pakfile
1205                                         *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1206                                         return com_filesize;
1207                                 }
1208                 }
1209                 else
1210                 {
1211                         sprintf (netpath, "%s/%s",search->filename, filename);
1212
1213                         findtime = Sys_FileTime (netpath);
1214                         if (findtime == -1)
1215                                 continue;
1216
1217 #if CACHEENABLE
1218                         // see if the file needs to be updated in the cache
1219                         if (com_cachedir[0])
1220                         {
1221 #if defined(_WIN32)
1222                                 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1223                                         sprintf (cachepath,"%s%s", com_cachedir, netpath);
1224                                 else
1225                                         sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1226 #else
1227                                 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1228 #endif
1229
1230                                 cachetime = Sys_FileTime (cachepath);
1231
1232                                 if (cachetime < findtime)
1233                                         COM_CopyFile (netpath, cachepath);
1234                                 strcpy (netpath, cachepath);
1235                         }
1236 #endif
1237                         if (!quiet)
1238                                 Sys_Printf ("FindFile: %s\n",netpath);
1239                         *file = COM_OpenRead (netpath, -1, -1, zip);
1240                         return com_filesize;
1241                 }
1242         }
1243
1244         if (!quiet)
1245                 Sys_Printf ("FindFile: can't find %s\n", filename);
1246
1247         *file = NULL;
1248         com_filesize = -1;
1249         return -1;
1250 }
1251
1252
1253 /*
1254 ===========
1255 COM_FOpenFile
1256
1257 If the requested file is inside a packfile, a new QFile * will be opened
1258 into the file.
1259 ===========
1260 */
1261 int COM_FOpenFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1262 {
1263         return COM_FindFile (filename, file, quiet, zip);
1264 }
1265
1266
1267 /*
1268 ============
1269 COM_LoadFile
1270
1271 Filename are reletive to the quake directory.
1272 Always appends a 0 byte.
1273 ============
1274 */
1275 qbyte *loadbuf;
1276 int loadsize;
1277 qbyte *COM_LoadFile (const char *path, qboolean quiet)
1278 {
1279         QFile *h;
1280         qbyte *buf;
1281         char base[1024];
1282         int len;
1283
1284         buf = NULL;     // quiet compiler warning
1285         loadsize = 0;
1286
1287 // look for it in the filesystem or pack files
1288         len = COM_FOpenFile (path, &h, quiet, true);
1289         if (!h)
1290                 return NULL;
1291
1292         loadsize = len;
1293
1294 // extract the filename base name for hunk tag
1295         COM_FileBase (path, base);
1296
1297         buf = Mem_Alloc(tempmempool, len+1);
1298         if (!buf)
1299                 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1300
1301         ((qbyte *)buf)[len] = 0;
1302
1303         Qread (h, buf, len);
1304         Qclose (h);
1305
1306         return buf;
1307 }
1308
1309 /*
1310 =================
1311 COM_LoadPackFile
1312
1313 Takes an explicit (not game tree related) path to a pak file.
1314
1315 Loads the header and directory, adding the files at the beginning
1316 of the list so they override previous pack files.
1317 =================
1318 */
1319 pack_t *COM_LoadPackFile (const char *packfile)
1320 {
1321         dpackheader_t header;
1322         int i, numpackfiles, packhandle;
1323         pack_t *pack;
1324         // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1325         dpackfile_t *info;
1326
1327         if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1328                 return NULL;
1329
1330         Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1331         if (memcmp(header.id, "PACK", 4))
1332                 Sys_Error ("%s is not a packfile", packfile);
1333         header.dirofs = LittleLong (header.dirofs);
1334         header.dirlen = LittleLong (header.dirlen);
1335
1336         if (header.dirlen % sizeof(dpackfile_t))
1337                 Sys_Error ("%s has an invalid directory size", packfile);
1338
1339         numpackfiles = header.dirlen / sizeof(dpackfile_t);
1340
1341         if (numpackfiles > MAX_FILES_IN_PACK)
1342                 Sys_Error ("%s has %i files", packfile, numpackfiles);
1343
1344         pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1345         strcpy (pack->filename, packfile);
1346         pack->handle = packhandle;
1347         pack->numfiles = numpackfiles;
1348         pack->mempool = Mem_AllocPool(packfile);
1349         pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1350         pack->next = packlist;
1351         packlist = pack;
1352
1353         info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1354         Sys_FileSeek (packhandle, header.dirofs);
1355         Sys_FileRead (packhandle, (void *)info, header.dirlen);
1356
1357 // parse the directory
1358         for (i = 0;i < numpackfiles;i++)
1359         {
1360                 strcpy (pack->files[i].name, info[i].name);
1361                 pack->files[i].filepos = LittleLong(info[i].filepos);
1362                 pack->files[i].filelen = LittleLong(info[i].filelen);
1363         }
1364
1365         Mem_Free(info);
1366
1367         Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1368         return pack;
1369 }
1370
1371
1372 /*
1373 ================
1374 COM_AddGameDirectory
1375
1376 Sets com_gamedir, adds the directory to the head of the path,
1377 then loads and adds pak1.pak pak2.pak ...
1378 ================
1379 */
1380 void COM_AddGameDirectory (char *dir)
1381 {
1382         stringlist_t *list, *current;
1383         searchpath_t *search;
1384         pack_t *pak;
1385         char pakfile[MAX_OSPATH];
1386
1387         strcpy (com_gamedir, dir);
1388
1389 //
1390 // add the directory to the search path
1391 //
1392         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1393         strcpy (search->filename, dir);
1394         search->next = com_searchpaths;
1395         com_searchpaths = search;
1396
1397         // add any paks in the directory
1398         list = listdirectory(dir);
1399         for (current = list;current;current = current->next)
1400         {
1401                 if (matchpattern(current->text, "*.pak", true))
1402                 {
1403                         sprintf (pakfile, "%s/%s", dir, current->text);
1404                         pak = COM_LoadPackFile (pakfile);
1405                         if (pak)
1406                         {
1407                                 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1408                                 search->pack = pak;
1409                                 search->next = com_searchpaths;
1410                                 com_searchpaths = search;
1411                         }
1412                         else
1413                                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1414                 }
1415         }
1416         freedirectory(list);
1417 }
1418
1419 /*
1420 ================
1421 COM_InitFilesystem
1422 ================
1423 */
1424 void COM_InitFilesystem (void)
1425 {
1426         int i;
1427         searchpath_t *search;
1428
1429         strcpy(com_basedir, ".");
1430
1431         // -basedir <path>
1432         // Overrides the system supplied base directory (under GAMENAME)
1433         i = COM_CheckParm ("-basedir");
1434         if (i && i < com_argc-1)
1435                 strcpy (com_basedir, com_argv[i+1]);
1436
1437         i = strlen (com_basedir);
1438         if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1439                 com_basedir[i-1] = 0;
1440
1441 // start up with GAMENAME by default (id1)
1442         strcpy(com_modname, GAMENAME);
1443         COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1444         if (gamedirname[0])
1445         {
1446                 com_modified = true;
1447                 strcpy(com_modname, gamedirname);
1448                 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1449         }
1450
1451         // -game <gamedir>
1452         // Adds basedir/gamedir as an override game
1453         i = COM_CheckParm ("-game");
1454         if (i && i < com_argc-1)
1455         {
1456                 com_modified = true;
1457                 strcpy(com_modname, com_argv[i+1]);
1458                 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1459         }
1460
1461         // -path <dir or packfile> [<dir or packfile>] ...
1462         // Fully specifies the exact search path, overriding the generated one
1463         i = COM_CheckParm ("-path");
1464         if (i)
1465         {
1466                 com_modified = true;
1467                 com_searchpaths = NULL;
1468                 while (++i < com_argc)
1469                 {
1470                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1471                                 break;
1472
1473                         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1474                         if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1475                         {
1476                                 search->pack = COM_LoadPackFile (com_argv[i]);
1477                                 if (!search->pack)
1478                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1479                         }
1480                         else
1481                                 strcpy (search->filename, com_argv[i]);
1482                         search->next = com_searchpaths;
1483                         com_searchpaths = search;
1484                 }
1485         }
1486 }
1487
1488 int COM_FileExists(const char *filename)
1489 {
1490         searchpath_t *search;
1491         char netpath[MAX_OSPATH];
1492         pack_t *pak;
1493         int i, findtime;
1494
1495         for (search = com_searchpaths;search;search = search->next)
1496         {
1497                 if (search->pack)
1498                 {
1499                         pak = search->pack;
1500                         for (i = 0;i < pak->numfiles;i++)
1501                                 if (!strcmp (pak->files[i].name, filename))
1502                                         return true;
1503                 }
1504                 else
1505                 {
1506                         sprintf (netpath, "%s/%s",search->filename, filename);
1507                         findtime = Sys_FileTime (netpath);
1508                         if (findtime != -1)
1509                                 return true;
1510                 }
1511         }
1512
1513         return false;
1514 }
1515
1516
1517 //======================================
1518 // LordHavoc: added these because they are useful
1519
1520 void COM_ToLowerString(const char *in, char *out)
1521 {
1522         while (*in)
1523         {
1524                 if (*in >= 'A' && *in <= 'Z')
1525                         *out++ = *in++ + 'a' - 'A';
1526                 else
1527                         *out++ = *in++;
1528         }
1529 }
1530
1531 void COM_ToUpperString(const char *in, char *out)
1532 {
1533         while (*in)
1534         {
1535                 if (*in >= 'a' && *in <= 'z')
1536                         *out++ = *in++ + 'A' - 'a';
1537                 else
1538                         *out++ = *in++;
1539         }
1540 }
1541
1542 int COM_StringBeginsWith(const char *s, const char *match)
1543 {
1544         for (;*s && *match;s++, match++)
1545                 if (*s != *match)
1546                         return false;
1547         return true;
1548 }