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