2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // common.c -- misc functions used in client and server
32 #define NUM_SAFE_ARGVS 7
34 static char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
35 static char *argvdummy = " ";
37 static char *safeargvs[NUM_SAFE_ARGVS] =
38 {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-window"};
40 cvar_t registered = {0, "registered","0"};
41 cvar_t cmdline = {0, "cmdline","0"};
43 mempool_t *pak_mempool;
45 qboolean com_modified; // set true if using non-id files
49 //int static_registered = 1; // only for startup check, then set
51 qboolean msg_suppress_1 = 0;
53 void COM_InitFilesystem (void);
59 // LordHavoc: made commandline 1024 characters instead of 256
60 #define CMDLINE_LENGTH 1024
61 char com_cmdline[CMDLINE_LENGTH];
69 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.
71 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
72 only used during filesystem initialization.
74 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.
76 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
77 specified, when a file is found by the normal search path, it will be mirrored
78 into the cache directory, then opened there.
83 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.
87 //============================================================================
91 ============================================================================
93 LIBRARY REPLACEMENT FUNCTIONS
95 ============================================================================
99 void Q_memset (void *dest, int fill, int count)
103 if ( (((long)dest | count) & 3) == 0)
106 fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
107 for (i=0 ; i<count ; i++)
108 ((int *)dest)[i] = fill;
111 for (i=0 ; i<count ; i++)
112 ((byte *)dest)[i] = fill;
115 void Q_memcpy (void *dest, void *src, int count)
119 if (( ( (long)dest | (long)src | count) & 3) == 0 )
122 for (i=0 ; i<count ; i++)
123 ((int *)dest)[i] = ((int *)src)[i];
126 for (i=0 ; i<count ; i++)
127 ((byte *)dest)[i] = ((byte *)src)[i];
130 int Q_memcmp (void *m1, void *m2, int count)
135 if (((byte *)m1)[count] != ((byte *)m2)[count])
141 void Q_strcpy (char *dest, char *src)
150 void Q_strncpy (char *dest, char *src, int count)
152 while (*src && count--)
160 int Q_strlen (char *str)
171 char *Q_strrchr(char *s, char c)
173 int len = Q_strlen(s);
176 if (*--s == c) return s;
180 void Q_strcat (char *dest, char *src)
182 dest += Q_strlen(dest);
183 Q_strcpy (dest, src);
186 int Q_strcmp (char *s1, char *s2)
191 return -1; // strings not equal
193 return 0; // strings are equal
201 int Q_strncmp (char *s1, char *s2, int count)
208 return -1; // strings not equal
210 return 0; // strings are equal
218 int Q_strncasecmp (char *s1, char *s2, int n)
228 return 0; // strings are equal until end point
232 if (c1 >= 'a' && c1 <= 'z')
234 if (c2 >= 'a' && c2 <= 'z')
237 return -1; // strings not equal
240 return 0; // strings are equal
248 int Q_strcasecmp (char *s1, char *s2)
250 return Q_strncasecmp (s1, s2, 99999);
253 int Q_atoi (char *str)
272 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
278 if (c >= '0' && c <= '9')
279 val = (val<<4) + c - '0';
280 else if (c >= 'a' && c <= 'f')
281 val = (val<<4) + c - 'a' + 10;
282 else if (c >= 'A' && c <= 'F')
283 val = (val<<4) + c - 'A' + 10;
290 // check for character
294 return sign * str[1];
303 if (c <'0' || c > '9')
305 val = val*10 + c - '0';
312 float Q_atof (char *str)
332 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
338 if (c >= '0' && c <= '9')
339 val = (val*16) + c - '0';
340 else if (c >= 'a' && c <= 'f')
341 val = (val*16) + c - 'a' + 10;
342 else if (c >= 'A' && c <= 'F')
343 val = (val*16) + c - 'A' + 10;
350 // check for character
354 return sign * str[1];
370 if (c <'0' || c > '9')
372 val = val*10 + c - '0';
378 while (total > decimal)
389 ============================================================================
393 ============================================================================
396 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
397 short (*BigShort) (short l);
398 short (*LittleShort) (short l);
399 int (*BigLong) (int l);
400 int (*LittleLong) (int l);
401 float (*BigFloat) (float l);
402 float (*LittleFloat) (float l);
405 short ShortSwap (short l)
415 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
416 short ShortNoSwap (short l)
431 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
434 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
435 int LongNoSwap (int l)
441 float FloatSwap (float f)
451 dat2.b[0] = dat1.b[3];
452 dat2.b[1] = dat1.b[2];
453 dat2.b[2] = dat1.b[1];
454 dat2.b[3] = dat1.b[0];
458 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
459 float FloatNoSwap (float f)
466 ==============================================================================
470 Handles byte ordering and avoids alignment errors
471 ==============================================================================
478 void MSG_WriteChar (sizebuf_t *sb, int c)
483 // if (c < -128 || c > 127)
484 // Sys_Error ("MSG_WriteChar: range error");
487 buf = SZ_GetSpace (sb, 1);
491 void MSG_WriteByte (sizebuf_t *sb, int c)
496 // if (c < 0 || c > 255)
497 // Sys_Error ("MSG_WriteByte: range error");
500 buf = SZ_GetSpace (sb, 1);
504 void MSG_WriteShort (sizebuf_t *sb, int c)
509 // if (c < ((short)0x8000) || c > (short)0x7fff)
510 // Sys_Error ("MSG_WriteShort: range error");
513 buf = SZ_GetSpace (sb, 2);
518 void MSG_WriteLong (sizebuf_t *sb, int c)
522 buf = SZ_GetSpace (sb, 4);
524 buf[1] = (c>>8)&0xff;
525 buf[2] = (c>>16)&0xff;
529 void MSG_WriteFloat (sizebuf_t *sb, float f)
539 dat.l = LittleLong (dat.l);
541 SZ_Write (sb, &dat.l, 4);
544 void MSG_WriteString (sizebuf_t *sb, char *s)
547 SZ_Write (sb, "", 1);
549 SZ_Write (sb, s, strlen(s)+1);
552 // used by server (always dpprotocol)
553 // moved to common.h as #define
555 void MSG_WriteFloatCoord (sizebuf_t *sb, float f)
557 MSG_WriteFloat(sb, f);
562 void MSG_WriteCoord (sizebuf_t *sb, float f)
565 MSG_WriteFloat(sb, f);
569 MSG_WriteShort (sb, (int)(f*8.0f + 0.5f));
571 MSG_WriteShort (sb, (int)(f*8.0f - 0.5f));
575 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
578 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
580 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
583 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
584 void MSG_WriteAngle (sizebuf_t *sb, float f)
587 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
589 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
596 qboolean msg_badread;
598 void MSG_BeginReading (void)
605 // returns -1 and sets msg_badread if no more characters are available
606 int MSG_ReadChar (void)
610 // LordHavoc: minor optimization
611 if (msg_readcount >= net_message.cursize)
612 // if (msg_readcount+1 > net_message.cursize)
618 c = (signed char)net_message.data[msg_readcount];
624 int MSG_ReadByte (void)
628 // LordHavoc: minor optimization
629 if (msg_readcount >= net_message.cursize)
630 // if (msg_readcount+1 > net_message.cursize)
636 c = (unsigned char)net_message.data[msg_readcount];
643 int MSG_ReadShort (void)
647 if (msg_readcount+2 > net_message.cursize)
653 c = (short)(net_message.data[msg_readcount]
654 + (net_message.data[msg_readcount+1]<<8));
661 int MSG_ReadLong (void)
665 if (msg_readcount+4 > net_message.cursize)
671 c = net_message.data[msg_readcount]
672 + (net_message.data[msg_readcount+1]<<8)
673 + (net_message.data[msg_readcount+2]<<16)
674 + (net_message.data[msg_readcount+3]<<24);
681 float MSG_ReadFloat (void)
690 dat.b[0] = net_message.data[msg_readcount];
691 dat.b[1] = net_message.data[msg_readcount+1];
692 dat.b[2] = net_message.data[msg_readcount+2];
693 dat.b[3] = net_message.data[msg_readcount+3];
696 dat.l = LittleLong (dat.l);
701 char *MSG_ReadString (void)
703 static char string[2048];
710 if (c == -1 || c == 0)
714 } while (l < sizeof(string)-1);
721 // used by server (always dpprotocol)
722 // moved to common.h as #define
724 float MSG_ReadFloatCoord (void)
726 return MSG_ReadFloat();
731 float MSG_ReadCoord (void)
734 return MSG_ReadFloat();
736 return MSG_ReadShort() * (1.0f/8.0f);
740 float MSG_ReadCoord (void)
742 return MSG_ReadShort() * (1.0f/8.0f);
745 float MSG_ReadAngle (void)
747 return MSG_ReadChar() * (360.0f/256.0f);
750 float MSG_ReadPreciseAngle (void)
752 return MSG_ReadShort() * (360.0f/65536);
757 //===========================================================================
759 void SZ_Alloc (sizebuf_t *buf, int startsize, char *name)
763 buf->mempool = Mem_AllocPool(name);
764 buf->data = Mem_Alloc(buf->mempool, startsize);
765 buf->maxsize = startsize;
770 void SZ_Free (sizebuf_t *buf)
772 Mem_FreePool(&buf->mempool);
778 void SZ_Clear (sizebuf_t *buf)
783 void *SZ_GetSpace (sizebuf_t *buf, int length)
787 if (buf->cursize + length > buf->maxsize)
789 if (!buf->allowoverflow)
790 Host_Error ("SZ_GetSpace: overflow without allowoverflow set - use -zone on the commandline for more zone memory, default: 128k (quake original default was 48k)");
792 if (length > buf->maxsize)
793 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
795 buf->overflowed = true;
796 Con_Printf ("SZ_GetSpace: overflow");
800 data = buf->data + buf->cursize;
801 buf->cursize += length;
806 void SZ_Write (sizebuf_t *buf, void *data, int length)
808 memcpy (SZ_GetSpace(buf,length),data,length);
811 void SZ_Print (sizebuf_t *buf, char *data)
815 len = strlen(data)+1;
817 // byte * cast to keep VC++ happy
818 if (buf->data[buf->cursize-1])
819 memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
821 memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
825 //============================================================================
833 char *COM_SkipPath (char *pathname)
852 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
853 void COM_StripExtension (char *in, char *out)
860 if ((*in == '/') || (*in == '\\') || (*in == ':'))
873 char *COM_FileExtension (char *in)
875 static char exten[8];
878 while (*in && *in != '.')
883 for (i=0 ; i<7 && *in ; i++,in++)
894 void COM_FileBase (char *in, char *out)
913 strcpy (out,"?model?");
928 void COM_DefaultExtension (char *path, char *extension)
932 // if path doesn't have a .EXT, append extension
933 // (extension should include the .)
935 src = path + strlen(path) - 1;
937 while (*src != '/' && src != path)
940 return; // it has an extension
944 strcat (path, extension);
952 Parse a token out of a string
955 char *COM_Parse (char *data)
968 while ( (c = *data) <= ' ')
971 return NULL; // end of file;
976 if (c=='/' && data[1] == '/')
978 while (*data && *data != '\n')
984 // handle quoted strings specially
1001 // parse single characters
1002 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1010 // parse a regular word
1017 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1030 Returns the position (1 to argc-1) in the program's argument list
1031 where the given parameter apears, or 0 if not present
1034 int COM_CheckParm (char *parm)
1038 for (i=1 ; i<com_argc ; i++)
1041 continue; // NEXTSTEP sometimes clears appkit vars.
1042 if (!strcmp (parm,com_argv[i]))
1053 Looks for the pop.txt file and verifies it.
1054 Sets the "registered" cvar.
1055 Immediately exits out if an alternate game was attempted to be started without
1059 void COM_CheckRegistered (void)
1061 Cvar_Set ("cmdline", com_cmdline);
1063 // static_registered = 0;
1065 if (!Sys_FileTime("gfx/pop.lmp"))
1068 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1070 Con_Printf ("Playing shareware version.\n");
1072 // Sys_Error ("This dedicated server requires a full registered copy of Quake");
1074 // Con_Printf ("Playing shareware version.\n");
1075 // if (com_modified)
1076 // Sys_Error ("You must have the registered version to use modified games");
1080 // Cvar_Set ("cmdline", com_cmdline);
1081 Cvar_Set ("registered", "1");
1082 // static_registered = 1;
1083 Con_Printf ("Playing registered version.\n");
1087 void COM_Path_f (void);
1095 void COM_InitArgv (int argc, char **argv)
1100 // reconstitute the command line for the cmdline externally visible cvar
1103 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1107 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1109 com_cmdline[n++] = argv[j][i++];
1112 if (n < (CMDLINE_LENGTH - 1))
1113 com_cmdline[n++] = ' ';
1122 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1125 largv[com_argc] = argv[com_argc];
1126 if (!strcmp ("-safe", argv[com_argc]))
1132 // force all the safe-mode switches. Note that we reserved extra space in
1133 // case we need to add these, so we don't need an overflow check
1134 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1136 largv[com_argc] = safeargvs[i];
1141 largv[com_argc] = argvdummy;
1145 gamemode = GAME_ZYMOTIC;
1147 gamemode = GAME_FIENDARENA;
1149 gamemode = GAME_NEHAHRA;
1151 if (COM_CheckParm ("-zymotic"))
1152 gamemode = GAME_ZYMOTIC;
1153 else if (COM_CheckParm ("-fiendarena"))
1154 gamemode = GAME_FIENDARENA;
1155 else if (COM_CheckParm ("-nehahra"))
1156 gamemode = GAME_NEHAHRA;
1157 else if (COM_CheckParm ("-hipnotic"))
1158 gamemode = GAME_HIPNOTIC;
1159 else if (COM_CheckParm ("-rogue"))
1160 gamemode = GAME_ROGUE;
1165 gamename = "DarkPlaces";
1168 gamename = "Darkplaces-Hipnotic";
1171 gamename = "Darkplaces-Rogue";
1174 gamename = "DarkPlaces-Nehahra";
1176 case GAME_FIENDARENA:
1177 gamename = "FiendArena";
1180 gamename = "Zymotic";
1183 Sys_Error("COM_InitArgv: unknown gamemode %i\n", gamemode);
1189 extern void Mathlib_Init(void);
1196 void COM_Init (void)
1198 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
1199 byte swaptest[2] = {1,0};
1201 // set the byte swapping variables in a portable manner
1202 if ( *(short *)swaptest == 1)
1204 BigShort = ShortSwap;
1205 LittleShort = ShortNoSwap;
1207 LittleLong = LongNoSwap;
1208 BigFloat = FloatSwap;
1209 LittleFloat = FloatNoSwap;
1213 BigShort = ShortNoSwap;
1214 LittleShort = ShortSwap;
1215 BigLong = LongNoSwap;
1216 LittleLong = LongSwap;
1217 BigFloat = FloatNoSwap;
1218 LittleFloat = FloatSwap;
1222 pak_mempool = Mem_AllocPool("paks");
1224 Cvar_RegisterVariable (®istered);
1225 Cvar_RegisterVariable (&cmdline);
1226 Cmd_AddCommand ("path", COM_Path_f);
1230 COM_InitFilesystem ();
1231 COM_CheckRegistered ();
1239 does a varargs printf into a temp buffer, so I don't need to have
1240 varargs versions of all text functions.
1241 FIXME: make this buffer size safe someday
1244 char *va(char *format, ...)
1247 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1248 static char string[8][1024], *s;
1249 static int stringindex = 0;
1251 s = string[stringindex];
1252 stringindex = (stringindex + 1) & 7;
1253 va_start (argptr, format);
1254 vsprintf (s, format,argptr);
1261 /// just for debugging
1262 int memsearch (byte *start, int count, int search)
1266 for (i=0 ; i<count ; i++)
1267 if (start[i] == search)
1273 =============================================================================
1277 =============================================================================
1289 char name[MAX_QPATH];
1290 int filepos, filelen;
1293 typedef struct pack_s
1295 char filename[MAX_OSPATH];
1300 struct pack_s *next;
1309 int filepos, filelen;
1319 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
1320 #define MAX_FILES_IN_PACK 65536
1322 pack_t *packlist = NULL;
1325 char com_cachedir[MAX_OSPATH];
1327 char com_gamedir[MAX_OSPATH];
1329 typedef struct searchpath_s
1331 char filename[MAX_OSPATH];
1332 pack_t *pack; // only one of filename / pack will be used
1333 struct searchpath_s *next;
1336 searchpath_t *com_searchpaths;
1344 void COM_Path_f (void)
1348 Con_Printf ("Current search path:\n");
1349 for (s=com_searchpaths ; s ; s=s->next)
1353 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1356 Con_Printf ("%s\n", s->filename);
1364 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1367 void COM_CreatePath (char *path)
1371 for (ofs = path+1 ; *ofs ; ofs++)
1373 if (*ofs == '/' || *ofs == '\\' || *ofs == ':')
1374 { // create the directory
1388 The filename will be prefixed by the current game directory
1391 void COM_WriteFile (char *filename, void *data, int len)
1394 char name[MAX_OSPATH];
1396 sprintf (name, "%s/%s", com_gamedir, filename);
1398 // LordHavoc: added this
1399 COM_CreatePath (name); // create directories up to the file
1401 handle = Sys_FileOpenWrite (name);
1404 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1408 Con_Printf ("COM_WriteFile: %s\n", name);
1409 Sys_FileWrite (handle, data, len);
1410 Sys_FileClose (handle);
1418 Copies a file over from the net to the local cache, creating any directories
1419 needed. This is for the convenience of developers using ISDN from home.
1422 void COM_CopyFile (char *netpath, char *cachepath)
1425 int remaining, count;
1428 remaining = Sys_FileOpenRead (netpath, &in);
1429 COM_CreatePath (cachepath); // create directories up to the cache file
1430 out = Sys_FileOpenWrite (cachepath);
1434 if (remaining < sizeof(buf))
1437 count = sizeof(buf);
1438 Sys_FileRead (in, buf, count);
1439 Sys_FileWrite (out, buf, count);
1444 Sys_FileClose (out);
1452 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1454 int fd = open (path, O_RDONLY);
1455 unsigned char id[2];
1456 unsigned char len_bytes[4];
1460 Sys_Error ("Couldn't open %s", path);
1463 if (offs < 0 || len < 0)
1467 len = lseek (fd, 0, SEEK_END);
1468 lseek (fd, 0, SEEK_SET);
1470 lseek (fd, offs, SEEK_SET);
1474 if (id[0] == 0x1f && id[1] == 0x8b)
1476 lseek (fd, offs + len - 4, SEEK_SET);
1477 read (fd, len_bytes, 4);
1478 len = ((len_bytes[3] << 24)
1479 | (len_bytes[2] << 16)
1480 | (len_bytes[1] << 8)
1484 lseek (fd, offs, SEEK_SET);
1488 setmode (fd, O_BINARY);
1491 return Qdopen (fd, "rbz");
1493 return Qdopen (fd, "rb");
1500 Finds the file in the search path.
1501 Sets com_filesize and one of handle or file
1504 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1506 searchpath_t *search;
1507 char netpath[MAX_OSPATH];
1509 char cachepath[MAX_OSPATH];
1515 char gzfilename[MAX_OSPATH];
1518 filenamelen = strlen (filename);
1519 sprintf (gzfilename, "%s.gz", filename);
1522 Sys_Error ("COM_FindFile: file not set");
1525 // search through the path, one element at a time
1527 search = com_searchpaths;
1529 // { // gross hack to use quake 1 progs with quake 2 maps
1530 // if (!strcmp(filename, "progs.dat"))
1531 // search = search->next;
1534 for ( ; search ; search = search->next)
1536 // is the element a pak file?
1539 // look through all the pak file elements
1541 for (i=0 ; i<pak->numfiles ; i++)
1542 if (!strcmp (pak->files[i].name, filename)
1543 || !strcmp (pak->files[i].name, gzfilename))
1546 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1547 // open a new file on the pakfile
1548 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1549 return com_filesize;
1554 // check a file in the directory tree
1555 // if (!static_registered)
1556 // { // if not a registered version, don't ever go beyond base
1557 // if ( strchr (filename, '/') || strchr (filename,'\\'))
1561 sprintf (netpath, "%s/%s",search->filename, filename);
1563 findtime = Sys_FileTime (netpath);
1568 // see if the file needs to be updated in the cache
1569 if (com_cachedir[0])
1572 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1573 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1575 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1577 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1580 cachetime = Sys_FileTime (cachepath);
1582 if (cachetime < findtime)
1583 COM_CopyFile (netpath, cachepath);
1584 strcpy (netpath, cachepath);
1589 Sys_Printf ("FindFile: %s\n",netpath);
1590 *file = COM_OpenRead (netpath, -1, -1, zip);
1591 return com_filesize;
1597 Sys_Printf ("FindFile: can't find %s\n", filename);
1609 If the requested file is inside a packfile, a new QFile * will be opened
1613 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1615 return COM_FindFile (filename, file, quiet, zip);
1623 Filename are reletive to the quake directory.
1624 Always appends a 0 byte.
1629 byte *COM_LoadFile (char *path, qboolean quiet)
1636 buf = NULL; // quiet compiler warning
1639 // look for it in the filesystem or pack files
1640 len = COM_FOpenFile (path, &h, quiet, true);
1646 // extract the filename base name for hunk tag
1647 COM_FileBase (path, base);
1649 buf = Mem_Alloc(tempmempool, len+1);
1651 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1653 ((byte *)buf)[len] = 0;
1655 Qread (h, buf, len);
1665 Takes an explicit (not game tree related) path to a pak file.
1667 Loads the header and directory, adding the files at the beginning
1668 of the list so they override previous pack files.
1671 pack_t *COM_LoadPackFile (char *packfile)
1673 dpackheader_t header;
1678 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1681 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1683 //Con_Printf ("Couldn't open %s\n", packfile);
1686 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1687 if (memcmp(header.id, "PACK", 4))
1688 Sys_Error ("%s is not a packfile", packfile);
1689 header.dirofs = LittleLong (header.dirofs);
1690 header.dirlen = LittleLong (header.dirlen);
1692 if (header.dirlen % sizeof(dpackfile_t))
1693 Sys_Error ("%s has an invalid directory size", packfile);
1695 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1697 if (numpackfiles > MAX_FILES_IN_PACK)
1698 Sys_Error ("%s has %i files", packfile, numpackfiles);
1700 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1701 strcpy (pack->filename, packfile);
1702 pack->handle = packhandle;
1703 pack->numfiles = numpackfiles;
1704 pack->mempool = Mem_AllocPool(packfile);
1705 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1706 pack->next = packlist;
1709 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1710 Sys_FileSeek (packhandle, header.dirofs);
1711 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1713 // parse the directory
1714 for (i = 0;i < numpackfiles;i++)
1716 strcpy (pack->files[i].name, info[i].name);
1717 pack->files[i].filepos = LittleLong(info[i].filepos);
1718 pack->files[i].filelen = LittleLong(info[i].filelen);
1723 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1730 COM_AddGameDirectory
1732 Sets com_gamedir, adds the directory to the head of the path,
1733 then loads and adds pak1.pak pak2.pak ...
1736 void COM_AddGameDirectory (char *dir)
1739 searchpath_t *search;
1741 char pakfile[MAX_OSPATH];
1743 strcpy (com_gamedir, dir);
1746 // add the directory to the search path
1748 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1749 strcpy (search->filename, dir);
1750 search->next = com_searchpaths;
1751 com_searchpaths = search;
1754 // add any pak files in the format pak0.pak pak1.pak, ...
1758 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1759 pak = COM_LoadPackFile (pakfile);
1762 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1764 search->next = com_searchpaths;
1765 com_searchpaths = search;
1769 // add the contents of the parms.txt file to the end of the command line
1779 void COM_InitFilesystem (void)
1782 char basedir[MAX_OSPATH];
1783 searchpath_t *search;
1787 // Overrides the system supplied base directory (under GAMENAME)
1789 i = COM_CheckParm ("-basedir");
1790 if (i && i < com_argc-1)
1791 strcpy (basedir, com_argv[i+1]);
1793 strcpy (basedir, host_parms.basedir);
1795 j = strlen (basedir);
1799 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1806 // Overrides the system supplied cache directory (NULL or /qcache)
1807 // -cachedir - will disable caching.
1809 i = COM_CheckParm ("-cachedir");
1810 if (i && i < com_argc-1)
1812 if (com_argv[i+1][0] == '-')
1813 com_cachedir[0] = 0;
1815 strcpy (com_cachedir, com_argv[i+1]);
1817 else if (host_parms.cachedir)
1818 strcpy (com_cachedir, host_parms.cachedir);
1820 com_cachedir[0] = 0;
1823 // start up with GAMENAME by default (id1)
1824 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1831 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1834 COM_AddGameDirectory (va("%s/rogue", basedir) );
1837 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1839 case GAME_FIENDARENA:
1840 COM_AddGameDirectory (va("%s/fiendarena", basedir) );
1843 COM_AddGameDirectory (va("%s/zymotic", basedir) );
1846 Sys_Error("COM_InitFilesystem: unknown gamemode %i\n", gamemode);
1852 // Adds basedir/gamedir as an override game
1854 i = COM_CheckParm ("-game");
1855 if (i && i < com_argc-1)
1857 com_modified = true;
1858 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1862 // -path <dir or packfile> [<dir or packfile>] ...
1863 // Fully specifies the exact search path, overriding the generated one
1865 i = COM_CheckParm ("-path");
1868 com_modified = true;
1869 com_searchpaths = NULL;
1870 while (++i < com_argc)
1872 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1875 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1876 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1878 search->pack = COM_LoadPackFile (com_argv[i]);
1880 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1883 strcpy (search->filename, com_argv[i]);
1884 search->next = com_searchpaths;
1885 com_searchpaths = search;
1889 // if (COM_CheckParm ("-proghack"))
1893 int COM_FileExists(char *filename)
1895 searchpath_t *search;
1896 char netpath[MAX_OSPATH];
1901 for (search = com_searchpaths;search;search = search->next)
1906 for (i = 0;i < pak->numfiles;i++)
1907 if (!strcmp (pak->files[i].name, filename))
1912 sprintf (netpath, "%s/%s",search->filename, filename);
1913 findtime = Sys_FileTime (netpath);
1923 //======================================
1924 // LordHavoc: added these because they are useful
1926 void COM_ToLowerString(char *in, char *out)
1930 if (*in >= 'A' && *in <= 'Z')
1931 *out++ = *in++ + 'a' - 'A';
1937 void COM_ToUpperString(char *in, char *out)
1941 if (*in >= 'a' && *in <= 'z')
1942 *out++ = *in++ + 'A' - 'a';