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];
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 (char *s1, 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 (char *s1, 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, 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, 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");
439 if (length > buf->maxsize)
440 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
442 buf->overflowed = true;
443 Con_Printf ("SZ_GetSpace: overflow");
447 data = buf->data + buf->cursize;
448 buf->cursize += length;
453 void SZ_Write (sizebuf_t *buf, void *data, int length)
455 memcpy (SZ_GetSpace(buf,length),data,length);
458 void SZ_Print (sizebuf_t *buf, char *data)
462 len = strlen(data)+1;
464 // byte * cast to keep VC++ happy
465 if (buf->data[buf->cursize-1])
466 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
468 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
472 //============================================================================
480 char *COM_SkipPath (char *pathname)
499 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
500 void COM_StripExtension (char *in, char *out)
507 else if (*in == '/' || *in == '\\' || *in == ':')
522 char *COM_FileExtension (char *in)
524 static char exten[8];
527 while (*in && *in != '.')
532 for (i=0 ; i<7 && *in ; i++,in++)
543 void COM_FileBase (char *in, char *out)
562 strcpy (out,"?model?");
577 void COM_DefaultExtension (char *path, char *extension)
581 // if path doesn't have a .EXT, append extension
582 // (extension should include the .)
584 src = path + strlen(path) - 1;
586 while (*src != '/' && src != path)
589 return; // it has an extension
593 strcat (path, extension);
601 Parse a token out of a string
604 char *COM_Parse (char *data)
617 while ( (c = *data) <= ' ')
620 return NULL; // end of file;
625 if (c=='/' && data[1] == '/')
627 while (*data && *data != '\n')
633 // handle quoted strings specially
650 // parse single characters
651 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
659 // parse a regular word
666 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
679 Returns the position (1 to argc-1) in the program's argument list
680 where the given parameter apears, or 0 if not present
683 int COM_CheckParm (char *parm)
687 for (i=1 ; i<com_argc ; i++)
690 continue; // NEXTSTEP sometimes clears appkit vars.
691 if (!strcmp (parm,com_argv[i]))
702 Looks for the pop.txt file and verifies it.
703 Sets the "registered" cvar.
704 Immediately exits out if an alternate game was attempted to be started without
708 void COM_CheckRegistered (void)
710 Cvar_Set ("cmdline", com_cmdline);
712 if (!Sys_FileTime("gfx/pop.lmp"))
715 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
717 Con_Printf ("Playing shareware version.\n");
721 Cvar_Set ("registered", "1");
722 Con_Printf ("Playing registered version.\n");
726 void COM_Path_f (void);
734 void COM_InitArgv (void)
737 // reconstitute the command line for the cmdline externally visible cvar
739 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
742 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
743 com_cmdline[n++] = com_argv[j][i++];
744 if (n < (CMDLINE_LENGTH - 1))
745 com_cmdline[n++] = ' ';
752 void COM_InitGameType (void)
755 COM_StripExtension(com_argv[0], name);
756 COM_ToLowerString(name, name);
758 if (strstr(name, "transfusion"))
759 gamemode = GAME_TRANSFUSION;
760 else if (strstr(name, "zymotic"))
761 gamemode = GAME_ZYMOTIC;
762 else if (strstr(name, "fiendarena"))
763 gamemode = GAME_FIENDARENA;
764 else if (strstr(name, "nehahra"))
765 gamemode = GAME_NEHAHRA;
766 else if (strstr(name, "hipnotic"))
767 gamemode = GAME_HIPNOTIC;
768 else if (strstr(name, "rogue"))
769 gamemode = GAME_ROGUE;
771 gamemode = GAME_NORMAL;
773 if (COM_CheckParm ("-transfusion"))
774 gamemode = GAME_TRANSFUSION;
775 else if (COM_CheckParm ("-zymotic"))
776 gamemode = GAME_ZYMOTIC;
777 else if (COM_CheckParm ("-fiendarena"))
778 gamemode = GAME_FIENDARENA;
779 else if (COM_CheckParm ("-nehahra"))
780 gamemode = GAME_NEHAHRA;
781 else if (COM_CheckParm ("-hipnotic"))
782 gamemode = GAME_HIPNOTIC;
783 else if (COM_CheckParm ("-rogue"))
784 gamemode = GAME_ROGUE;
785 else if (COM_CheckParm ("-quake"))
786 gamemode = GAME_NORMAL;
791 if (registered.integer)
792 gamename = "DarkPlaces-Quake";
794 gamename = "DarkPlaces-SharewareQuake";
798 gamename = "Darkplaces-Hipnotic";
799 gamedirname = "hipnotic";
802 gamename = "Darkplaces-Rogue";
803 gamedirname = "rogue";
806 gamename = "DarkPlaces-Nehahra";
807 gamedirname = "nehahra";
809 case GAME_FIENDARENA:
810 gamename = "FiendArena";
811 gamedirname = "fiendarena";
814 gamename = "Zymotic";
815 gamedirname = "zymotic";
817 case GAME_TRANSFUSION:
818 gamename = "Transfusion";
819 gamedirname = "transfusion";
822 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
828 extern void Mathlib_Init(void);
837 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
838 qbyte swaptest[2] = {1,0};
840 // set the byte swapping variables in a portable manner
841 if ( *(short *)swaptest == 1)
843 BigShort = ShortSwap;
844 LittleShort = ShortNoSwap;
846 LittleLong = LongNoSwap;
847 BigFloat = FloatSwap;
848 LittleFloat = FloatNoSwap;
852 BigShort = ShortNoSwap;
853 LittleShort = ShortSwap;
854 BigLong = LongNoSwap;
855 LittleLong = LongSwap;
856 BigFloat = FloatNoSwap;
857 LittleFloat = FloatSwap;
861 pak_mempool = Mem_AllocPool("paks");
863 Cvar_RegisterVariable (®istered);
864 Cvar_RegisterVariable (&cmdline);
865 Cmd_AddCommand ("path", COM_Path_f);
869 COM_InitFilesystem ();
870 COM_CheckRegistered ();
878 does a varargs printf into a temp buffer, so I don't need to have
879 varargs versions of all text functions.
880 FIXME: make this buffer size safe someday
883 char *va(char *format, ...)
886 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
887 static char string[8][1024], *s;
888 static int stringindex = 0;
890 s = string[stringindex];
891 stringindex = (stringindex + 1) & 7;
892 va_start (argptr, format);
893 vsprintf (s, format,argptr);
901 =============================================================================
905 =============================================================================
917 char name[MAX_QPATH];
918 int filepos, filelen;
921 typedef struct pack_s
923 char filename[MAX_OSPATH];
937 int filepos, filelen;
947 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
948 #define MAX_FILES_IN_PACK 65536
950 pack_t *packlist = NULL;
953 char com_cachedir[MAX_OSPATH];
955 char com_gamedir[MAX_OSPATH];
957 typedef struct searchpath_s
959 char filename[MAX_OSPATH];
960 pack_t *pack; // only one of filename / pack will be used
961 struct searchpath_s *next;
964 searchpath_t *com_searchpaths;
972 void COM_Path_f (void)
976 Con_Printf ("Current search path:\n");
977 for (s=com_searchpaths ; s ; s=s->next)
981 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
984 Con_Printf ("%s\n", s->filename);
992 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
995 void COM_CreatePath (char *path)
999 for (ofs = path+1 ; *ofs ; ofs++)
1001 if (*ofs == '/' || *ofs == '\\')
1003 // create the directory
1017 The filename will be prefixed by the current game directory
1020 qboolean COM_WriteFile (char *filename, void *data, int len)
1023 char name[MAX_OSPATH];
1025 sprintf (name, "%s/%s", com_gamedir, filename);
1027 // LordHavoc: added this
1028 COM_CreatePath (name); // create directories up to the file
1030 handle = Sys_FileOpenWrite (name);
1033 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1037 Con_DPrintf ("COM_WriteFile: %s\n", name);
1038 Sys_FileWrite (handle, data, len);
1039 Sys_FileClose (handle);
1048 Copies a file over from the net to the local cache, creating any directories
1049 needed. This is for the convenience of developers using ISDN from home.
1052 void COM_CopyFile (char *netpath, char *cachepath)
1055 int remaining, count;
1058 remaining = Sys_FileOpenRead (netpath, &in);
1059 COM_CreatePath (cachepath); // create directories up to the cache file
1060 out = Sys_FileOpenWrite (cachepath);
1064 if (remaining < sizeof(buf))
1067 count = sizeof(buf);
1068 Sys_FileRead (in, buf, count);
1069 Sys_FileWrite (out, buf, count);
1074 Sys_FileClose (out);
1082 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1084 int fd = open (path, O_RDONLY);
1085 unsigned char id[2];
1086 unsigned char len_bytes[4];
1090 Sys_Error ("Couldn't open %s", path);
1093 if (offs < 0 || len < 0)
1097 len = lseek (fd, 0, SEEK_END);
1098 lseek (fd, 0, SEEK_SET);
1100 lseek (fd, offs, SEEK_SET);
1104 if (id[0] == 0x1f && id[1] == 0x8b)
1106 lseek (fd, offs + len - 4, SEEK_SET);
1107 read (fd, len_bytes, 4);
1108 len = ((len_bytes[3] << 24)
1109 | (len_bytes[2] << 16)
1110 | (len_bytes[1] << 8)
1114 lseek (fd, offs, SEEK_SET);
1118 setmode (fd, O_BINARY);
1121 return Qdopen (fd, "rbz");
1123 return Qdopen (fd, "rb");
1130 Finds the file in the search path.
1131 Sets com_filesize and one of handle or file
1134 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1136 searchpath_t *search;
1137 char netpath[MAX_OSPATH];
1139 char cachepath[MAX_OSPATH];
1145 char gzfilename[MAX_OSPATH];
1148 filenamelen = strlen (filename);
1149 sprintf (gzfilename, "%s.gz", filename);
1152 Sys_Error ("COM_FindFile: file not set");
1155 // search through the path, one element at a time
1157 search = com_searchpaths;
1159 for ( ; search ; search = search->next)
1161 // is the element a pak file?
1164 // look through all the pak file elements
1166 for (i=0 ; i<pak->numfiles ; i++)
1167 if (!strcmp (pak->files[i].name, filename)
1168 || !strcmp (pak->files[i].name, gzfilename))
1171 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1172 // open a new file on the pakfile
1173 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1174 return com_filesize;
1179 sprintf (netpath, "%s/%s",search->filename, filename);
1181 findtime = Sys_FileTime (netpath);
1186 // see if the file needs to be updated in the cache
1187 if (com_cachedir[0])
1190 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1191 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1193 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1195 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1198 cachetime = Sys_FileTime (cachepath);
1200 if (cachetime < findtime)
1201 COM_CopyFile (netpath, cachepath);
1202 strcpy (netpath, cachepath);
1207 Sys_Printf ("FindFile: %s\n",netpath);
1208 *file = COM_OpenRead (netpath, -1, -1, zip);
1209 return com_filesize;
1215 Sys_Printf ("FindFile: can't find %s\n", filename);
1227 If the requested file is inside a packfile, a new QFile * will be opened
1231 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1233 return COM_FindFile (filename, file, quiet, zip);
1241 Filename are reletive to the quake directory.
1242 Always appends a 0 byte.
1247 qbyte *COM_LoadFile (char *path, qboolean quiet)
1254 buf = NULL; // quiet compiler warning
1257 // look for it in the filesystem or pack files
1258 len = COM_FOpenFile (path, &h, quiet, true);
1264 // extract the filename base name for hunk tag
1265 COM_FileBase (path, base);
1267 buf = Mem_Alloc(tempmempool, len+1);
1269 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1271 ((qbyte *)buf)[len] = 0;
1273 Qread (h, buf, len);
1283 Takes an explicit (not game tree related) path to a pak file.
1285 Loads the header and directory, adding the files at the beginning
1286 of the list so they override previous pack files.
1289 pack_t *COM_LoadPackFile (char *packfile)
1291 dpackheader_t header;
1296 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1299 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1302 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1303 if (memcmp(header.id, "PACK", 4))
1304 Sys_Error ("%s is not a packfile", packfile);
1305 header.dirofs = LittleLong (header.dirofs);
1306 header.dirlen = LittleLong (header.dirlen);
1308 if (header.dirlen % sizeof(dpackfile_t))
1309 Sys_Error ("%s has an invalid directory size", packfile);
1311 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1313 if (numpackfiles > MAX_FILES_IN_PACK)
1314 Sys_Error ("%s has %i files", packfile, numpackfiles);
1316 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1317 strcpy (pack->filename, packfile);
1318 pack->handle = packhandle;
1319 pack->numfiles = numpackfiles;
1320 pack->mempool = Mem_AllocPool(packfile);
1321 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1322 pack->next = packlist;
1325 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1326 Sys_FileSeek (packhandle, header.dirofs);
1327 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1329 // parse the directory
1330 for (i = 0;i < numpackfiles;i++)
1332 strcpy (pack->files[i].name, info[i].name);
1333 pack->files[i].filepos = LittleLong(info[i].filepos);
1334 pack->files[i].filelen = LittleLong(info[i].filelen);
1339 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1346 COM_AddGameDirectory
1348 Sets com_gamedir, adds the directory to the head of the path,
1349 then loads and adds pak1.pak pak2.pak ...
1352 void COM_AddGameDirectory (char *dir)
1354 stringlist_t *list, *current;
1355 searchpath_t *search;
1357 char pakfile[MAX_OSPATH];
1359 strcpy (com_gamedir, dir);
1362 // add the directory to the search path
1364 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1365 strcpy (search->filename, dir);
1366 search->next = com_searchpaths;
1367 com_searchpaths = search;
1369 // add any paks in the directory
1370 list = listdirectory(dir);
1371 for (current = list;current;current = current->next)
1373 if (matchpattern(current->text, "*.pak"))
1375 sprintf (pakfile, "%s/%s", dir, current->text);
1376 pak = COM_LoadPackFile (pakfile);
1379 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1381 search->next = com_searchpaths;
1382 com_searchpaths = search;
1385 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1388 freedirectory(list);
1396 void COM_InitFilesystem (void)
1399 searchpath_t *search;
1401 strcpy(com_basedir, ".");
1404 // Overrides the system supplied base directory (under GAMENAME)
1405 i = COM_CheckParm ("-basedir");
1406 if (i && i < com_argc-1)
1407 strcpy (com_basedir, com_argv[i+1]);
1409 i = strlen (com_basedir);
1410 if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1411 com_basedir[i-1] = 0;
1413 // start up with GAMENAME by default (id1)
1414 COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1417 com_modified = true;
1418 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1422 // Adds basedir/gamedir as an override game
1423 i = COM_CheckParm ("-game");
1424 if (i && i < com_argc-1)
1426 com_modified = true;
1427 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1430 // -path <dir or packfile> [<dir or packfile>] ...
1431 // Fully specifies the exact search path, overriding the generated one
1432 i = COM_CheckParm ("-path");
1435 com_modified = true;
1436 com_searchpaths = NULL;
1437 while (++i < com_argc)
1439 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1442 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1443 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1445 search->pack = COM_LoadPackFile (com_argv[i]);
1447 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1450 strcpy (search->filename, com_argv[i]);
1451 search->next = com_searchpaths;
1452 com_searchpaths = search;
1457 int COM_FileExists(char *filename)
1459 searchpath_t *search;
1460 char netpath[MAX_OSPATH];
1465 for (search = com_searchpaths;search;search = search->next)
1470 for (i = 0;i < pak->numfiles;i++)
1471 if (!strcmp (pak->files[i].name, filename))
1476 sprintf (netpath, "%s/%s",search->filename, filename);
1477 findtime = Sys_FileTime (netpath);
1487 //======================================
1488 // LordHavoc: added these because they are useful
1490 void COM_ToLowerString(char *in, char *out)
1494 if (*in >= 'A' && *in <= 'Z')
1495 *out++ = *in++ + 'a' - 'A';
1501 void COM_ToUpperString(char *in, char *out)
1505 if (*in >= 'a' && *in <= 'z')
1506 *out++ = *in++ + 'A' - 'a';
1512 int COM_StringBeginsWith(const char *s, const char *match)
1514 for (;*s && *match;s++, match++)