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