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