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