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 ((qbyte *)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 ((qbyte *)dest)[i] = ((qbyte *)src)[i];
130 int Q_memcmp (void *m1, void *m2, int count)
135 if (((qbyte *)m1)[count] != ((qbyte *)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 latest dpprotocol)
553 void MSG_WriteDPCoord (sizebuf_t *sb, float f)
556 MSG_WriteShort (sb, (int)(f + 0.5f));
558 MSG_WriteShort (sb, (int)(f - 0.5f));
561 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
564 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
566 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
569 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
570 void MSG_WriteAngle (sizebuf_t *sb, float f)
573 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
575 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
582 qboolean msg_badread;
584 void MSG_BeginReading (void)
591 // returns -1 and sets msg_badread if no more characters are available
592 int MSG_ReadChar (void)
596 // LordHavoc: minor optimization
597 if (msg_readcount >= net_message.cursize)
598 // if (msg_readcount+1 > net_message.cursize)
604 c = (signed char)net_message.data[msg_readcount];
610 int MSG_ReadByte (void)
614 // LordHavoc: minor optimization
615 if (msg_readcount >= net_message.cursize)
616 // if (msg_readcount+1 > net_message.cursize)
622 c = (unsigned char)net_message.data[msg_readcount];
629 int MSG_ReadShort (void)
633 if (msg_readcount+2 > net_message.cursize)
639 c = (short)(net_message.data[msg_readcount]
640 + (net_message.data[msg_readcount+1]<<8));
647 int MSG_ReadLong (void)
651 if (msg_readcount+4 > net_message.cursize)
657 c = net_message.data[msg_readcount]
658 + (net_message.data[msg_readcount+1]<<8)
659 + (net_message.data[msg_readcount+2]<<16)
660 + (net_message.data[msg_readcount+3]<<24);
667 float MSG_ReadFloat (void)
676 dat.b[0] = net_message.data[msg_readcount];
677 dat.b[1] = net_message.data[msg_readcount+1];
678 dat.b[2] = net_message.data[msg_readcount+2];
679 dat.b[3] = net_message.data[msg_readcount+3];
682 dat.l = LittleLong (dat.l);
687 char *MSG_ReadString (void)
689 static char string[2048];
696 if (c == -1 || c == 0)
700 } while (l < sizeof(string)-1);
707 // used by server (always latest dpprotocol)
708 float MSG_ReadDPCoord (void)
710 return (signed short) MSG_ReadShort();
714 float MSG_ReadCoord (void)
716 if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
717 return (signed short) MSG_ReadShort();
718 else if (dpprotocol == DPPROTOCOL_VERSION1)
719 return MSG_ReadFloat();
721 return MSG_ReadShort() * (1.0f/8.0f);
725 float MSG_ReadCoord (void)
727 return MSG_ReadShort() * (1.0f/8.0f);
730 float MSG_ReadAngle (void)
732 return MSG_ReadChar() * (360.0f/256.0f);
735 float MSG_ReadPreciseAngle (void)
737 return MSG_ReadShort() * (360.0f/65536);
742 //===========================================================================
744 void SZ_Alloc (sizebuf_t *buf, int startsize, char *name)
748 buf->mempool = Mem_AllocPool(name);
749 buf->data = Mem_Alloc(buf->mempool, startsize);
750 buf->maxsize = startsize;
755 void SZ_Free (sizebuf_t *buf)
757 Mem_FreePool(&buf->mempool);
763 void SZ_Clear (sizebuf_t *buf)
768 void *SZ_GetSpace (sizebuf_t *buf, int length)
772 if (buf->cursize + length > buf->maxsize)
774 if (!buf->allowoverflow)
775 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
777 if (length > buf->maxsize)
778 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
780 buf->overflowed = true;
781 Con_Printf ("SZ_GetSpace: overflow");
785 data = buf->data + buf->cursize;
786 buf->cursize += length;
791 void SZ_Write (sizebuf_t *buf, void *data, int length)
793 memcpy (SZ_GetSpace(buf,length),data,length);
796 void SZ_Print (sizebuf_t *buf, char *data)
800 len = strlen(data)+1;
802 // byte * cast to keep VC++ happy
803 if (buf->data[buf->cursize-1])
804 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
806 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
810 //============================================================================
818 char *COM_SkipPath (char *pathname)
837 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
838 void COM_StripExtension (char *in, char *out)
845 else if (*in == '/' || *in == '\\' || *in == ':')
858 char *COM_FileExtension (char *in)
860 static char exten[8];
863 while (*in && *in != '.')
868 for (i=0 ; i<7 && *in ; i++,in++)
879 void COM_FileBase (char *in, char *out)
898 strcpy (out,"?model?");
913 void COM_DefaultExtension (char *path, char *extension)
917 // if path doesn't have a .EXT, append extension
918 // (extension should include the .)
920 src = path + strlen(path) - 1;
922 while (*src != '/' && src != path)
925 return; // it has an extension
929 strcat (path, extension);
937 Parse a token out of a string
940 char *COM_Parse (char *data)
953 while ( (c = *data) <= ' ')
956 return NULL; // end of file;
961 if (c=='/' && data[1] == '/')
963 while (*data && *data != '\n')
969 // handle quoted strings specially
986 // parse single characters
987 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
995 // parse a regular word
1002 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1015 Returns the position (1 to argc-1) in the program's argument list
1016 where the given parameter apears, or 0 if not present
1019 int COM_CheckParm (char *parm)
1023 for (i=1 ; i<com_argc ; i++)
1026 continue; // NEXTSTEP sometimes clears appkit vars.
1027 if (!strcmp (parm,com_argv[i]))
1038 Looks for the pop.txt file and verifies it.
1039 Sets the "registered" cvar.
1040 Immediately exits out if an alternate game was attempted to be started without
1044 void COM_CheckRegistered (void)
1046 Cvar_Set ("cmdline", com_cmdline);
1048 // static_registered = 0;
1050 if (!Sys_FileTime("gfx/pop.lmp"))
1053 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1055 Con_Printf ("Playing shareware version.\n");
1057 // Sys_Error ("This dedicated server requires a full registered copy of Quake");
1059 // Con_Printf ("Playing shareware version.\n");
1060 // if (com_modified)
1061 // Sys_Error ("You must have the registered version to use modified games");
1065 // Cvar_Set ("cmdline", com_cmdline);
1066 Cvar_Set ("registered", "1");
1067 // static_registered = 1;
1068 Con_Printf ("Playing registered version.\n");
1072 void COM_Path_f (void);
1080 void COM_InitArgv (int argc, char **argv)
1085 // reconstitute the command line for the cmdline externally visible cvar
1088 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1092 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1094 com_cmdline[n++] = argv[j][i++];
1097 if (n < (CMDLINE_LENGTH - 1))
1098 com_cmdline[n++] = ' ';
1107 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1110 largv[com_argc] = argv[com_argc];
1111 if (!strcmp ("-safe", argv[com_argc]))
1117 // force all the safe-mode switches. Note that we reserved extra space in
1118 // case we need to add these, so we don't need an overflow check
1119 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1121 largv[com_argc] = safeargvs[i];
1126 largv[com_argc] = argvdummy;
1130 gamemode = GAME_BLOODBATH;
1132 gamemode = GAME_ZYMOTIC;
1134 gamemode = GAME_FIENDARENA;
1136 gamemode = GAME_NEHAHRA;
1138 if (COM_CheckParm ("-bloodbath"))
1139 gamemode = GAME_BLOODBATH;
1140 else if (COM_CheckParm ("-zymotic"))
1141 gamemode = GAME_ZYMOTIC;
1142 else if (COM_CheckParm ("-fiendarena"))
1143 gamemode = GAME_FIENDARENA;
1144 else if (COM_CheckParm ("-nehahra"))
1145 gamemode = GAME_NEHAHRA;
1146 else if (COM_CheckParm ("-hipnotic"))
1147 gamemode = GAME_HIPNOTIC;
1148 else if (COM_CheckParm ("-rogue"))
1149 gamemode = GAME_ROGUE;
1154 gamename = "DarkPlaces";
1157 gamename = "Darkplaces-Hipnotic";
1160 gamename = "Darkplaces-Rogue";
1163 gamename = "DarkPlaces-Nehahra";
1165 case GAME_FIENDARENA:
1166 gamename = "FiendArena";
1169 gamename = "Zymotic";
1171 case GAME_BLOODBATH:
1172 gamename = "BloodBath";
1175 Sys_Error("COM_InitArgv: unknown gamemode %i\n", gamemode);
1181 extern void Mathlib_Init(void);
1188 void COM_Init (void)
1190 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
1191 qbyte swaptest[2] = {1,0};
1193 // set the byte swapping variables in a portable manner
1194 if ( *(short *)swaptest == 1)
1196 BigShort = ShortSwap;
1197 LittleShort = ShortNoSwap;
1199 LittleLong = LongNoSwap;
1200 BigFloat = FloatSwap;
1201 LittleFloat = FloatNoSwap;
1205 BigShort = ShortNoSwap;
1206 LittleShort = ShortSwap;
1207 BigLong = LongNoSwap;
1208 LittleLong = LongSwap;
1209 BigFloat = FloatNoSwap;
1210 LittleFloat = FloatSwap;
1214 pak_mempool = Mem_AllocPool("paks");
1216 Cvar_RegisterVariable (®istered);
1217 Cvar_RegisterVariable (&cmdline);
1218 Cmd_AddCommand ("path", COM_Path_f);
1222 COM_InitFilesystem ();
1223 COM_CheckRegistered ();
1231 does a varargs printf into a temp buffer, so I don't need to have
1232 varargs versions of all text functions.
1233 FIXME: make this buffer size safe someday
1236 char *va(char *format, ...)
1239 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1240 static char string[8][1024], *s;
1241 static int stringindex = 0;
1243 s = string[stringindex];
1244 stringindex = (stringindex + 1) & 7;
1245 va_start (argptr, format);
1246 vsprintf (s, format,argptr);
1253 /// just for debugging
1254 int memsearch (qbyte *start, int count, int search)
1258 for (i=0 ; i<count ; i++)
1259 if (start[i] == search)
1265 =============================================================================
1269 =============================================================================
1281 char name[MAX_QPATH];
1282 int filepos, filelen;
1285 typedef struct pack_s
1287 char filename[MAX_OSPATH];
1292 struct pack_s *next;
1301 int filepos, filelen;
1311 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
1312 #define MAX_FILES_IN_PACK 65536
1314 pack_t *packlist = NULL;
1317 char com_cachedir[MAX_OSPATH];
1319 char com_gamedir[MAX_OSPATH];
1321 typedef struct searchpath_s
1323 char filename[MAX_OSPATH];
1324 pack_t *pack; // only one of filename / pack will be used
1325 struct searchpath_s *next;
1328 searchpath_t *com_searchpaths;
1336 void COM_Path_f (void)
1340 Con_Printf ("Current search path:\n");
1341 for (s=com_searchpaths ; s ; s=s->next)
1345 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1348 Con_Printf ("%s\n", s->filename);
1356 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1359 void COM_CreatePath (char *path)
1363 for (ofs = path+1 ; *ofs ; ofs++)
1365 if (*ofs == '/' || *ofs == '\\')
1367 // create the directory
1381 The filename will be prefixed by the current game directory
1384 qboolean COM_WriteFile (char *filename, void *data, int len)
1387 char name[MAX_OSPATH];
1389 sprintf (name, "%s/%s", com_gamedir, filename);
1391 // LordHavoc: added this
1392 COM_CreatePath (name); // create directories up to the file
1394 handle = Sys_FileOpenWrite (name);
1397 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1401 Con_DPrintf ("COM_WriteFile: %s\n", name);
1402 Sys_FileWrite (handle, data, len);
1403 Sys_FileClose (handle);
1412 Copies a file over from the net to the local cache, creating any directories
1413 needed. This is for the convenience of developers using ISDN from home.
1416 void COM_CopyFile (char *netpath, char *cachepath)
1419 int remaining, count;
1422 remaining = Sys_FileOpenRead (netpath, &in);
1423 COM_CreatePath (cachepath); // create directories up to the cache file
1424 out = Sys_FileOpenWrite (cachepath);
1428 if (remaining < sizeof(buf))
1431 count = sizeof(buf);
1432 Sys_FileRead (in, buf, count);
1433 Sys_FileWrite (out, buf, count);
1438 Sys_FileClose (out);
1446 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1448 int fd = open (path, O_RDONLY);
1449 unsigned char id[2];
1450 unsigned char len_bytes[4];
1454 Sys_Error ("Couldn't open %s", path);
1457 if (offs < 0 || len < 0)
1461 len = lseek (fd, 0, SEEK_END);
1462 lseek (fd, 0, SEEK_SET);
1464 lseek (fd, offs, SEEK_SET);
1468 if (id[0] == 0x1f && id[1] == 0x8b)
1470 lseek (fd, offs + len - 4, SEEK_SET);
1471 read (fd, len_bytes, 4);
1472 len = ((len_bytes[3] << 24)
1473 | (len_bytes[2] << 16)
1474 | (len_bytes[1] << 8)
1478 lseek (fd, offs, SEEK_SET);
1482 setmode (fd, O_BINARY);
1485 return Qdopen (fd, "rbz");
1487 return Qdopen (fd, "rb");
1494 Finds the file in the search path.
1495 Sets com_filesize and one of handle or file
1498 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1500 searchpath_t *search;
1501 char netpath[MAX_OSPATH];
1503 char cachepath[MAX_OSPATH];
1509 char gzfilename[MAX_OSPATH];
1512 filenamelen = strlen (filename);
1513 sprintf (gzfilename, "%s.gz", filename);
1516 Sys_Error ("COM_FindFile: file not set");
1519 // search through the path, one element at a time
1521 search = com_searchpaths;
1523 // { // gross hack to use quake 1 progs with quake 2 maps
1524 // if (!strcmp(filename, "progs.dat"))
1525 // search = search->next;
1528 for ( ; search ; search = search->next)
1530 // is the element a pak file?
1533 // look through all the pak file elements
1535 for (i=0 ; i<pak->numfiles ; i++)
1536 if (!strcmp (pak->files[i].name, filename)
1537 || !strcmp (pak->files[i].name, gzfilename))
1540 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1541 // open a new file on the pakfile
1542 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1543 return com_filesize;
1548 // check a file in the directory tree
1549 // if (!static_registered)
1550 // { // if not a registered version, don't ever go beyond base
1551 // if ( strchr (filename, '/') || strchr (filename,'\\'))
1555 sprintf (netpath, "%s/%s",search->filename, filename);
1557 findtime = Sys_FileTime (netpath);
1562 // see if the file needs to be updated in the cache
1563 if (com_cachedir[0])
1566 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1567 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1569 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1571 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1574 cachetime = Sys_FileTime (cachepath);
1576 if (cachetime < findtime)
1577 COM_CopyFile (netpath, cachepath);
1578 strcpy (netpath, cachepath);
1583 Sys_Printf ("FindFile: %s\n",netpath);
1584 *file = COM_OpenRead (netpath, -1, -1, zip);
1585 return com_filesize;
1591 Sys_Printf ("FindFile: can't find %s\n", filename);
1603 If the requested file is inside a packfile, a new QFile * will be opened
1607 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1609 return COM_FindFile (filename, file, quiet, zip);
1617 Filename are reletive to the quake directory.
1618 Always appends a 0 byte.
1623 qbyte *COM_LoadFile (char *path, qboolean quiet)
1630 buf = NULL; // quiet compiler warning
1633 // look for it in the filesystem or pack files
1634 len = COM_FOpenFile (path, &h, quiet, true);
1640 // extract the filename base name for hunk tag
1641 COM_FileBase (path, base);
1643 buf = Mem_Alloc(tempmempool, len+1);
1645 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1647 ((qbyte *)buf)[len] = 0;
1649 Qread (h, buf, len);
1659 Takes an explicit (not game tree related) path to a pak file.
1661 Loads the header and directory, adding the files at the beginning
1662 of the list so they override previous pack files.
1665 pack_t *COM_LoadPackFile (char *packfile)
1667 dpackheader_t header;
1672 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1675 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1677 //Con_Printf ("Couldn't open %s\n", packfile);
1680 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1681 if (memcmp(header.id, "PACK", 4))
1682 Sys_Error ("%s is not a packfile", packfile);
1683 header.dirofs = LittleLong (header.dirofs);
1684 header.dirlen = LittleLong (header.dirlen);
1686 if (header.dirlen % sizeof(dpackfile_t))
1687 Sys_Error ("%s has an invalid directory size", packfile);
1689 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1691 if (numpackfiles > MAX_FILES_IN_PACK)
1692 Sys_Error ("%s has %i files", packfile, numpackfiles);
1694 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1695 strcpy (pack->filename, packfile);
1696 pack->handle = packhandle;
1697 pack->numfiles = numpackfiles;
1698 pack->mempool = Mem_AllocPool(packfile);
1699 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1700 pack->next = packlist;
1703 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1704 Sys_FileSeek (packhandle, header.dirofs);
1705 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1707 // parse the directory
1708 for (i = 0;i < numpackfiles;i++)
1710 strcpy (pack->files[i].name, info[i].name);
1711 pack->files[i].filepos = LittleLong(info[i].filepos);
1712 pack->files[i].filelen = LittleLong(info[i].filelen);
1717 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1724 COM_AddGameDirectory
1726 Sets com_gamedir, adds the directory to the head of the path,
1727 then loads and adds pak1.pak pak2.pak ...
1730 void COM_AddGameDirectory (char *dir)
1733 stringlist_t *list, *current;
1734 searchpath_t *search;
1736 char pakfile[MAX_OSPATH];
1738 strcpy (com_gamedir, dir);
1741 // add the directory to the search path
1743 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1744 strcpy (search->filename, dir);
1745 search->next = com_searchpaths;
1746 com_searchpaths = search;
1748 // add any paks in the directory
1749 list = listdirectory(dir);
1750 for (current = list;current;current = current->next)
1752 if (matchpattern(current->text, "*.pak"))
1754 sprintf (pakfile, "%s/%s", dir, current->text);
1755 pak = COM_LoadPackFile (pakfile);
1758 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1760 search->next = com_searchpaths;
1761 com_searchpaths = search;
1764 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1767 freedirectory(list);
1771 // add any pak files in the format pak0.pak pak1.pak, ...
1775 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1776 pak = COM_LoadPackFile (pakfile);
1779 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1781 search->next = com_searchpaths;
1782 com_searchpaths = search;
1787 // add the contents of the parms.txt file to the end of the command line
1797 void COM_InitFilesystem (void)
1800 char basedir[MAX_OSPATH];
1801 searchpath_t *search;
1805 // Overrides the system supplied base directory (under GAMENAME)
1807 i = COM_CheckParm ("-basedir");
1808 if (i && i < com_argc-1)
1809 strcpy (basedir, com_argv[i+1]);
1811 strcpy (basedir, host_parms.basedir);
1813 j = strlen (basedir);
1817 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1824 // Overrides the system supplied cache directory (NULL or /qcache)
1825 // -cachedir - will disable caching.
1827 i = COM_CheckParm ("-cachedir");
1828 if (i && i < com_argc-1)
1830 if (com_argv[i+1][0] == '-')
1831 com_cachedir[0] = 0;
1833 strcpy (com_cachedir, com_argv[i+1]);
1835 else if (host_parms.cachedir)
1836 strcpy (com_cachedir, host_parms.cachedir);
1838 com_cachedir[0] = 0;
1841 // start up with GAMENAME by default (id1)
1842 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1849 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1852 COM_AddGameDirectory (va("%s/rogue", basedir) );
1855 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1857 case GAME_FIENDARENA:
1858 COM_AddGameDirectory (va("%s/fiendarena", basedir) );
1861 COM_AddGameDirectory (va("%s/zymotic", basedir) );
1863 case GAME_BLOODBATH:
1864 COM_AddGameDirectory (va("%s/bb", basedir) );
1867 Sys_Error("COM_InitFilesystem: unknown gamemode %i\n", gamemode);
1873 // Adds basedir/gamedir as an override game
1875 i = COM_CheckParm ("-game");
1876 if (i && i < com_argc-1)
1878 com_modified = true;
1879 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1883 // -path <dir or packfile> [<dir or packfile>] ...
1884 // Fully specifies the exact search path, overriding the generated one
1886 i = COM_CheckParm ("-path");
1889 com_modified = true;
1890 com_searchpaths = NULL;
1891 while (++i < com_argc)
1893 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1896 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1897 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1899 search->pack = COM_LoadPackFile (com_argv[i]);
1901 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1904 strcpy (search->filename, com_argv[i]);
1905 search->next = com_searchpaths;
1906 com_searchpaths = search;
1910 // if (COM_CheckParm ("-proghack"))
1914 int COM_FileExists(char *filename)
1916 searchpath_t *search;
1917 char netpath[MAX_OSPATH];
1922 for (search = com_searchpaths;search;search = search->next)
1927 for (i = 0;i < pak->numfiles;i++)
1928 if (!strcmp (pak->files[i].name, filename))
1933 sprintf (netpath, "%s/%s",search->filename, filename);
1934 findtime = Sys_FileTime (netpath);
1944 //======================================
1945 // LordHavoc: added these because they are useful
1947 void COM_ToLowerString(char *in, char *out)
1951 if (*in >= 'A' && *in <= 'Z')
1952 *out++ = *in++ + 'a' - 'A';
1958 void COM_ToUpperString(char *in, char *out)
1962 if (*in >= 'a' && *in <= 'z')
1963 *out++ = *in++ + 'A' - 'a';