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 cvar_t registered = {0, "registered","0"};
33 cvar_t cmdline = {0, "cmdline","0"};
35 mempool_t *pak_mempool;
37 qboolean com_modified; // set true if using non-id files
39 void COM_InitFilesystem (void);
42 char com_basedir[MAX_OSPATH];
44 const char **com_argv;
46 // LordHavoc: made commandline 1024 characters instead of 256
47 #define CMDLINE_LENGTH 1024
48 char com_cmdline[CMDLINE_LENGTH];
53 char com_modname[MAX_OSPATH];
58 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 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
61 only used during filesystem initialization.
63 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 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
66 specified, when a file is found by the normal search path, it will be mirrored
67 into the cache directory, then opened there.
72 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.
76 //============================================================================
80 ============================================================================
82 LIBRARY REPLACEMENT FUNCTIONS
84 ============================================================================
87 int Q_strncasecmp (const char *s1, const char *s2, int n)
97 return 0; // strings are equal until end point
101 if (c1 >= 'a' && c1 <= 'z')
103 if (c2 >= 'a' && c2 <= 'z')
106 return -1; // strings not equal
109 return 0; // strings are equal
115 int Q_strcasecmp (const char *s1, const char *s2)
117 return Q_strncasecmp (s1, s2, 99999);
121 ============================================================================
125 ============================================================================
128 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
129 short (*BigShort) (short l);
130 short (*LittleShort) (short l);
131 int (*BigLong) (int l);
132 int (*LittleLong) (int l);
133 float (*BigFloat) (float l);
134 float (*LittleFloat) (float l);
137 short ShortSwap (short l)
147 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
148 short ShortNoSwap (short l)
163 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
166 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
167 int LongNoSwap (int l)
173 float FloatSwap (float f)
183 dat2.b[0] = dat1.b[3];
184 dat2.b[1] = dat1.b[2];
185 dat2.b[2] = dat1.b[1];
186 dat2.b[3] = dat1.b[0];
190 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
191 float FloatNoSwap (float f)
198 ==============================================================================
202 Handles byte ordering and avoids alignment errors
203 ==============================================================================
210 void MSG_WriteChar (sizebuf_t *sb, int c)
214 buf = SZ_GetSpace (sb, 1);
218 void MSG_WriteByte (sizebuf_t *sb, int c)
222 buf = SZ_GetSpace (sb, 1);
226 void MSG_WriteShort (sizebuf_t *sb, int c)
230 buf = SZ_GetSpace (sb, 2);
235 void MSG_WriteLong (sizebuf_t *sb, int c)
239 buf = SZ_GetSpace (sb, 4);
241 buf[1] = (c>>8)&0xff;
242 buf[2] = (c>>16)&0xff;
246 void MSG_WriteFloat (sizebuf_t *sb, float f)
256 dat.l = LittleLong (dat.l);
258 SZ_Write (sb, &dat.l, 4);
261 void MSG_WriteString (sizebuf_t *sb, const char *s)
264 SZ_Write (sb, "", 1);
266 SZ_Write (sb, s, strlen(s)+1);
269 // used by server (always latest dpprotocol)
270 void MSG_WriteDPCoord (sizebuf_t *sb, float f)
273 MSG_WriteShort (sb, (int)(f + 0.5f));
275 MSG_WriteShort (sb, (int)(f - 0.5f));
278 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
281 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
283 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
286 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
287 void MSG_WriteAngle (sizebuf_t *sb, float f)
290 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
292 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
299 qboolean msg_badread;
301 void MSG_BeginReading (void)
307 int MSG_ReadShort (void)
311 if (msg_readcount+2 > net_message.cursize)
317 c = (short)(net_message.data[msg_readcount]
318 + (net_message.data[msg_readcount+1]<<8));
325 int MSG_ReadLong (void)
329 if (msg_readcount+4 > net_message.cursize)
335 c = net_message.data[msg_readcount]
336 + (net_message.data[msg_readcount+1]<<8)
337 + (net_message.data[msg_readcount+2]<<16)
338 + (net_message.data[msg_readcount+3]<<24);
345 float MSG_ReadFloat (void)
354 dat.b[0] = net_message.data[msg_readcount];
355 dat.b[1] = net_message.data[msg_readcount+1];
356 dat.b[2] = net_message.data[msg_readcount+2];
357 dat.b[3] = net_message.data[msg_readcount+3];
360 dat.l = LittleLong (dat.l);
365 char *MSG_ReadString (void)
367 static char string[2048];
374 if (c == -1 || c == 0)
378 } while (l < (int)sizeof(string)-1);
385 // used by server (always latest dpprotocol)
386 float MSG_ReadDPCoord (void)
388 return (signed short) MSG_ReadShort();
392 float MSG_ReadCoord (void)
394 if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
395 return (signed short) MSG_ReadShort();
396 else if (dpprotocol == DPPROTOCOL_VERSION1)
397 return MSG_ReadFloat();
399 return MSG_ReadShort() * (1.0f/8.0f);
403 //===========================================================================
405 void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name)
409 buf->mempool = Mem_AllocPool(name);
410 buf->data = Mem_Alloc(buf->mempool, startsize);
411 buf->maxsize = startsize;
416 void SZ_Free (sizebuf_t *buf)
418 Mem_FreePool(&buf->mempool);
424 void SZ_Clear (sizebuf_t *buf)
429 void *SZ_GetSpace (sizebuf_t *buf, int length)
433 if (buf->cursize + length > buf->maxsize)
435 if (!buf->allowoverflow)
436 Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
438 if (length > buf->maxsize)
439 Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
441 buf->overflowed = true;
442 Con_Printf ("SZ_GetSpace: overflow\n");
446 data = buf->data + buf->cursize;
447 buf->cursize += length;
452 void SZ_Write (sizebuf_t *buf, const void *data, int length)
454 memcpy (SZ_GetSpace(buf,length),data,length);
457 void SZ_Print (sizebuf_t *buf, const char *data)
460 len = strlen(data)+1;
462 // byte * cast to keep VC++ happy
463 if (buf->data[buf->cursize-1])
464 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
466 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
469 static char *hexchar = "0123456789ABCDEF";
470 void Com_HexDumpToConsole(const qbyte *data, int size)
474 char *cur, *flushpointer;
476 flushpointer = text + 512;
477 for (i = 0;i < size;i++)
481 *cur++ = hexchar[(i >> 12) & 15];
482 *cur++ = hexchar[(i >> 8) & 15];
483 *cur++ = hexchar[(i >> 4) & 15];
484 *cur++ = hexchar[(i >> 0) & 15];
488 else if ((i & 15) == 15)
494 *cur++ = hexchar[(data[i] >> 4) & 15] | 0x80;
495 *cur++ = hexchar[(data[i] >> 0) & 15] | 0x80;
499 *cur++ = hexchar[(data[i] >> 4) & 15];
500 *cur++ = hexchar[(data[i] >> 0) & 15];
502 if (cur >= flushpointer)
505 Con_Printf("%s", text);
514 Con_Printf("%s", text);
518 void SZ_HexDumpToConsole(const sizebuf_t *buf)
520 Com_HexDumpToConsole(buf->data, buf->cursize);
524 //============================================================================
532 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
533 void COM_StripExtension (const char *in, char *out)
540 else if (*in == '/' || *in == '\\' || *in == ':')
555 char *COM_FileExtension (const char *in)
557 static char exten[8];
560 while (*in && *in != '.')
565 for (i=0 ; i<7 && *in ; i++,in++)
576 void COM_FileBase (const char *in, char *out)
578 const char *slash, *dot, *s;
594 strcpy (out,"?model?");
609 void COM_DefaultExtension (char *path, const char *extension)
613 // if path doesn't have a .EXT, append extension
614 // (extension should include the .)
616 src = path + strlen(path) - 1;
618 while (*src != '/' && src != path)
621 return; // it has an extension
625 strcat (path, extension);
633 Parse a token out of a string
636 int COM_ParseToken (const char **datapointer)
640 const char *data = *datapointer;
653 while ((c = *data) <= ' ')
665 if (c=='/' && data[1] == '/')
667 while (*data && *data != '\n')
673 // handle quoted strings specially
691 // parse single characters
692 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
697 *datapointer = data+1;
701 // parse a regular word
708 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
722 Returns the position (1 to argc-1) in the program's argument list
723 where the given parameter apears, or 0 if not present
726 int COM_CheckParm (const char *parm)
730 for (i=1 ; i<com_argc ; i++)
733 continue; // NEXTSTEP sometimes clears appkit vars.
734 if (!strcmp (parm,com_argv[i]))
745 Looks for the pop.txt file and verifies it.
746 Sets the "registered" cvar.
747 Immediately exits out if an alternate game was attempted to be started without
751 void COM_CheckRegistered (void)
753 Cvar_Set ("cmdline", com_cmdline);
755 if (!Sys_FileTime("gfx/pop.lmp"))
758 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
760 Con_Printf ("Playing shareware version.\n");
764 Cvar_Set ("registered", "1");
765 Con_Printf ("Playing registered version.\n");
769 void COM_Path_f (void);
777 void COM_InitArgv (void)
780 // reconstitute the command line for the cmdline externally visible cvar
782 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
785 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
786 com_cmdline[n++] = com_argv[j][i++];
787 if (n < (CMDLINE_LENGTH - 1))
788 com_cmdline[n++] = ' ';
795 void COM_InitGameType (void)
797 char name[MAX_OSPATH];
798 COM_StripExtension(com_argv[0], name);
799 COM_ToLowerString(name, name);
801 if (strstr(name, "transfusion"))
802 gamemode = GAME_TRANSFUSION;
803 else if (strstr(name, "nexiuz"))
804 gamemode = GAME_NEXIUZ;
805 else if (strstr(name, "nehahra"))
806 gamemode = GAME_NEHAHRA;
807 else if (strstr(name, "hipnotic"))
808 gamemode = GAME_HIPNOTIC;
809 else if (strstr(name, "rogue"))
810 gamemode = GAME_ROGUE;
812 gamemode = GAME_NORMAL;
814 if (COM_CheckParm ("-transfusion"))
815 gamemode = GAME_TRANSFUSION;
816 else if (COM_CheckParm ("-nexiuz"))
817 gamemode = GAME_NEXIUZ;
818 else if (COM_CheckParm ("-nehahra"))
819 gamemode = GAME_NEHAHRA;
820 else if (COM_CheckParm ("-hipnotic"))
821 gamemode = GAME_HIPNOTIC;
822 else if (COM_CheckParm ("-rogue"))
823 gamemode = GAME_ROGUE;
824 else if (COM_CheckParm ("-quake"))
825 gamemode = GAME_NORMAL;
830 gamename = "DarkPlaces-Quake";
834 gamename = "Darkplaces-Hipnotic";
835 gamedirname = "hipnotic";
838 gamename = "Darkplaces-Rogue";
839 gamedirname = "rogue";
842 gamename = "DarkPlaces-Nehahra";
843 gamedirname = "nehahra";
847 gamedirname = "data";
849 case GAME_TRANSFUSION:
850 gamename = "Transfusion";
851 gamedirname = "transfusion";
854 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
860 extern void Mathlib_Init(void);
869 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
870 qbyte swaptest[2] = {1,0};
872 // set the byte swapping variables in a portable manner
873 if ( *(short *)swaptest == 1)
875 BigShort = ShortSwap;
876 LittleShort = ShortNoSwap;
878 LittleLong = LongNoSwap;
879 BigFloat = FloatSwap;
880 LittleFloat = FloatNoSwap;
884 BigShort = ShortNoSwap;
885 LittleShort = ShortSwap;
886 BigLong = LongNoSwap;
887 LittleLong = LongSwap;
888 BigFloat = FloatNoSwap;
889 LittleFloat = FloatSwap;
893 pak_mempool = Mem_AllocPool("paks");
895 Cvar_RegisterVariable (®istered);
896 Cvar_RegisterVariable (&cmdline);
897 Cmd_AddCommand ("path", COM_Path_f);
901 COM_InitFilesystem ();
902 COM_CheckRegistered ();
912 does a varargs printf into a temp buffer, so I don't need to have
913 varargs versions of all text functions.
914 FIXME: make this buffer size safe someday
917 char *va(const char *format, ...)
920 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
921 static char string[8][1024], *s;
922 static int stringindex = 0;
924 s = string[stringindex];
925 stringindex = (stringindex + 1) & 7;
926 va_start (argptr, format);
927 vsprintf (s, format,argptr);
935 =============================================================================
939 =============================================================================
951 char name[MAX_QPATH];
952 int filepos, filelen;
955 typedef struct pack_s
957 char filename[MAX_OSPATH];
971 int filepos, filelen;
981 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
982 #define MAX_FILES_IN_PACK 65536
984 pack_t *packlist = NULL;
987 char com_cachedir[MAX_OSPATH];
989 char com_gamedir[MAX_OSPATH];
991 typedef struct searchpath_s
993 // only one of filename / pack will be used
994 char filename[MAX_OSPATH];
996 struct searchpath_s *next;
999 searchpath_t *com_searchpaths;
1007 void COM_Path_f (void)
1011 Con_Printf ("Current search path:\n");
1012 for (s=com_searchpaths ; s ; s=s->next)
1016 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1019 Con_Printf ("%s\n", s->filename);
1027 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1030 void COM_CreatePath (char *path)
1034 for (ofs = path+1 ; *ofs ; ofs++)
1036 if (*ofs == '/' || *ofs == '\\')
1038 // create the directory
1052 The filename will be prefixed by the current game directory
1055 qboolean COM_WriteFile (const char *filename, void *data, int len)
1058 char name[MAX_OSPATH];
1060 sprintf (name, "%s/%s", com_gamedir, filename);
1062 // LordHavoc: added this
1063 // create directories up to the file
1064 COM_CreatePath (name);
1066 handle = Sys_FileOpenWrite (name);
1069 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1073 Con_DPrintf ("COM_WriteFile: %s\n", name);
1074 Sys_FileWrite (handle, data, len);
1075 Sys_FileClose (handle);
1084 Copies a file over from the net to the local cache, creating any directories
1085 needed. This is for the convenience of developers using ISDN from home.
1088 void COM_CopyFile (char *netpath, char *cachepath)
1090 int in, out, remaining, count;
1093 remaining = Sys_FileOpenRead (netpath, &in);
1094 COM_CreatePath (cachepath); // create directories up to the cache file
1095 out = Sys_FileOpenWrite (cachepath);
1099 if (remaining < (int)sizeof(buf))
1102 count = sizeof(buf);
1103 Sys_FileRead (in, buf, count);
1104 Sys_FileWrite (out, buf, count);
1109 Sys_FileClose (out);
1117 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1119 int fd = open (path, O_RDONLY);
1120 unsigned char id[2], len_bytes[4];
1124 Sys_Error ("Couldn't open %s", path);
1127 if (offs < 0 || len < 0)
1131 len = lseek (fd, 0, SEEK_END);
1132 lseek (fd, 0, SEEK_SET);
1134 lseek (fd, offs, SEEK_SET);
1138 if (id[0] == 0x1f && id[1] == 0x8b)
1140 lseek (fd, offs + len - 4, SEEK_SET);
1141 read (fd, len_bytes, 4);
1142 len = ((len_bytes[3] << 24)
1143 | (len_bytes[2] << 16)
1144 | (len_bytes[1] << 8)
1148 lseek (fd, offs, SEEK_SET);
1152 setmode (fd, O_BINARY);
1155 return Qdopen (fd, "rbz");
1157 return Qdopen (fd, "rb");
1164 Finds the file in the search path.
1165 Sets com_filesize and one of handle or file
1168 int COM_FindFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1170 searchpath_t *search;
1171 char netpath[MAX_OSPATH];
1173 char cachepath[MAX_OSPATH];
1177 int i, findtime, filenamelen;
1178 char gzfilename[MAX_OSPATH];
1180 filenamelen = strlen (filename);
1181 sprintf (gzfilename, "%s.gz", filename);
1184 Sys_Error ("COM_FindFile: file not set");
1187 // search through the path, one element at a time
1189 search = com_searchpaths;
1191 for ( ; search ; search = search->next)
1193 // is the element a pak file?
1196 // look through all the pak file elements
1198 for (i=0 ; i<pak->numfiles ; i++)
1199 if (!strcmp (pak->files[i].name, filename)
1200 || !strcmp (pak->files[i].name, gzfilename))
1203 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1204 // open a new file on the pakfile
1205 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1206 return com_filesize;
1211 sprintf (netpath, "%s/%s",search->filename, filename);
1213 findtime = Sys_FileTime (netpath);
1218 // see if the file needs to be updated in the cache
1219 if (com_cachedir[0])
1222 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1223 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1225 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1227 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1230 cachetime = Sys_FileTime (cachepath);
1232 if (cachetime < findtime)
1233 COM_CopyFile (netpath, cachepath);
1234 strcpy (netpath, cachepath);
1238 Sys_Printf ("FindFile: %s\n",netpath);
1239 *file = COM_OpenRead (netpath, -1, -1, zip);
1240 return com_filesize;
1245 Sys_Printf ("FindFile: can't find %s\n", filename);
1257 If the requested file is inside a packfile, a new QFile * will be opened
1261 int COM_FOpenFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1263 return COM_FindFile (filename, file, quiet, zip);
1271 Filename are reletive to the quake directory.
1272 Always appends a 0 byte.
1277 qbyte *COM_LoadFile (const char *path, qboolean quiet)
1284 buf = NULL; // quiet compiler warning
1287 // look for it in the filesystem or pack files
1288 len = COM_FOpenFile (path, &h, quiet, true);
1294 // extract the filename base name for hunk tag
1295 COM_FileBase (path, base);
1297 buf = Mem_Alloc(tempmempool, len+1);
1299 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1301 ((qbyte *)buf)[len] = 0;
1303 Qread (h, buf, len);
1313 Takes an explicit (not game tree related) path to a pak file.
1315 Loads the header and directory, adding the files at the beginning
1316 of the list so they override previous pack files.
1319 pack_t *COM_LoadPackFile (const char *packfile)
1321 dpackheader_t header;
1322 int i, numpackfiles, packhandle;
1324 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1327 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1330 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1331 if (memcmp(header.id, "PACK", 4))
1332 Sys_Error ("%s is not a packfile", packfile);
1333 header.dirofs = LittleLong (header.dirofs);
1334 header.dirlen = LittleLong (header.dirlen);
1336 if (header.dirlen % sizeof(dpackfile_t))
1337 Sys_Error ("%s has an invalid directory size", packfile);
1339 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1341 if (numpackfiles > MAX_FILES_IN_PACK)
1342 Sys_Error ("%s has %i files", packfile, numpackfiles);
1344 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1345 strcpy (pack->filename, packfile);
1346 pack->handle = packhandle;
1347 pack->numfiles = numpackfiles;
1348 pack->mempool = Mem_AllocPool(packfile);
1349 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1350 pack->next = packlist;
1353 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1354 Sys_FileSeek (packhandle, header.dirofs);
1355 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1357 // parse the directory
1358 for (i = 0;i < numpackfiles;i++)
1360 strcpy (pack->files[i].name, info[i].name);
1361 pack->files[i].filepos = LittleLong(info[i].filepos);
1362 pack->files[i].filelen = LittleLong(info[i].filelen);
1367 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1374 COM_AddGameDirectory
1376 Sets com_gamedir, adds the directory to the head of the path,
1377 then loads and adds pak1.pak pak2.pak ...
1380 void COM_AddGameDirectory (char *dir)
1382 stringlist_t *list, *current;
1383 searchpath_t *search;
1385 char pakfile[MAX_OSPATH];
1387 strcpy (com_gamedir, dir);
1390 // add the directory to the search path
1392 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1393 strcpy (search->filename, dir);
1394 search->next = com_searchpaths;
1395 com_searchpaths = search;
1397 // add any paks in the directory
1398 list = listdirectory(dir);
1399 for (current = list;current;current = current->next)
1401 if (matchpattern(current->text, "*.pak", true))
1403 sprintf (pakfile, "%s/%s", dir, current->text);
1404 pak = COM_LoadPackFile (pakfile);
1407 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1409 search->next = com_searchpaths;
1410 com_searchpaths = search;
1413 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1416 freedirectory(list);
1424 void COM_InitFilesystem (void)
1427 searchpath_t *search;
1429 strcpy(com_basedir, ".");
1432 // Overrides the system supplied base directory (under GAMENAME)
1433 i = COM_CheckParm ("-basedir");
1434 if (i && i < com_argc-1)
1435 strcpy (com_basedir, com_argv[i+1]);
1437 i = strlen (com_basedir);
1438 if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1439 com_basedir[i-1] = 0;
1441 // start up with GAMENAME by default (id1)
1442 strcpy(com_modname, GAMENAME);
1443 COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1446 com_modified = true;
1447 strcpy(com_modname, gamedirname);
1448 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1452 // Adds basedir/gamedir as an override game
1453 i = COM_CheckParm ("-game");
1454 if (i && i < com_argc-1)
1456 com_modified = true;
1457 strcpy(com_modname, com_argv[i+1]);
1458 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1461 // -path <dir or packfile> [<dir or packfile>] ...
1462 // Fully specifies the exact search path, overriding the generated one
1463 i = COM_CheckParm ("-path");
1466 com_modified = true;
1467 com_searchpaths = NULL;
1468 while (++i < com_argc)
1470 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1473 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1474 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1476 search->pack = COM_LoadPackFile (com_argv[i]);
1478 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1481 strcpy (search->filename, com_argv[i]);
1482 search->next = com_searchpaths;
1483 com_searchpaths = search;
1488 int COM_FileExists(const char *filename)
1490 searchpath_t *search;
1491 char netpath[MAX_OSPATH];
1495 for (search = com_searchpaths;search;search = search->next)
1500 for (i = 0;i < pak->numfiles;i++)
1501 if (!strcmp (pak->files[i].name, filename))
1506 sprintf (netpath, "%s/%s",search->filename, filename);
1507 findtime = Sys_FileTime (netpath);
1517 //======================================
1518 // LordHavoc: added these because they are useful
1520 void COM_ToLowerString(const char *in, char *out)
1524 if (*in >= 'A' && *in <= 'Z')
1525 *out++ = *in++ + 'a' - 'A';
1531 void COM_ToUpperString(const char *in, char *out)
1535 if (*in >= 'a' && *in <= 'z')
1536 *out++ = *in++ + 'A' - 'a';
1542 int COM_StringBeginsWith(const char *s, const char *match)
1544 for (;*s && *match;s++, match++)