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 qboolean msg_suppress_1 = 0;
41 void COM_InitFilesystem (void);
44 char com_basedir[MAX_OSPATH];
46 const char **com_argv;
48 // LordHavoc: made commandline 1024 characters instead of 256
49 #define CMDLINE_LENGTH 1024
50 char com_cmdline[CMDLINE_LENGTH];
59 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.
61 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
62 only used during filesystem initialization.
64 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.
66 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
67 specified, when a file is found by the normal search path, it will be mirrored
68 into the cache directory, then opened there.
73 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.
77 //============================================================================
81 ============================================================================
83 LIBRARY REPLACEMENT FUNCTIONS
85 ============================================================================
88 int Q_strncasecmp (const char *s1, const char *s2, int n)
98 return 0; // strings are equal until end point
102 if (c1 >= 'a' && c1 <= 'z')
104 if (c2 >= 'a' && c2 <= 'z')
107 return -1; // strings not equal
110 return 0; // strings are equal
116 int Q_strcasecmp (const char *s1, const char *s2)
118 return Q_strncasecmp (s1, s2, 99999);
122 ============================================================================
126 ============================================================================
129 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
130 short (*BigShort) (short l);
131 short (*LittleShort) (short l);
132 int (*BigLong) (int l);
133 int (*LittleLong) (int l);
134 float (*BigFloat) (float l);
135 float (*LittleFloat) (float l);
138 short ShortSwap (short l)
148 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
149 short ShortNoSwap (short l)
164 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
167 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
168 int LongNoSwap (int l)
174 float FloatSwap (float f)
184 dat2.b[0] = dat1.b[3];
185 dat2.b[1] = dat1.b[2];
186 dat2.b[2] = dat1.b[1];
187 dat2.b[3] = dat1.b[0];
191 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
192 float FloatNoSwap (float f)
199 ==============================================================================
203 Handles byte ordering and avoids alignment errors
204 ==============================================================================
211 void MSG_WriteChar (sizebuf_t *sb, int c)
215 buf = SZ_GetSpace (sb, 1);
219 void MSG_WriteByte (sizebuf_t *sb, int c)
223 buf = SZ_GetSpace (sb, 1);
227 void MSG_WriteShort (sizebuf_t *sb, int c)
231 buf = SZ_GetSpace (sb, 2);
236 void MSG_WriteLong (sizebuf_t *sb, int c)
240 buf = SZ_GetSpace (sb, 4);
242 buf[1] = (c>>8)&0xff;
243 buf[2] = (c>>16)&0xff;
247 void MSG_WriteFloat (sizebuf_t *sb, float f)
257 dat.l = LittleLong (dat.l);
259 SZ_Write (sb, &dat.l, 4);
262 void MSG_WriteString (sizebuf_t *sb, const char *s)
265 SZ_Write (sb, "", 1);
267 SZ_Write (sb, s, strlen(s)+1);
270 // used by server (always latest dpprotocol)
271 void MSG_WriteDPCoord (sizebuf_t *sb, float f)
274 MSG_WriteShort (sb, (int)(f + 0.5f));
276 MSG_WriteShort (sb, (int)(f - 0.5f));
279 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
282 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
284 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
287 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
288 void MSG_WriteAngle (sizebuf_t *sb, float f)
291 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
293 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
300 qboolean msg_badread;
302 void MSG_BeginReading (void)
308 int MSG_ReadShort (void)
312 if (msg_readcount+2 > net_message.cursize)
318 c = (short)(net_message.data[msg_readcount]
319 + (net_message.data[msg_readcount+1]<<8));
326 int MSG_ReadLong (void)
330 if (msg_readcount+4 > net_message.cursize)
336 c = net_message.data[msg_readcount]
337 + (net_message.data[msg_readcount+1]<<8)
338 + (net_message.data[msg_readcount+2]<<16)
339 + (net_message.data[msg_readcount+3]<<24);
346 float MSG_ReadFloat (void)
355 dat.b[0] = net_message.data[msg_readcount];
356 dat.b[1] = net_message.data[msg_readcount+1];
357 dat.b[2] = net_message.data[msg_readcount+2];
358 dat.b[3] = net_message.data[msg_readcount+3];
361 dat.l = LittleLong (dat.l);
366 char *MSG_ReadString (void)
368 static char string[2048];
375 if (c == -1 || c == 0)
379 } while (l < sizeof(string)-1);
386 // used by server (always latest dpprotocol)
387 float MSG_ReadDPCoord (void)
389 return (signed short) MSG_ReadShort();
393 float MSG_ReadCoord (void)
395 if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
396 return (signed short) MSG_ReadShort();
397 else if (dpprotocol == DPPROTOCOL_VERSION1)
398 return MSG_ReadFloat();
400 return MSG_ReadShort() * (1.0f/8.0f);
404 //===========================================================================
406 void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name)
410 buf->mempool = Mem_AllocPool(name);
411 buf->data = Mem_Alloc(buf->mempool, startsize);
412 buf->maxsize = startsize;
417 void SZ_Free (sizebuf_t *buf)
419 Mem_FreePool(&buf->mempool);
425 void SZ_Clear (sizebuf_t *buf)
430 void *SZ_GetSpace (sizebuf_t *buf, int length)
434 if (buf->cursize + length > buf->maxsize)
436 if (!buf->allowoverflow)
437 Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
439 if (length > buf->maxsize)
440 Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
442 buf->overflowed = true;
443 Con_Printf ("SZ_GetSpace: overflow\n");
447 data = buf->data + buf->cursize;
448 buf->cursize += length;
453 void SZ_Write (sizebuf_t *buf, const void *data, int length)
455 memcpy (SZ_GetSpace(buf,length),data,length);
458 void SZ_Print (sizebuf_t *buf, const char *data)
461 len = strlen(data)+1;
463 // byte * cast to keep VC++ happy
464 if (buf->data[buf->cursize-1])
465 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
467 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
470 static char *hexchar = "0123456789ABCDEF";
471 void SZ_HexDumpToConsole(const sizebuf_t *buf)
475 char *cur, *flushpointer;
477 flushpointer = text + 512;
478 for (i = 0;i < buf->cursize;i++)
482 *cur++ = hexchar[(i >> 12) & 15];
483 *cur++ = hexchar[(i >> 8) & 15];
484 *cur++ = hexchar[(i >> 4) & 15];
485 *cur++ = hexchar[(i >> 0) & 15];
489 else if ((i & 15) == 15)
495 *cur++ = hexchar[(buf->data[i] >> 4) & 15] | 0x80;
496 *cur++ = hexchar[(buf->data[i] >> 0) & 15] | 0x80;
500 *cur++ = hexchar[(buf->data[i] >> 4) & 15];
501 *cur++ = hexchar[(buf->data[i] >> 0) & 15];
503 if (cur >= flushpointer)
506 Con_Printf("%s", text);
515 Con_Printf("%s", text);
520 //============================================================================
528 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
529 void COM_StripExtension (const char *in, char *out)
536 else if (*in == '/' || *in == '\\' || *in == ':')
551 char *COM_FileExtension (const char *in)
553 static char exten[8];
556 while (*in && *in != '.')
561 for (i=0 ; i<7 && *in ; i++,in++)
572 void COM_FileBase (const char *in, char *out)
574 const char *slash, *dot, *s;
590 strcpy (out,"?model?");
605 void COM_DefaultExtension (char *path, const char *extension)
609 // if path doesn't have a .EXT, append extension
610 // (extension should include the .)
612 src = path + strlen(path) - 1;
614 while (*src != '/' && src != path)
617 return; // it has an extension
621 strcat (path, extension);
629 Parse a token out of a string
632 int COM_ParseToken (const char **datapointer)
636 const char *data = *datapointer;
649 while ((c = *data) <= ' ')
661 if (c=='/' && data[1] == '/')
663 while (*data && *data != '\n')
669 // handle quoted strings specially
687 // parse single characters
688 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
693 *datapointer = data+1;
697 // parse a regular word
704 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
718 Returns the position (1 to argc-1) in the program's argument list
719 where the given parameter apears, or 0 if not present
722 int COM_CheckParm (const char *parm)
726 for (i=1 ; i<com_argc ; i++)
729 continue; // NEXTSTEP sometimes clears appkit vars.
730 if (!strcmp (parm,com_argv[i]))
741 Looks for the pop.txt file and verifies it.
742 Sets the "registered" cvar.
743 Immediately exits out if an alternate game was attempted to be started without
747 void COM_CheckRegistered (void)
749 Cvar_Set ("cmdline", com_cmdline);
751 if (!Sys_FileTime("gfx/pop.lmp"))
754 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
756 Con_Printf ("Playing shareware version.\n");
760 Cvar_Set ("registered", "1");
761 Con_Printf ("Playing registered version.\n");
765 void COM_Path_f (void);
773 void COM_InitArgv (void)
776 // reconstitute the command line for the cmdline externally visible cvar
778 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
781 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
782 com_cmdline[n++] = com_argv[j][i++];
783 if (n < (CMDLINE_LENGTH - 1))
784 com_cmdline[n++] = ' ';
791 void COM_InitGameType (void)
793 char name[MAX_OSPATH];
794 COM_StripExtension(com_argv[0], name);
795 COM_ToLowerString(name, name);
797 if (strstr(name, "transfusion"))
798 gamemode = GAME_TRANSFUSION;
799 else if (strstr(name, "nehahra"))
800 gamemode = GAME_NEHAHRA;
801 else if (strstr(name, "hipnotic"))
802 gamemode = GAME_HIPNOTIC;
803 else if (strstr(name, "rogue"))
804 gamemode = GAME_ROGUE;
806 gamemode = GAME_NORMAL;
808 if (COM_CheckParm ("-transfusion"))
809 gamemode = GAME_TRANSFUSION;
810 else if (COM_CheckParm ("-nehahra"))
811 gamemode = GAME_NEHAHRA;
812 else if (COM_CheckParm ("-hipnotic"))
813 gamemode = GAME_HIPNOTIC;
814 else if (COM_CheckParm ("-rogue"))
815 gamemode = GAME_ROGUE;
816 else if (COM_CheckParm ("-quake"))
817 gamemode = GAME_NORMAL;
822 gamename = "DarkPlaces-Quake";
826 gamename = "Darkplaces-Hipnotic";
827 gamedirname = "hipnotic";
830 gamename = "Darkplaces-Rogue";
831 gamedirname = "rogue";
834 gamename = "DarkPlaces-Nehahra";
835 gamedirname = "nehahra";
837 case GAME_TRANSFUSION:
838 gamename = "Transfusion";
839 gamedirname = "transfusion";
842 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
848 extern void Mathlib_Init(void);
857 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
858 qbyte swaptest[2] = {1,0};
860 // set the byte swapping variables in a portable manner
861 if ( *(short *)swaptest == 1)
863 BigShort = ShortSwap;
864 LittleShort = ShortNoSwap;
866 LittleLong = LongNoSwap;
867 BigFloat = FloatSwap;
868 LittleFloat = FloatNoSwap;
872 BigShort = ShortNoSwap;
873 LittleShort = ShortSwap;
874 BigLong = LongNoSwap;
875 LittleLong = LongSwap;
876 BigFloat = FloatNoSwap;
877 LittleFloat = FloatSwap;
881 pak_mempool = Mem_AllocPool("paks");
883 Cvar_RegisterVariable (®istered);
884 Cvar_RegisterVariable (&cmdline);
885 Cmd_AddCommand ("path", COM_Path_f);
889 COM_InitFilesystem ();
890 COM_CheckRegistered ();
900 does a varargs printf into a temp buffer, so I don't need to have
901 varargs versions of all text functions.
902 FIXME: make this buffer size safe someday
905 char *va(const char *format, ...)
908 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
909 static char string[8][1024], *s;
910 static int stringindex = 0;
912 s = string[stringindex];
913 stringindex = (stringindex + 1) & 7;
914 va_start (argptr, format);
915 vsprintf (s, format,argptr);
923 =============================================================================
927 =============================================================================
939 char name[MAX_QPATH];
940 int filepos, filelen;
943 typedef struct pack_s
945 char filename[MAX_OSPATH];
959 int filepos, filelen;
969 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
970 #define MAX_FILES_IN_PACK 65536
972 pack_t *packlist = NULL;
975 char com_cachedir[MAX_OSPATH];
977 char com_gamedir[MAX_OSPATH];
979 typedef struct searchpath_s
981 // only one of filename / pack will be used
982 char filename[MAX_OSPATH];
984 struct searchpath_s *next;
987 searchpath_t *com_searchpaths;
995 void COM_Path_f (void)
999 Con_Printf ("Current search path:\n");
1000 for (s=com_searchpaths ; s ; s=s->next)
1004 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1007 Con_Printf ("%s\n", s->filename);
1015 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1018 void COM_CreatePath (char *path)
1022 for (ofs = path+1 ; *ofs ; ofs++)
1024 if (*ofs == '/' || *ofs == '\\')
1026 // create the directory
1040 The filename will be prefixed by the current game directory
1043 qboolean COM_WriteFile (const char *filename, void *data, int len)
1046 char name[MAX_OSPATH];
1048 sprintf (name, "%s/%s", com_gamedir, filename);
1050 // LordHavoc: added this
1051 // create directories up to the file
1052 COM_CreatePath (name);
1054 handle = Sys_FileOpenWrite (name);
1057 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1061 Con_DPrintf ("COM_WriteFile: %s\n", name);
1062 Sys_FileWrite (handle, data, len);
1063 Sys_FileClose (handle);
1072 Copies a file over from the net to the local cache, creating any directories
1073 needed. This is for the convenience of developers using ISDN from home.
1076 void COM_CopyFile (char *netpath, char *cachepath)
1078 int in, out, remaining, count;
1081 remaining = Sys_FileOpenRead (netpath, &in);
1082 COM_CreatePath (cachepath); // create directories up to the cache file
1083 out = Sys_FileOpenWrite (cachepath);
1087 if (remaining < sizeof(buf))
1090 count = sizeof(buf);
1091 Sys_FileRead (in, buf, count);
1092 Sys_FileWrite (out, buf, count);
1097 Sys_FileClose (out);
1105 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1107 int fd = open (path, O_RDONLY);
1108 unsigned char id[2], len_bytes[4];
1112 Sys_Error ("Couldn't open %s", path);
1115 if (offs < 0 || len < 0)
1119 len = lseek (fd, 0, SEEK_END);
1120 lseek (fd, 0, SEEK_SET);
1122 lseek (fd, offs, SEEK_SET);
1126 if (id[0] == 0x1f && id[1] == 0x8b)
1128 lseek (fd, offs + len - 4, SEEK_SET);
1129 read (fd, len_bytes, 4);
1130 len = ((len_bytes[3] << 24)
1131 | (len_bytes[2] << 16)
1132 | (len_bytes[1] << 8)
1136 lseek (fd, offs, SEEK_SET);
1140 setmode (fd, O_BINARY);
1143 return Qdopen (fd, "rbz");
1145 return Qdopen (fd, "rb");
1152 Finds the file in the search path.
1153 Sets com_filesize and one of handle or file
1156 int COM_FindFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1158 searchpath_t *search;
1159 char netpath[MAX_OSPATH];
1161 char cachepath[MAX_OSPATH];
1165 int i, findtime, filenamelen;
1166 char gzfilename[MAX_OSPATH];
1168 filenamelen = strlen (filename);
1169 sprintf (gzfilename, "%s.gz", filename);
1172 Sys_Error ("COM_FindFile: file not set");
1175 // search through the path, one element at a time
1177 search = com_searchpaths;
1179 for ( ; search ; search = search->next)
1181 // is the element a pak file?
1184 // look through all the pak file elements
1186 for (i=0 ; i<pak->numfiles ; i++)
1187 if (!strcmp (pak->files[i].name, filename)
1188 || !strcmp (pak->files[i].name, gzfilename))
1191 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1192 // open a new file on the pakfile
1193 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1194 return com_filesize;
1199 sprintf (netpath, "%s/%s",search->filename, filename);
1201 findtime = Sys_FileTime (netpath);
1206 // see if the file needs to be updated in the cache
1207 if (com_cachedir[0])
1210 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1211 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1213 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1215 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1218 cachetime = Sys_FileTime (cachepath);
1220 if (cachetime < findtime)
1221 COM_CopyFile (netpath, cachepath);
1222 strcpy (netpath, cachepath);
1226 Sys_Printf ("FindFile: %s\n",netpath);
1227 *file = COM_OpenRead (netpath, -1, -1, zip);
1228 return com_filesize;
1233 Sys_Printf ("FindFile: can't find %s\n", filename);
1245 If the requested file is inside a packfile, a new QFile * will be opened
1249 int COM_FOpenFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1251 return COM_FindFile (filename, file, quiet, zip);
1259 Filename are reletive to the quake directory.
1260 Always appends a 0 byte.
1265 qbyte *COM_LoadFile (const char *path, qboolean quiet)
1272 buf = NULL; // quiet compiler warning
1275 // look for it in the filesystem or pack files
1276 len = COM_FOpenFile (path, &h, quiet, true);
1282 // extract the filename base name for hunk tag
1283 COM_FileBase (path, base);
1285 buf = Mem_Alloc(tempmempool, len+1);
1287 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1289 ((qbyte *)buf)[len] = 0;
1291 Qread (h, buf, len);
1301 Takes an explicit (not game tree related) path to a pak file.
1303 Loads the header and directory, adding the files at the beginning
1304 of the list so they override previous pack files.
1307 pack_t *COM_LoadPackFile (const char *packfile)
1309 dpackheader_t header;
1310 int i, numpackfiles, packhandle;
1312 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1315 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1318 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1319 if (memcmp(header.id, "PACK", 4))
1320 Sys_Error ("%s is not a packfile", packfile);
1321 header.dirofs = LittleLong (header.dirofs);
1322 header.dirlen = LittleLong (header.dirlen);
1324 if (header.dirlen % sizeof(dpackfile_t))
1325 Sys_Error ("%s has an invalid directory size", packfile);
1327 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1329 if (numpackfiles > MAX_FILES_IN_PACK)
1330 Sys_Error ("%s has %i files", packfile, numpackfiles);
1332 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1333 strcpy (pack->filename, packfile);
1334 pack->handle = packhandle;
1335 pack->numfiles = numpackfiles;
1336 pack->mempool = Mem_AllocPool(packfile);
1337 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1338 pack->next = packlist;
1341 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1342 Sys_FileSeek (packhandle, header.dirofs);
1343 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1345 // parse the directory
1346 for (i = 0;i < numpackfiles;i++)
1348 strcpy (pack->files[i].name, info[i].name);
1349 pack->files[i].filepos = LittleLong(info[i].filepos);
1350 pack->files[i].filelen = LittleLong(info[i].filelen);
1355 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1362 COM_AddGameDirectory
1364 Sets com_gamedir, adds the directory to the head of the path,
1365 then loads and adds pak1.pak pak2.pak ...
1368 void COM_AddGameDirectory (char *dir)
1370 stringlist_t *list, *current;
1371 searchpath_t *search;
1373 char pakfile[MAX_OSPATH];
1375 strcpy (com_gamedir, dir);
1378 // add the directory to the search path
1380 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1381 strcpy (search->filename, dir);
1382 search->next = com_searchpaths;
1383 com_searchpaths = search;
1385 // add any paks in the directory
1386 list = listdirectory(dir);
1387 for (current = list;current;current = current->next)
1389 if (matchpattern(current->text, "*.pak"))
1391 sprintf (pakfile, "%s/%s", dir, current->text);
1392 pak = COM_LoadPackFile (pakfile);
1395 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1397 search->next = com_searchpaths;
1398 com_searchpaths = search;
1401 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1404 freedirectory(list);
1412 void COM_InitFilesystem (void)
1415 searchpath_t *search;
1417 strcpy(com_basedir, ".");
1420 // Overrides the system supplied base directory (under GAMENAME)
1421 i = COM_CheckParm ("-basedir");
1422 if (i && i < com_argc-1)
1423 strcpy (com_basedir, com_argv[i+1]);
1425 i = strlen (com_basedir);
1426 if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1427 com_basedir[i-1] = 0;
1429 // start up with GAMENAME by default (id1)
1430 COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1433 com_modified = true;
1434 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1438 // Adds basedir/gamedir as an override game
1439 i = COM_CheckParm ("-game");
1440 if (i && i < com_argc-1)
1442 com_modified = true;
1443 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1446 // -path <dir or packfile> [<dir or packfile>] ...
1447 // Fully specifies the exact search path, overriding the generated one
1448 i = COM_CheckParm ("-path");
1451 com_modified = true;
1452 com_searchpaths = NULL;
1453 while (++i < com_argc)
1455 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1458 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1459 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1461 search->pack = COM_LoadPackFile (com_argv[i]);
1463 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1466 strcpy (search->filename, com_argv[i]);
1467 search->next = com_searchpaths;
1468 com_searchpaths = search;
1473 int COM_FileExists(const char *filename)
1475 searchpath_t *search;
1476 char netpath[MAX_OSPATH];
1480 for (search = com_searchpaths;search;search = search->next)
1485 for (i = 0;i < pak->numfiles;i++)
1486 if (!strcmp (pak->files[i].name, filename))
1491 sprintf (netpath, "%s/%s",search->filename, filename);
1492 findtime = Sys_FileTime (netpath);
1502 //======================================
1503 // LordHavoc: added these because they are useful
1505 void COM_ToLowerString(const char *in, char *out)
1509 if (*in >= 'A' && *in <= 'Z')
1510 *out++ = *in++ + 'a' - 'A';
1516 void COM_ToUpperString(const char *in, char *out)
1520 if (*in >= 'a' && *in <= 'z')
1521 *out++ = *in++ + 'A' - 'a';
1527 int COM_StringBeginsWith(const char *s, const char *match)
1529 for (;*s && *match;s++, match++)