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