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 < 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 SZ_HexDumpToConsole(const sizebuf_t *buf)
474 char *cur, *flushpointer;
476 flushpointer = text + 512;
477 for (i = 0;i < buf->cursize;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[(buf->data[i] >> 4) & 15] | 0x80;
495 *cur++ = hexchar[(buf->data[i] >> 0) & 15] | 0x80;
499 *cur++ = hexchar[(buf->data[i] >> 4) & 15];
500 *cur++ = hexchar[(buf->data[i] >> 0) & 15];
502 if (cur >= flushpointer)
505 Con_Printf("%s", text);
514 Con_Printf("%s", text);
519 //============================================================================
527 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
528 void COM_StripExtension (const char *in, char *out)
535 else if (*in == '/' || *in == '\\' || *in == ':')
550 char *COM_FileExtension (const char *in)
552 static char exten[8];
555 while (*in && *in != '.')
560 for (i=0 ; i<7 && *in ; i++,in++)
571 void COM_FileBase (const char *in, char *out)
573 const char *slash, *dot, *s;
589 strcpy (out,"?model?");
604 void COM_DefaultExtension (char *path, const char *extension)
608 // if path doesn't have a .EXT, append extension
609 // (extension should include the .)
611 src = path + strlen(path) - 1;
613 while (*src != '/' && src != path)
616 return; // it has an extension
620 strcat (path, extension);
628 Parse a token out of a string
631 int COM_ParseToken (const char **datapointer)
635 const char *data = *datapointer;
648 while ((c = *data) <= ' ')
660 if (c=='/' && data[1] == '/')
662 while (*data && *data != '\n')
668 // handle quoted strings specially
686 // parse single characters
687 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
692 *datapointer = data+1;
696 // parse a regular word
703 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
717 Returns the position (1 to argc-1) in the program's argument list
718 where the given parameter apears, or 0 if not present
721 int COM_CheckParm (const char *parm)
725 for (i=1 ; i<com_argc ; i++)
728 continue; // NEXTSTEP sometimes clears appkit vars.
729 if (!strcmp (parm,com_argv[i]))
740 Looks for the pop.txt file and verifies it.
741 Sets the "registered" cvar.
742 Immediately exits out if an alternate game was attempted to be started without
746 void COM_CheckRegistered (void)
748 Cvar_Set ("cmdline", com_cmdline);
750 if (!Sys_FileTime("gfx/pop.lmp"))
753 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
755 Con_Printf ("Playing shareware version.\n");
759 Cvar_Set ("registered", "1");
760 Con_Printf ("Playing registered version.\n");
764 void COM_Path_f (void);
772 void COM_InitArgv (void)
775 // reconstitute the command line for the cmdline externally visible cvar
777 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
780 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
781 com_cmdline[n++] = com_argv[j][i++];
782 if (n < (CMDLINE_LENGTH - 1))
783 com_cmdline[n++] = ' ';
790 void COM_InitGameType (void)
792 char name[MAX_OSPATH];
793 COM_StripExtension(com_argv[0], name);
794 COM_ToLowerString(name, name);
796 if (strstr(name, "transfusion"))
797 gamemode = GAME_TRANSFUSION;
798 else if (strstr(name, "nehahra"))
799 gamemode = GAME_NEHAHRA;
800 else if (strstr(name, "hipnotic"))
801 gamemode = GAME_HIPNOTIC;
802 else if (strstr(name, "rogue"))
803 gamemode = GAME_ROGUE;
805 gamemode = GAME_NORMAL;
807 if (COM_CheckParm ("-transfusion"))
808 gamemode = GAME_TRANSFUSION;
809 else if (COM_CheckParm ("-nehahra"))
810 gamemode = GAME_NEHAHRA;
811 else if (COM_CheckParm ("-hipnotic"))
812 gamemode = GAME_HIPNOTIC;
813 else if (COM_CheckParm ("-rogue"))
814 gamemode = GAME_ROGUE;
815 else if (COM_CheckParm ("-quake"))
816 gamemode = GAME_NORMAL;
821 gamename = "DarkPlaces-Quake";
825 gamename = "Darkplaces-Hipnotic";
826 gamedirname = "hipnotic";
829 gamename = "Darkplaces-Rogue";
830 gamedirname = "rogue";
833 gamename = "DarkPlaces-Nehahra";
834 gamedirname = "nehahra";
836 case GAME_TRANSFUSION:
837 gamename = "Transfusion";
838 gamedirname = "transfusion";
841 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
847 extern void Mathlib_Init(void);
856 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
857 qbyte swaptest[2] = {1,0};
859 // set the byte swapping variables in a portable manner
860 if ( *(short *)swaptest == 1)
862 BigShort = ShortSwap;
863 LittleShort = ShortNoSwap;
865 LittleLong = LongNoSwap;
866 BigFloat = FloatSwap;
867 LittleFloat = FloatNoSwap;
871 BigShort = ShortNoSwap;
872 LittleShort = ShortSwap;
873 BigLong = LongNoSwap;
874 LittleLong = LongSwap;
875 BigFloat = FloatNoSwap;
876 LittleFloat = FloatSwap;
880 pak_mempool = Mem_AllocPool("paks");
882 Cvar_RegisterVariable (®istered);
883 Cvar_RegisterVariable (&cmdline);
884 Cmd_AddCommand ("path", COM_Path_f);
888 COM_InitFilesystem ();
889 COM_CheckRegistered ();
899 does a varargs printf into a temp buffer, so I don't need to have
900 varargs versions of all text functions.
901 FIXME: make this buffer size safe someday
904 char *va(const char *format, ...)
907 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
908 static char string[8][1024], *s;
909 static int stringindex = 0;
911 s = string[stringindex];
912 stringindex = (stringindex + 1) & 7;
913 va_start (argptr, format);
914 vsprintf (s, format,argptr);
922 =============================================================================
926 =============================================================================
938 char name[MAX_QPATH];
939 int filepos, filelen;
942 typedef struct pack_s
944 char filename[MAX_OSPATH];
958 int filepos, filelen;
968 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
969 #define MAX_FILES_IN_PACK 65536
971 pack_t *packlist = NULL;
974 char com_cachedir[MAX_OSPATH];
976 char com_gamedir[MAX_OSPATH];
978 typedef struct searchpath_s
980 // only one of filename / pack will be used
981 char filename[MAX_OSPATH];
983 struct searchpath_s *next;
986 searchpath_t *com_searchpaths;
994 void COM_Path_f (void)
998 Con_Printf ("Current search path:\n");
999 for (s=com_searchpaths ; s ; s=s->next)
1003 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1006 Con_Printf ("%s\n", s->filename);
1014 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1017 void COM_CreatePath (char *path)
1021 for (ofs = path+1 ; *ofs ; ofs++)
1023 if (*ofs == '/' || *ofs == '\\')
1025 // create the directory
1039 The filename will be prefixed by the current game directory
1042 qboolean COM_WriteFile (const char *filename, void *data, int len)
1045 char name[MAX_OSPATH];
1047 sprintf (name, "%s/%s", com_gamedir, filename);
1049 // LordHavoc: added this
1050 // create directories up to the file
1051 COM_CreatePath (name);
1053 handle = Sys_FileOpenWrite (name);
1056 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1060 Con_DPrintf ("COM_WriteFile: %s\n", name);
1061 Sys_FileWrite (handle, data, len);
1062 Sys_FileClose (handle);
1071 Copies a file over from the net to the local cache, creating any directories
1072 needed. This is for the convenience of developers using ISDN from home.
1075 void COM_CopyFile (char *netpath, char *cachepath)
1077 int in, out, remaining, count;
1080 remaining = Sys_FileOpenRead (netpath, &in);
1081 COM_CreatePath (cachepath); // create directories up to the cache file
1082 out = Sys_FileOpenWrite (cachepath);
1086 if (remaining < sizeof(buf))
1089 count = sizeof(buf);
1090 Sys_FileRead (in, buf, count);
1091 Sys_FileWrite (out, buf, count);
1096 Sys_FileClose (out);
1104 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1106 int fd = open (path, O_RDONLY);
1107 unsigned char id[2], len_bytes[4];
1111 Sys_Error ("Couldn't open %s", path);
1114 if (offs < 0 || len < 0)
1118 len = lseek (fd, 0, SEEK_END);
1119 lseek (fd, 0, SEEK_SET);
1121 lseek (fd, offs, SEEK_SET);
1125 if (id[0] == 0x1f && id[1] == 0x8b)
1127 lseek (fd, offs + len - 4, SEEK_SET);
1128 read (fd, len_bytes, 4);
1129 len = ((len_bytes[3] << 24)
1130 | (len_bytes[2] << 16)
1131 | (len_bytes[1] << 8)
1135 lseek (fd, offs, SEEK_SET);
1139 setmode (fd, O_BINARY);
1142 return Qdopen (fd, "rbz");
1144 return Qdopen (fd, "rb");
1151 Finds the file in the search path.
1152 Sets com_filesize and one of handle or file
1155 int COM_FindFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1157 searchpath_t *search;
1158 char netpath[MAX_OSPATH];
1160 char cachepath[MAX_OSPATH];
1164 int i, findtime, filenamelen;
1165 char gzfilename[MAX_OSPATH];
1167 filenamelen = strlen (filename);
1168 sprintf (gzfilename, "%s.gz", filename);
1171 Sys_Error ("COM_FindFile: file not set");
1174 // search through the path, one element at a time
1176 search = com_searchpaths;
1178 for ( ; search ; search = search->next)
1180 // is the element a pak file?
1183 // look through all the pak file elements
1185 for (i=0 ; i<pak->numfiles ; i++)
1186 if (!strcmp (pak->files[i].name, filename)
1187 || !strcmp (pak->files[i].name, gzfilename))
1190 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1191 // open a new file on the pakfile
1192 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1193 return com_filesize;
1198 sprintf (netpath, "%s/%s",search->filename, filename);
1200 findtime = Sys_FileTime (netpath);
1205 // see if the file needs to be updated in the cache
1206 if (com_cachedir[0])
1209 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1210 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1212 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1214 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1217 cachetime = Sys_FileTime (cachepath);
1219 if (cachetime < findtime)
1220 COM_CopyFile (netpath, cachepath);
1221 strcpy (netpath, cachepath);
1225 Sys_Printf ("FindFile: %s\n",netpath);
1226 *file = COM_OpenRead (netpath, -1, -1, zip);
1227 return com_filesize;
1232 Sys_Printf ("FindFile: can't find %s\n", filename);
1244 If the requested file is inside a packfile, a new QFile * will be opened
1248 int COM_FOpenFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1250 return COM_FindFile (filename, file, quiet, zip);
1258 Filename are reletive to the quake directory.
1259 Always appends a 0 byte.
1264 qbyte *COM_LoadFile (const char *path, qboolean quiet)
1271 buf = NULL; // quiet compiler warning
1274 // look for it in the filesystem or pack files
1275 len = COM_FOpenFile (path, &h, quiet, true);
1281 // extract the filename base name for hunk tag
1282 COM_FileBase (path, base);
1284 buf = Mem_Alloc(tempmempool, len+1);
1286 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1288 ((qbyte *)buf)[len] = 0;
1290 Qread (h, buf, len);
1300 Takes an explicit (not game tree related) path to a pak file.
1302 Loads the header and directory, adding the files at the beginning
1303 of the list so they override previous pack files.
1306 pack_t *COM_LoadPackFile (const char *packfile)
1308 dpackheader_t header;
1309 int i, numpackfiles, packhandle;
1311 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1314 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1317 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1318 if (memcmp(header.id, "PACK", 4))
1319 Sys_Error ("%s is not a packfile", packfile);
1320 header.dirofs = LittleLong (header.dirofs);
1321 header.dirlen = LittleLong (header.dirlen);
1323 if (header.dirlen % sizeof(dpackfile_t))
1324 Sys_Error ("%s has an invalid directory size", packfile);
1326 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1328 if (numpackfiles > MAX_FILES_IN_PACK)
1329 Sys_Error ("%s has %i files", packfile, numpackfiles);
1331 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1332 strcpy (pack->filename, packfile);
1333 pack->handle = packhandle;
1334 pack->numfiles = numpackfiles;
1335 pack->mempool = Mem_AllocPool(packfile);
1336 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1337 pack->next = packlist;
1340 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1341 Sys_FileSeek (packhandle, header.dirofs);
1342 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1344 // parse the directory
1345 for (i = 0;i < numpackfiles;i++)
1347 strcpy (pack->files[i].name, info[i].name);
1348 pack->files[i].filepos = LittleLong(info[i].filepos);
1349 pack->files[i].filelen = LittleLong(info[i].filelen);
1354 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1361 COM_AddGameDirectory
1363 Sets com_gamedir, adds the directory to the head of the path,
1364 then loads and adds pak1.pak pak2.pak ...
1367 void COM_AddGameDirectory (char *dir)
1369 stringlist_t *list, *current;
1370 searchpath_t *search;
1372 char pakfile[MAX_OSPATH];
1374 strcpy (com_gamedir, dir);
1377 // add the directory to the search path
1379 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1380 strcpy (search->filename, dir);
1381 search->next = com_searchpaths;
1382 com_searchpaths = search;
1384 // add any paks in the directory
1385 list = listdirectory(dir);
1386 for (current = list;current;current = current->next)
1388 if (matchpattern(current->text, "*.pak", true))
1390 sprintf (pakfile, "%s/%s", dir, current->text);
1391 pak = COM_LoadPackFile (pakfile);
1394 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1396 search->next = com_searchpaths;
1397 com_searchpaths = search;
1400 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1403 freedirectory(list);
1411 void COM_InitFilesystem (void)
1414 searchpath_t *search;
1416 strcpy(com_basedir, ".");
1419 // Overrides the system supplied base directory (under GAMENAME)
1420 i = COM_CheckParm ("-basedir");
1421 if (i && i < com_argc-1)
1422 strcpy (com_basedir, com_argv[i+1]);
1424 i = strlen (com_basedir);
1425 if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1426 com_basedir[i-1] = 0;
1428 // start up with GAMENAME by default (id1)
1429 strcpy(com_modname, GAMENAME);
1430 COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1433 com_modified = true;
1434 strcpy(com_modname, gamedirname);
1435 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1439 // Adds basedir/gamedir as an override game
1440 i = COM_CheckParm ("-game");
1441 if (i && i < com_argc-1)
1443 com_modified = true;
1444 strcpy(com_modname, com_argv[i+1]);
1445 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1448 // -path <dir or packfile> [<dir or packfile>] ...
1449 // Fully specifies the exact search path, overriding the generated one
1450 i = COM_CheckParm ("-path");
1453 com_modified = true;
1454 com_searchpaths = NULL;
1455 while (++i < com_argc)
1457 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1460 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1461 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1463 search->pack = COM_LoadPackFile (com_argv[i]);
1465 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1468 strcpy (search->filename, com_argv[i]);
1469 search->next = com_searchpaths;
1470 com_searchpaths = search;
1475 int COM_FileExists(const char *filename)
1477 searchpath_t *search;
1478 char netpath[MAX_OSPATH];
1482 for (search = com_searchpaths;search;search = search->next)
1487 for (i = 0;i < pak->numfiles;i++)
1488 if (!strcmp (pak->files[i].name, filename))
1493 sprintf (netpath, "%s/%s",search->filename, filename);
1494 findtime = Sys_FileTime (netpath);
1504 //======================================
1505 // LordHavoc: added these because they are useful
1507 void COM_ToLowerString(const char *in, char *out)
1511 if (*in >= 'A' && *in <= 'Z')
1512 *out++ = *in++ + 'a' - 'A';
1518 void COM_ToUpperString(const char *in, char *out)
1522 if (*in >= 'a' && *in <= 'z')
1523 *out++ = *in++ + 'A' - 'a';
1529 int COM_StringBeginsWith(const char *s, const char *match)
1531 for (;*s && *match;s++, match++)