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