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