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 == ':')
520 char *COM_FileExtension (char *in)
522 static char exten[8];
525 while (*in && *in != '.')
530 for (i=0 ; i<7 && *in ; i++,in++)
541 void COM_FileBase (char *in, char *out)
560 strcpy (out,"?model?");
575 void COM_DefaultExtension (char *path, char *extension)
579 // if path doesn't have a .EXT, append extension
580 // (extension should include the .)
582 src = path + strlen(path) - 1;
584 while (*src != '/' && src != path)
587 return; // it has an extension
591 strcat (path, extension);
599 Parse a token out of a string
602 char *COM_Parse (char *data)
615 while ( (c = *data) <= ' ')
618 return NULL; // end of file;
623 if (c=='/' && data[1] == '/')
625 while (*data && *data != '\n')
631 // handle quoted strings specially
648 // parse single characters
649 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
657 // parse a regular word
664 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
677 Returns the position (1 to argc-1) in the program's argument list
678 where the given parameter apears, or 0 if not present
681 int COM_CheckParm (char *parm)
685 for (i=1 ; i<com_argc ; i++)
688 continue; // NEXTSTEP sometimes clears appkit vars.
689 if (!strcmp (parm,com_argv[i]))
700 Looks for the pop.txt file and verifies it.
701 Sets the "registered" cvar.
702 Immediately exits out if an alternate game was attempted to be started without
706 void COM_CheckRegistered (void)
708 Cvar_Set ("cmdline", com_cmdline);
710 if (!Sys_FileTime("gfx/pop.lmp"))
713 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
715 Con_Printf ("Playing shareware version.\n");
719 Cvar_Set ("registered", "1");
720 Con_Printf ("Playing registered version.\n");
724 void COM_Path_f (void);
732 void COM_InitArgv (void)
735 // reconstitute the command line for the cmdline externally visible cvar
737 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
740 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
741 com_cmdline[n++] = com_argv[j][i++];
742 if (n < (CMDLINE_LENGTH - 1))
743 com_cmdline[n++] = ' ';
750 void COM_InitGameType (void)
753 COM_StripExtension(com_argv[0], name);
754 COM_ToLowerString(name, name);
756 if (strstr(name, "transfusion"))
757 gamemode = GAME_TRANSFUSION;
758 else if (strstr(name, "zymotic"))
759 gamemode = GAME_ZYMOTIC;
760 else if (strstr(name, "fiendarena"))
761 gamemode = GAME_FIENDARENA;
762 else if (strstr(name, "nehahra"))
763 gamemode = GAME_NEHAHRA;
764 else if (strstr(name, "hipnotic"))
765 gamemode = GAME_HIPNOTIC;
766 else if (strstr(name, "rogue"))
767 gamemode = GAME_ROGUE;
769 gamemode = GAME_NORMAL;
771 if (COM_CheckParm ("-transfusion"))
772 gamemode = GAME_TRANSFUSION;
773 else if (COM_CheckParm ("-zymotic"))
774 gamemode = GAME_ZYMOTIC;
775 else if (COM_CheckParm ("-fiendarena"))
776 gamemode = GAME_FIENDARENA;
777 else if (COM_CheckParm ("-nehahra"))
778 gamemode = GAME_NEHAHRA;
779 else if (COM_CheckParm ("-hipnotic"))
780 gamemode = GAME_HIPNOTIC;
781 else if (COM_CheckParm ("-rogue"))
782 gamemode = GAME_ROGUE;
783 else if (COM_CheckParm ("-quake"))
784 gamemode = GAME_NORMAL;
789 if (registered.integer)
790 gamename = "DarkPlaces-Quake";
792 gamename = "DarkPlaces-SharewareQuake";
796 gamename = "Darkplaces-Hipnotic";
797 gamedirname = "hipnotic";
800 gamename = "Darkplaces-Rogue";
801 gamedirname = "rogue";
804 gamename = "DarkPlaces-Nehahra";
805 gamedirname = "nehahra";
807 case GAME_FIENDARENA:
808 gamename = "FiendArena";
809 gamedirname = "fiendarena";
812 gamename = "Zymotic";
813 gamedirname = "zymotic";
815 case GAME_TRANSFUSION:
816 gamename = "Transfusion";
817 gamedirname = "transfusion";
820 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
826 extern void Mathlib_Init(void);
835 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
836 qbyte swaptest[2] = {1,0};
838 // set the byte swapping variables in a portable manner
839 if ( *(short *)swaptest == 1)
841 BigShort = ShortSwap;
842 LittleShort = ShortNoSwap;
844 LittleLong = LongNoSwap;
845 BigFloat = FloatSwap;
846 LittleFloat = FloatNoSwap;
850 BigShort = ShortNoSwap;
851 LittleShort = ShortSwap;
852 BigLong = LongNoSwap;
853 LittleLong = LongSwap;
854 BigFloat = FloatNoSwap;
855 LittleFloat = FloatSwap;
859 pak_mempool = Mem_AllocPool("paks");
861 Cvar_RegisterVariable (®istered);
862 Cvar_RegisterVariable (&cmdline);
863 Cmd_AddCommand ("path", COM_Path_f);
867 COM_InitFilesystem ();
868 COM_CheckRegistered ();
876 does a varargs printf into a temp buffer, so I don't need to have
877 varargs versions of all text functions.
878 FIXME: make this buffer size safe someday
881 char *va(char *format, ...)
884 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
885 static char string[8][1024], *s;
886 static int stringindex = 0;
888 s = string[stringindex];
889 stringindex = (stringindex + 1) & 7;
890 va_start (argptr, format);
891 vsprintf (s, format,argptr);
899 =============================================================================
903 =============================================================================
915 char name[MAX_QPATH];
916 int filepos, filelen;
919 typedef struct pack_s
921 char filename[MAX_OSPATH];
935 int filepos, filelen;
945 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
946 #define MAX_FILES_IN_PACK 65536
948 pack_t *packlist = NULL;
951 char com_cachedir[MAX_OSPATH];
953 char com_gamedir[MAX_OSPATH];
955 typedef struct searchpath_s
957 char filename[MAX_OSPATH];
958 pack_t *pack; // only one of filename / pack will be used
959 struct searchpath_s *next;
962 searchpath_t *com_searchpaths;
970 void COM_Path_f (void)
974 Con_Printf ("Current search path:\n");
975 for (s=com_searchpaths ; s ; s=s->next)
979 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
982 Con_Printf ("%s\n", s->filename);
990 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
993 void COM_CreatePath (char *path)
997 for (ofs = path+1 ; *ofs ; ofs++)
999 if (*ofs == '/' || *ofs == '\\')
1001 // create the directory
1015 The filename will be prefixed by the current game directory
1018 qboolean COM_WriteFile (char *filename, void *data, int len)
1021 char name[MAX_OSPATH];
1023 sprintf (name, "%s/%s", com_gamedir, filename);
1025 // LordHavoc: added this
1026 COM_CreatePath (name); // create directories up to the file
1028 handle = Sys_FileOpenWrite (name);
1031 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1035 Con_DPrintf ("COM_WriteFile: %s\n", name);
1036 Sys_FileWrite (handle, data, len);
1037 Sys_FileClose (handle);
1046 Copies a file over from the net to the local cache, creating any directories
1047 needed. This is for the convenience of developers using ISDN from home.
1050 void COM_CopyFile (char *netpath, char *cachepath)
1053 int remaining, count;
1056 remaining = Sys_FileOpenRead (netpath, &in);
1057 COM_CreatePath (cachepath); // create directories up to the cache file
1058 out = Sys_FileOpenWrite (cachepath);
1062 if (remaining < sizeof(buf))
1065 count = sizeof(buf);
1066 Sys_FileRead (in, buf, count);
1067 Sys_FileWrite (out, buf, count);
1072 Sys_FileClose (out);
1080 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1082 int fd = open (path, O_RDONLY);
1083 unsigned char id[2];
1084 unsigned char len_bytes[4];
1088 Sys_Error ("Couldn't open %s", path);
1091 if (offs < 0 || len < 0)
1095 len = lseek (fd, 0, SEEK_END);
1096 lseek (fd, 0, SEEK_SET);
1098 lseek (fd, offs, SEEK_SET);
1102 if (id[0] == 0x1f && id[1] == 0x8b)
1104 lseek (fd, offs + len - 4, SEEK_SET);
1105 read (fd, len_bytes, 4);
1106 len = ((len_bytes[3] << 24)
1107 | (len_bytes[2] << 16)
1108 | (len_bytes[1] << 8)
1112 lseek (fd, offs, SEEK_SET);
1116 setmode (fd, O_BINARY);
1119 return Qdopen (fd, "rbz");
1121 return Qdopen (fd, "rb");
1128 Finds the file in the search path.
1129 Sets com_filesize and one of handle or file
1132 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1134 searchpath_t *search;
1135 char netpath[MAX_OSPATH];
1137 char cachepath[MAX_OSPATH];
1143 char gzfilename[MAX_OSPATH];
1146 filenamelen = strlen (filename);
1147 sprintf (gzfilename, "%s.gz", filename);
1150 Sys_Error ("COM_FindFile: file not set");
1153 // search through the path, one element at a time
1155 search = com_searchpaths;
1157 for ( ; search ; search = search->next)
1159 // is the element a pak file?
1162 // look through all the pak file elements
1164 for (i=0 ; i<pak->numfiles ; i++)
1165 if (!strcmp (pak->files[i].name, filename)
1166 || !strcmp (pak->files[i].name, gzfilename))
1169 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1170 // open a new file on the pakfile
1171 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1172 return com_filesize;
1177 sprintf (netpath, "%s/%s",search->filename, filename);
1179 findtime = Sys_FileTime (netpath);
1184 // see if the file needs to be updated in the cache
1185 if (com_cachedir[0])
1188 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1189 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1191 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1193 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1196 cachetime = Sys_FileTime (cachepath);
1198 if (cachetime < findtime)
1199 COM_CopyFile (netpath, cachepath);
1200 strcpy (netpath, cachepath);
1205 Sys_Printf ("FindFile: %s\n",netpath);
1206 *file = COM_OpenRead (netpath, -1, -1, zip);
1207 return com_filesize;
1213 Sys_Printf ("FindFile: can't find %s\n", filename);
1225 If the requested file is inside a packfile, a new QFile * will be opened
1229 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1231 return COM_FindFile (filename, file, quiet, zip);
1239 Filename are reletive to the quake directory.
1240 Always appends a 0 byte.
1245 qbyte *COM_LoadFile (char *path, qboolean quiet)
1252 buf = NULL; // quiet compiler warning
1255 // look for it in the filesystem or pack files
1256 len = COM_FOpenFile (path, &h, quiet, true);
1262 // extract the filename base name for hunk tag
1263 COM_FileBase (path, base);
1265 buf = Mem_Alloc(tempmempool, len+1);
1267 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1269 ((qbyte *)buf)[len] = 0;
1271 Qread (h, buf, len);
1281 Takes an explicit (not game tree related) path to a pak file.
1283 Loads the header and directory, adding the files at the beginning
1284 of the list so they override previous pack files.
1287 pack_t *COM_LoadPackFile (char *packfile)
1289 dpackheader_t header;
1294 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1297 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1300 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1301 if (memcmp(header.id, "PACK", 4))
1302 Sys_Error ("%s is not a packfile", packfile);
1303 header.dirofs = LittleLong (header.dirofs);
1304 header.dirlen = LittleLong (header.dirlen);
1306 if (header.dirlen % sizeof(dpackfile_t))
1307 Sys_Error ("%s has an invalid directory size", packfile);
1309 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1311 if (numpackfiles > MAX_FILES_IN_PACK)
1312 Sys_Error ("%s has %i files", packfile, numpackfiles);
1314 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1315 strcpy (pack->filename, packfile);
1316 pack->handle = packhandle;
1317 pack->numfiles = numpackfiles;
1318 pack->mempool = Mem_AllocPool(packfile);
1319 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1320 pack->next = packlist;
1323 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1324 Sys_FileSeek (packhandle, header.dirofs);
1325 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1327 // parse the directory
1328 for (i = 0;i < numpackfiles;i++)
1330 strcpy (pack->files[i].name, info[i].name);
1331 pack->files[i].filepos = LittleLong(info[i].filepos);
1332 pack->files[i].filelen = LittleLong(info[i].filelen);
1337 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1344 COM_AddGameDirectory
1346 Sets com_gamedir, adds the directory to the head of the path,
1347 then loads and adds pak1.pak pak2.pak ...
1350 void COM_AddGameDirectory (char *dir)
1352 stringlist_t *list, *current;
1353 searchpath_t *search;
1355 char pakfile[MAX_OSPATH];
1357 strcpy (com_gamedir, dir);
1360 // add the directory to the search path
1362 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1363 strcpy (search->filename, dir);
1364 search->next = com_searchpaths;
1365 com_searchpaths = search;
1367 // add any paks in the directory
1368 list = listdirectory(dir);
1369 for (current = list;current;current = current->next)
1371 if (matchpattern(current->text, "*.pak"))
1373 sprintf (pakfile, "%s/%s", dir, current->text);
1374 pak = COM_LoadPackFile (pakfile);
1377 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1379 search->next = com_searchpaths;
1380 com_searchpaths = search;
1383 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1386 freedirectory(list);
1394 void COM_InitFilesystem (void)
1397 searchpath_t *search;
1399 strcpy(com_basedir, "");
1402 // Overrides the system supplied base directory (under GAMENAME)
1403 i = COM_CheckParm ("-basedir");
1404 if (i && i < com_argc-1)
1405 strcpy (com_basedir, com_argv[i+1]);
1407 i = strlen (com_basedir);
1408 if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1409 com_basedir[i-1] = 0;
1411 // start up with GAMENAME by default (id1)
1412 COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1415 com_modified = true;
1416 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1420 // Adds basedir/gamedir as an override game
1421 i = COM_CheckParm ("-game");
1422 if (i && i < com_argc-1)
1424 com_modified = true;
1425 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1428 // -path <dir or packfile> [<dir or packfile>] ...
1429 // Fully specifies the exact search path, overriding the generated one
1430 i = COM_CheckParm ("-path");
1433 com_modified = true;
1434 com_searchpaths = NULL;
1435 while (++i < com_argc)
1437 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1440 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1441 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1443 search->pack = COM_LoadPackFile (com_argv[i]);
1445 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1448 strcpy (search->filename, com_argv[i]);
1449 search->next = com_searchpaths;
1450 com_searchpaths = search;
1455 int COM_FileExists(char *filename)
1457 searchpath_t *search;
1458 char netpath[MAX_OSPATH];
1463 for (search = com_searchpaths;search;search = search->next)
1468 for (i = 0;i < pak->numfiles;i++)
1469 if (!strcmp (pak->files[i].name, filename))
1474 sprintf (netpath, "%s/%s",search->filename, filename);
1475 findtime = Sys_FileTime (netpath);
1485 //======================================
1486 // LordHavoc: added these because they are useful
1488 void COM_ToLowerString(char *in, char *out)
1492 if (*in >= 'A' && *in <= 'Z')
1493 *out++ = *in++ + 'a' - 'A';
1499 void COM_ToUpperString(char *in, char *out)
1503 if (*in >= 'a' && *in <= 'z')
1504 *out++ = *in++ + 'A' - 'a';
1510 int COM_StringBeginsWith(const char *s, const char *match)
1512 for (;*s && *match;s++, match++)