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