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));
562 void MSG_WriteCoord (sizebuf_t *sb, float f)
564 if (dpprotocol == DPPROTOCOL_VERSION2)
567 MSG_WriteShort (sb, (int)(f + 0.5f));
569 MSG_WriteShort (sb, (int)(f - 0.5f));
571 else if (dpprotocol == DPPROTOCOL_VERSION1)
572 MSG_WriteFloat(sb, f);
576 MSG_WriteShort (sb, (int)(f*8.0f + 0.5f));
578 MSG_WriteShort (sb, (int)(f*8.0f - 0.5f));
582 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
585 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
587 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
590 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
591 void MSG_WriteAngle (sizebuf_t *sb, float f)
594 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
596 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
603 qboolean msg_badread;
605 void MSG_BeginReading (void)
612 // returns -1 and sets msg_badread if no more characters are available
613 int MSG_ReadChar (void)
617 // LordHavoc: minor optimization
618 if (msg_readcount >= net_message.cursize)
619 // if (msg_readcount+1 > net_message.cursize)
625 c = (signed char)net_message.data[msg_readcount];
631 int MSG_ReadByte (void)
635 // LordHavoc: minor optimization
636 if (msg_readcount >= net_message.cursize)
637 // if (msg_readcount+1 > net_message.cursize)
643 c = (unsigned char)net_message.data[msg_readcount];
650 int MSG_ReadShort (void)
654 if (msg_readcount+2 > net_message.cursize)
660 c = (short)(net_message.data[msg_readcount]
661 + (net_message.data[msg_readcount+1]<<8));
668 int MSG_ReadLong (void)
672 if (msg_readcount+4 > net_message.cursize)
678 c = net_message.data[msg_readcount]
679 + (net_message.data[msg_readcount+1]<<8)
680 + (net_message.data[msg_readcount+2]<<16)
681 + (net_message.data[msg_readcount+3]<<24);
688 float MSG_ReadFloat (void)
697 dat.b[0] = net_message.data[msg_readcount];
698 dat.b[1] = net_message.data[msg_readcount+1];
699 dat.b[2] = net_message.data[msg_readcount+2];
700 dat.b[3] = net_message.data[msg_readcount+3];
703 dat.l = LittleLong (dat.l);
708 char *MSG_ReadString (void)
710 static char string[2048];
717 if (c == -1 || c == 0)
721 } while (l < sizeof(string)-1);
728 // used by server (always latest dpprotocol)
729 float MSG_ReadDPCoord (void)
731 return (signed short) MSG_ReadShort();
735 float MSG_ReadCoord (void)
737 if (dpprotocol == DPPROTOCOL_VERSION2)
738 return (signed short) MSG_ReadShort();
739 else if (dpprotocol == DPPROTOCOL_VERSION1)
740 return MSG_ReadFloat();
742 return MSG_ReadShort() * (1.0f/8.0f);
746 float MSG_ReadCoord (void)
748 return MSG_ReadShort() * (1.0f/8.0f);
751 float MSG_ReadAngle (void)
753 return MSG_ReadChar() * (360.0f/256.0f);
756 float MSG_ReadPreciseAngle (void)
758 return MSG_ReadShort() * (360.0f/65536);
763 //===========================================================================
765 void SZ_Alloc (sizebuf_t *buf, int startsize, char *name)
769 buf->mempool = Mem_AllocPool(name);
770 buf->data = Mem_Alloc(buf->mempool, startsize);
771 buf->maxsize = startsize;
776 void SZ_Free (sizebuf_t *buf)
778 Mem_FreePool(&buf->mempool);
784 void SZ_Clear (sizebuf_t *buf)
789 void *SZ_GetSpace (sizebuf_t *buf, int length)
793 if (buf->cursize + length > buf->maxsize)
795 if (!buf->allowoverflow)
796 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
798 if (length > buf->maxsize)
799 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
801 buf->overflowed = true;
802 Con_Printf ("SZ_GetSpace: overflow");
806 data = buf->data + buf->cursize;
807 buf->cursize += length;
812 void SZ_Write (sizebuf_t *buf, void *data, int length)
814 memcpy (SZ_GetSpace(buf,length),data,length);
817 void SZ_Print (sizebuf_t *buf, char *data)
821 len = strlen(data)+1;
823 // byte * cast to keep VC++ happy
824 if (buf->data[buf->cursize-1])
825 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
827 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
831 //============================================================================
839 char *COM_SkipPath (char *pathname)
858 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
859 void COM_StripExtension (char *in, char *out)
866 if ((*in == '/') || (*in == '\\') || (*in == ':'))
879 char *COM_FileExtension (char *in)
881 static char exten[8];
884 while (*in && *in != '.')
889 for (i=0 ; i<7 && *in ; i++,in++)
900 void COM_FileBase (char *in, char *out)
919 strcpy (out,"?model?");
934 void COM_DefaultExtension (char *path, char *extension)
938 // if path doesn't have a .EXT, append extension
939 // (extension should include the .)
941 src = path + strlen(path) - 1;
943 while (*src != '/' && src != path)
946 return; // it has an extension
950 strcat (path, extension);
958 Parse a token out of a string
961 char *COM_Parse (char *data)
974 while ( (c = *data) <= ' ')
977 return NULL; // end of file;
982 if (c=='/' && data[1] == '/')
984 while (*data && *data != '\n')
990 // handle quoted strings specially
1007 // parse single characters
1008 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1016 // parse a regular word
1023 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1036 Returns the position (1 to argc-1) in the program's argument list
1037 where the given parameter apears, or 0 if not present
1040 int COM_CheckParm (char *parm)
1044 for (i=1 ; i<com_argc ; i++)
1047 continue; // NEXTSTEP sometimes clears appkit vars.
1048 if (!strcmp (parm,com_argv[i]))
1059 Looks for the pop.txt file and verifies it.
1060 Sets the "registered" cvar.
1061 Immediately exits out if an alternate game was attempted to be started without
1065 void COM_CheckRegistered (void)
1067 Cvar_Set ("cmdline", com_cmdline);
1069 // static_registered = 0;
1071 if (!Sys_FileTime("gfx/pop.lmp"))
1074 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1076 Con_Printf ("Playing shareware version.\n");
1078 // Sys_Error ("This dedicated server requires a full registered copy of Quake");
1080 // Con_Printf ("Playing shareware version.\n");
1081 // if (com_modified)
1082 // Sys_Error ("You must have the registered version to use modified games");
1086 // Cvar_Set ("cmdline", com_cmdline);
1087 Cvar_Set ("registered", "1");
1088 // static_registered = 1;
1089 Con_Printf ("Playing registered version.\n");
1093 void COM_Path_f (void);
1101 void COM_InitArgv (int argc, char **argv)
1106 // reconstitute the command line for the cmdline externally visible cvar
1109 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1113 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1115 com_cmdline[n++] = argv[j][i++];
1118 if (n < (CMDLINE_LENGTH - 1))
1119 com_cmdline[n++] = ' ';
1128 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1131 largv[com_argc] = argv[com_argc];
1132 if (!strcmp ("-safe", argv[com_argc]))
1138 // force all the safe-mode switches. Note that we reserved extra space in
1139 // case we need to add these, so we don't need an overflow check
1140 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1142 largv[com_argc] = safeargvs[i];
1147 largv[com_argc] = argvdummy;
1151 gamemode = GAME_ZYMOTIC;
1153 gamemode = GAME_FIENDARENA;
1155 gamemode = GAME_NEHAHRA;
1157 if (COM_CheckParm ("-zymotic"))
1158 gamemode = GAME_ZYMOTIC;
1159 else if (COM_CheckParm ("-fiendarena"))
1160 gamemode = GAME_FIENDARENA;
1161 else if (COM_CheckParm ("-nehahra"))
1162 gamemode = GAME_NEHAHRA;
1163 else if (COM_CheckParm ("-hipnotic"))
1164 gamemode = GAME_HIPNOTIC;
1165 else if (COM_CheckParm ("-rogue"))
1166 gamemode = GAME_ROGUE;
1171 gamename = "DarkPlaces";
1174 gamename = "Darkplaces-Hipnotic";
1177 gamename = "Darkplaces-Rogue";
1180 gamename = "DarkPlaces-Nehahra";
1182 case GAME_FIENDARENA:
1183 gamename = "FiendArena";
1186 gamename = "Zymotic";
1189 Sys_Error("COM_InitArgv: unknown gamemode %i\n", gamemode);
1195 extern void Mathlib_Init(void);
1202 void COM_Init (void)
1204 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
1205 qbyte swaptest[2] = {1,0};
1207 // set the byte swapping variables in a portable manner
1208 if ( *(short *)swaptest == 1)
1210 BigShort = ShortSwap;
1211 LittleShort = ShortNoSwap;
1213 LittleLong = LongNoSwap;
1214 BigFloat = FloatSwap;
1215 LittleFloat = FloatNoSwap;
1219 BigShort = ShortNoSwap;
1220 LittleShort = ShortSwap;
1221 BigLong = LongNoSwap;
1222 LittleLong = LongSwap;
1223 BigFloat = FloatNoSwap;
1224 LittleFloat = FloatSwap;
1228 pak_mempool = Mem_AllocPool("paks");
1230 Cvar_RegisterVariable (®istered);
1231 Cvar_RegisterVariable (&cmdline);
1232 Cmd_AddCommand ("path", COM_Path_f);
1236 COM_InitFilesystem ();
1237 COM_CheckRegistered ();
1245 does a varargs printf into a temp buffer, so I don't need to have
1246 varargs versions of all text functions.
1247 FIXME: make this buffer size safe someday
1250 char *va(char *format, ...)
1253 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
1254 static char string[8][1024], *s;
1255 static int stringindex = 0;
1257 s = string[stringindex];
1258 stringindex = (stringindex + 1) & 7;
1259 va_start (argptr, format);
1260 vsprintf (s, format,argptr);
1267 /// just for debugging
1268 int memsearch (qbyte *start, int count, int search)
1272 for (i=0 ; i<count ; i++)
1273 if (start[i] == search)
1279 =============================================================================
1283 =============================================================================
1295 char name[MAX_QPATH];
1296 int filepos, filelen;
1299 typedef struct pack_s
1301 char filename[MAX_OSPATH];
1306 struct pack_s *next;
1315 int filepos, filelen;
1325 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
1326 #define MAX_FILES_IN_PACK 65536
1328 pack_t *packlist = NULL;
1331 char com_cachedir[MAX_OSPATH];
1333 char com_gamedir[MAX_OSPATH];
1335 typedef struct searchpath_s
1337 char filename[MAX_OSPATH];
1338 pack_t *pack; // only one of filename / pack will be used
1339 struct searchpath_s *next;
1342 searchpath_t *com_searchpaths;
1350 void COM_Path_f (void)
1354 Con_Printf ("Current search path:\n");
1355 for (s=com_searchpaths ; s ; s=s->next)
1359 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1362 Con_Printf ("%s\n", s->filename);
1370 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1373 void COM_CreatePath (char *path)
1377 for (ofs = path+1 ; *ofs ; ofs++)
1379 if (*ofs == '/' || *ofs == '\\' || *ofs == ':')
1380 { // create the directory
1394 The filename will be prefixed by the current game directory
1397 void COM_WriteFile (char *filename, void *data, int len)
1400 char name[MAX_OSPATH];
1402 sprintf (name, "%s/%s", com_gamedir, filename);
1404 // LordHavoc: added this
1405 COM_CreatePath (name); // create directories up to the file
1407 handle = Sys_FileOpenWrite (name);
1410 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1414 Con_Printf ("COM_WriteFile: %s\n", name);
1415 Sys_FileWrite (handle, data, len);
1416 Sys_FileClose (handle);
1424 Copies a file over from the net to the local cache, creating any directories
1425 needed. This is for the convenience of developers using ISDN from home.
1428 void COM_CopyFile (char *netpath, char *cachepath)
1431 int remaining, count;
1434 remaining = Sys_FileOpenRead (netpath, &in);
1435 COM_CreatePath (cachepath); // create directories up to the cache file
1436 out = Sys_FileOpenWrite (cachepath);
1440 if (remaining < sizeof(buf))
1443 count = sizeof(buf);
1444 Sys_FileRead (in, buf, count);
1445 Sys_FileWrite (out, buf, count);
1450 Sys_FileClose (out);
1458 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1460 int fd = open (path, O_RDONLY);
1461 unsigned char id[2];
1462 unsigned char len_bytes[4];
1466 Sys_Error ("Couldn't open %s", path);
1469 if (offs < 0 || len < 0)
1473 len = lseek (fd, 0, SEEK_END);
1474 lseek (fd, 0, SEEK_SET);
1476 lseek (fd, offs, SEEK_SET);
1480 if (id[0] == 0x1f && id[1] == 0x8b)
1482 lseek (fd, offs + len - 4, SEEK_SET);
1483 read (fd, len_bytes, 4);
1484 len = ((len_bytes[3] << 24)
1485 | (len_bytes[2] << 16)
1486 | (len_bytes[1] << 8)
1490 lseek (fd, offs, SEEK_SET);
1494 setmode (fd, O_BINARY);
1497 return Qdopen (fd, "rbz");
1499 return Qdopen (fd, "rb");
1506 Finds the file in the search path.
1507 Sets com_filesize and one of handle or file
1510 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1512 searchpath_t *search;
1513 char netpath[MAX_OSPATH];
1515 char cachepath[MAX_OSPATH];
1521 char gzfilename[MAX_OSPATH];
1524 filenamelen = strlen (filename);
1525 sprintf (gzfilename, "%s.gz", filename);
1528 Sys_Error ("COM_FindFile: file not set");
1531 // search through the path, one element at a time
1533 search = com_searchpaths;
1535 // { // gross hack to use quake 1 progs with quake 2 maps
1536 // if (!strcmp(filename, "progs.dat"))
1537 // search = search->next;
1540 for ( ; search ; search = search->next)
1542 // is the element a pak file?
1545 // look through all the pak file elements
1547 for (i=0 ; i<pak->numfiles ; i++)
1548 if (!strcmp (pak->files[i].name, filename)
1549 || !strcmp (pak->files[i].name, gzfilename))
1552 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1553 // open a new file on the pakfile
1554 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1555 return com_filesize;
1560 // check a file in the directory tree
1561 // if (!static_registered)
1562 // { // if not a registered version, don't ever go beyond base
1563 // if ( strchr (filename, '/') || strchr (filename,'\\'))
1567 sprintf (netpath, "%s/%s",search->filename, filename);
1569 findtime = Sys_FileTime (netpath);
1574 // see if the file needs to be updated in the cache
1575 if (com_cachedir[0])
1578 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1579 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1581 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1583 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1586 cachetime = Sys_FileTime (cachepath);
1588 if (cachetime < findtime)
1589 COM_CopyFile (netpath, cachepath);
1590 strcpy (netpath, cachepath);
1595 Sys_Printf ("FindFile: %s\n",netpath);
1596 *file = COM_OpenRead (netpath, -1, -1, zip);
1597 return com_filesize;
1603 Sys_Printf ("FindFile: can't find %s\n", filename);
1615 If the requested file is inside a packfile, a new QFile * will be opened
1619 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1621 return COM_FindFile (filename, file, quiet, zip);
1629 Filename are reletive to the quake directory.
1630 Always appends a 0 byte.
1635 qbyte *COM_LoadFile (char *path, qboolean quiet)
1642 buf = NULL; // quiet compiler warning
1645 // look for it in the filesystem or pack files
1646 len = COM_FOpenFile (path, &h, quiet, true);
1652 // extract the filename base name for hunk tag
1653 COM_FileBase (path, base);
1655 buf = Mem_Alloc(tempmempool, len+1);
1657 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1659 ((qbyte *)buf)[len] = 0;
1661 Qread (h, buf, len);
1671 Takes an explicit (not game tree related) path to a pak file.
1673 Loads the header and directory, adding the files at the beginning
1674 of the list so they override previous pack files.
1677 pack_t *COM_LoadPackFile (char *packfile)
1679 dpackheader_t header;
1684 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1687 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1689 //Con_Printf ("Couldn't open %s\n", packfile);
1692 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1693 if (memcmp(header.id, "PACK", 4))
1694 Sys_Error ("%s is not a packfile", packfile);
1695 header.dirofs = LittleLong (header.dirofs);
1696 header.dirlen = LittleLong (header.dirlen);
1698 if (header.dirlen % sizeof(dpackfile_t))
1699 Sys_Error ("%s has an invalid directory size", packfile);
1701 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1703 if (numpackfiles > MAX_FILES_IN_PACK)
1704 Sys_Error ("%s has %i files", packfile, numpackfiles);
1706 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1707 strcpy (pack->filename, packfile);
1708 pack->handle = packhandle;
1709 pack->numfiles = numpackfiles;
1710 pack->mempool = Mem_AllocPool(packfile);
1711 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1712 pack->next = packlist;
1715 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1716 Sys_FileSeek (packhandle, header.dirofs);
1717 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1719 // parse the directory
1720 for (i = 0;i < numpackfiles;i++)
1722 strcpy (pack->files[i].name, info[i].name);
1723 pack->files[i].filepos = LittleLong(info[i].filepos);
1724 pack->files[i].filelen = LittleLong(info[i].filelen);
1729 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1736 COM_AddGameDirectory
1738 Sets com_gamedir, adds the directory to the head of the path,
1739 then loads and adds pak1.pak pak2.pak ...
1742 void COM_AddGameDirectory (char *dir)
1745 searchpath_t *search;
1747 char pakfile[MAX_OSPATH];
1749 strcpy (com_gamedir, dir);
1752 // add the directory to the search path
1754 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1755 strcpy (search->filename, dir);
1756 search->next = com_searchpaths;
1757 com_searchpaths = search;
1760 // add any pak files in the format pak0.pak pak1.pak, ...
1764 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1765 pak = COM_LoadPackFile (pakfile);
1768 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1770 search->next = com_searchpaths;
1771 com_searchpaths = search;
1775 // add the contents of the parms.txt file to the end of the command line
1785 void COM_InitFilesystem (void)
1788 char basedir[MAX_OSPATH];
1789 searchpath_t *search;
1793 // Overrides the system supplied base directory (under GAMENAME)
1795 i = COM_CheckParm ("-basedir");
1796 if (i && i < com_argc-1)
1797 strcpy (basedir, com_argv[i+1]);
1799 strcpy (basedir, host_parms.basedir);
1801 j = strlen (basedir);
1805 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1812 // Overrides the system supplied cache directory (NULL or /qcache)
1813 // -cachedir - will disable caching.
1815 i = COM_CheckParm ("-cachedir");
1816 if (i && i < com_argc-1)
1818 if (com_argv[i+1][0] == '-')
1819 com_cachedir[0] = 0;
1821 strcpy (com_cachedir, com_argv[i+1]);
1823 else if (host_parms.cachedir)
1824 strcpy (com_cachedir, host_parms.cachedir);
1826 com_cachedir[0] = 0;
1829 // start up with GAMENAME by default (id1)
1830 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1837 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1840 COM_AddGameDirectory (va("%s/rogue", basedir) );
1843 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1845 case GAME_FIENDARENA:
1846 COM_AddGameDirectory (va("%s/fiendarena", basedir) );
1849 COM_AddGameDirectory (va("%s/zymotic", basedir) );
1852 Sys_Error("COM_InitFilesystem: unknown gamemode %i\n", gamemode);
1858 // Adds basedir/gamedir as an override game
1860 i = COM_CheckParm ("-game");
1861 if (i && i < com_argc-1)
1863 com_modified = true;
1864 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1868 // -path <dir or packfile> [<dir or packfile>] ...
1869 // Fully specifies the exact search path, overriding the generated one
1871 i = COM_CheckParm ("-path");
1874 com_modified = true;
1875 com_searchpaths = NULL;
1876 while (++i < com_argc)
1878 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1881 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1882 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1884 search->pack = COM_LoadPackFile (com_argv[i]);
1886 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1889 strcpy (search->filename, com_argv[i]);
1890 search->next = com_searchpaths;
1891 com_searchpaths = search;
1895 // if (COM_CheckParm ("-proghack"))
1899 int COM_FileExists(char *filename)
1901 searchpath_t *search;
1902 char netpath[MAX_OSPATH];
1907 for (search = com_searchpaths;search;search = search->next)
1912 for (i = 0;i < pak->numfiles;i++)
1913 if (!strcmp (pak->files[i].name, filename))
1918 sprintf (netpath, "%s/%s",search->filename, filename);
1919 findtime = Sys_FileTime (netpath);
1929 //======================================
1930 // LordHavoc: added these because they are useful
1932 void COM_ToLowerString(char *in, char *out)
1936 if (*in >= 'A' && *in <= 'Z')
1937 *out++ = *in++ + 'a' - 'A';
1943 void COM_ToUpperString(char *in, char *out)
1947 if (*in >= 'a' && *in <= 'z')
1948 *out++ = *in++ + 'A' - 'a';