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];
57 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.
59 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
60 only used during filesystem initialization.
62 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.
64 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
65 specified, when a file is found by the normal search path, it will be mirrored
66 into the cache directory, then opened there.
71 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.
75 //============================================================================
79 ============================================================================
81 LIBRARY REPLACEMENT FUNCTIONS
83 ============================================================================
86 int Q_strncasecmp (const char *s1, const char *s2, int n)
96 return 0; // strings are equal until end point
100 if (c1 >= 'a' && c1 <= 'z')
102 if (c2 >= 'a' && c2 <= 'z')
105 return -1; // strings not equal
108 return 0; // strings are equal
114 int Q_strcasecmp (const char *s1, const char *s2)
116 return Q_strncasecmp (s1, s2, 99999);
120 ============================================================================
124 ============================================================================
127 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
128 short (*BigShort) (short l);
129 short (*LittleShort) (short l);
130 int (*BigLong) (int l);
131 int (*LittleLong) (int l);
132 float (*BigFloat) (float l);
133 float (*LittleFloat) (float l);
136 short ShortSwap (short l)
146 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
147 short ShortNoSwap (short l)
162 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
165 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
166 int LongNoSwap (int l)
172 float FloatSwap (float f)
182 dat2.b[0] = dat1.b[3];
183 dat2.b[1] = dat1.b[2];
184 dat2.b[2] = dat1.b[1];
185 dat2.b[3] = dat1.b[0];
189 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
190 float FloatNoSwap (float f)
197 ==============================================================================
201 Handles byte ordering and avoids alignment errors
202 ==============================================================================
209 void MSG_WriteChar (sizebuf_t *sb, int c)
213 buf = SZ_GetSpace (sb, 1);
217 void MSG_WriteByte (sizebuf_t *sb, int c)
221 buf = SZ_GetSpace (sb, 1);
225 void MSG_WriteShort (sizebuf_t *sb, int c)
229 buf = SZ_GetSpace (sb, 2);
234 void MSG_WriteLong (sizebuf_t *sb, int c)
238 buf = SZ_GetSpace (sb, 4);
240 buf[1] = (c>>8)&0xff;
241 buf[2] = (c>>16)&0xff;
245 void MSG_WriteFloat (sizebuf_t *sb, float f)
255 dat.l = LittleLong (dat.l);
257 SZ_Write (sb, &dat.l, 4);
260 void MSG_WriteString (sizebuf_t *sb, const char *s)
263 SZ_Write (sb, "", 1);
265 SZ_Write (sb, s, strlen(s)+1);
268 // used by server (always latest dpprotocol)
269 void MSG_WriteDPCoord (sizebuf_t *sb, float f)
272 MSG_WriteShort (sb, (int)(f + 0.5f));
274 MSG_WriteShort (sb, (int)(f - 0.5f));
277 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
280 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
282 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
285 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
286 void MSG_WriteAngle (sizebuf_t *sb, float f)
289 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
291 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
298 qboolean msg_badread;
300 void MSG_BeginReading (void)
306 int MSG_ReadShort (void)
310 if (msg_readcount+2 > net_message.cursize)
316 c = (short)(net_message.data[msg_readcount]
317 + (net_message.data[msg_readcount+1]<<8));
324 int MSG_ReadLong (void)
328 if (msg_readcount+4 > net_message.cursize)
334 c = net_message.data[msg_readcount]
335 + (net_message.data[msg_readcount+1]<<8)
336 + (net_message.data[msg_readcount+2]<<16)
337 + (net_message.data[msg_readcount+3]<<24);
344 float MSG_ReadFloat (void)
353 dat.b[0] = net_message.data[msg_readcount];
354 dat.b[1] = net_message.data[msg_readcount+1];
355 dat.b[2] = net_message.data[msg_readcount+2];
356 dat.b[3] = net_message.data[msg_readcount+3];
359 dat.l = LittleLong (dat.l);
364 char *MSG_ReadString (void)
366 static char string[2048];
373 if (c == -1 || c == 0)
377 } while (l < sizeof(string)-1);
384 // used by server (always latest dpprotocol)
385 float MSG_ReadDPCoord (void)
387 return (signed short) MSG_ReadShort();
391 float MSG_ReadCoord (void)
393 if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
394 return (signed short) MSG_ReadShort();
395 else if (dpprotocol == DPPROTOCOL_VERSION1)
396 return MSG_ReadFloat();
398 return MSG_ReadShort() * (1.0f/8.0f);
402 //===========================================================================
404 void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name)
408 buf->mempool = Mem_AllocPool(name);
409 buf->data = Mem_Alloc(buf->mempool, startsize);
410 buf->maxsize = startsize;
415 void SZ_Free (sizebuf_t *buf)
417 Mem_FreePool(&buf->mempool);
423 void SZ_Clear (sizebuf_t *buf)
428 void *SZ_GetSpace (sizebuf_t *buf, int length)
432 if (buf->cursize + length > buf->maxsize)
434 if (!buf->allowoverflow)
435 Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
437 if (length > buf->maxsize)
438 Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
440 buf->overflowed = true;
441 Con_Printf ("SZ_GetSpace: overflow\n");
445 data = buf->data + buf->cursize;
446 buf->cursize += length;
451 void SZ_Write (sizebuf_t *buf, const void *data, int length)
453 memcpy (SZ_GetSpace(buf,length),data,length);
456 void SZ_Print (sizebuf_t *buf, const char *data)
459 len = strlen(data)+1;
461 // byte * cast to keep VC++ happy
462 if (buf->data[buf->cursize-1])
463 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
465 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
468 static char *hexchar = "0123456789ABCDEF";
469 void SZ_HexDumpToConsole(const sizebuf_t *buf)
473 char *cur, *flushpointer;
475 flushpointer = text + 512;
476 for (i = 0;i < buf->cursize;i++)
480 *cur++ = hexchar[(i >> 12) & 15];
481 *cur++ = hexchar[(i >> 8) & 15];
482 *cur++ = hexchar[(i >> 4) & 15];
483 *cur++ = hexchar[(i >> 0) & 15];
487 else if ((i & 15) == 15)
493 *cur++ = hexchar[(buf->data[i] >> 4) & 15] | 0x80;
494 *cur++ = hexchar[(buf->data[i] >> 0) & 15] | 0x80;
498 *cur++ = hexchar[(buf->data[i] >> 4) & 15];
499 *cur++ = hexchar[(buf->data[i] >> 0) & 15];
501 if (cur >= flushpointer)
504 Con_Printf("%s", text);
513 Con_Printf("%s", text);
518 //============================================================================
526 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
527 void COM_StripExtension (const char *in, char *out)
534 else if (*in == '/' || *in == '\\' || *in == ':')
549 char *COM_FileExtension (const char *in)
551 static char exten[8];
554 while (*in && *in != '.')
559 for (i=0 ; i<7 && *in ; i++,in++)
570 void COM_FileBase (const char *in, char *out)
572 const char *slash, *dot, *s;
588 strcpy (out,"?model?");
603 void COM_DefaultExtension (char *path, const char *extension)
607 // if path doesn't have a .EXT, append extension
608 // (extension should include the .)
610 src = path + strlen(path) - 1;
612 while (*src != '/' && src != path)
615 return; // it has an extension
619 strcat (path, extension);
627 Parse a token out of a string
630 int COM_ParseToken (const char **datapointer)
634 const char *data = *datapointer;
647 while ((c = *data) <= ' ')
659 if (c=='/' && data[1] == '/')
661 while (*data && *data != '\n')
667 // handle quoted strings specially
685 // parse single characters
686 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
691 *datapointer = data+1;
695 // parse a regular word
702 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
716 Returns the position (1 to argc-1) in the program's argument list
717 where the given parameter apears, or 0 if not present
720 int COM_CheckParm (const char *parm)
724 for (i=1 ; i<com_argc ; i++)
727 continue; // NEXTSTEP sometimes clears appkit vars.
728 if (!strcmp (parm,com_argv[i]))
739 Looks for the pop.txt file and verifies it.
740 Sets the "registered" cvar.
741 Immediately exits out if an alternate game was attempted to be started without
745 void COM_CheckRegistered (void)
747 Cvar_Set ("cmdline", com_cmdline);
749 if (!Sys_FileTime("gfx/pop.lmp"))
752 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
754 Con_Printf ("Playing shareware version.\n");
758 Cvar_Set ("registered", "1");
759 Con_Printf ("Playing registered version.\n");
763 void COM_Path_f (void);
771 void COM_InitArgv (void)
774 // reconstitute the command line for the cmdline externally visible cvar
776 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
779 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
780 com_cmdline[n++] = com_argv[j][i++];
781 if (n < (CMDLINE_LENGTH - 1))
782 com_cmdline[n++] = ' ';
789 void COM_InitGameType (void)
791 char name[MAX_OSPATH];
792 COM_StripExtension(com_argv[0], name);
793 COM_ToLowerString(name, name);
795 if (strstr(name, "transfusion"))
796 gamemode = GAME_TRANSFUSION;
797 else if (strstr(name, "nehahra"))
798 gamemode = GAME_NEHAHRA;
799 else if (strstr(name, "hipnotic"))
800 gamemode = GAME_HIPNOTIC;
801 else if (strstr(name, "rogue"))
802 gamemode = GAME_ROGUE;
804 gamemode = GAME_NORMAL;
806 if (COM_CheckParm ("-transfusion"))
807 gamemode = GAME_TRANSFUSION;
808 else if (COM_CheckParm ("-nehahra"))
809 gamemode = GAME_NEHAHRA;
810 else if (COM_CheckParm ("-hipnotic"))
811 gamemode = GAME_HIPNOTIC;
812 else if (COM_CheckParm ("-rogue"))
813 gamemode = GAME_ROGUE;
814 else if (COM_CheckParm ("-quake"))
815 gamemode = GAME_NORMAL;
820 gamename = "DarkPlaces-Quake";
824 gamename = "Darkplaces-Hipnotic";
825 gamedirname = "hipnotic";
828 gamename = "Darkplaces-Rogue";
829 gamedirname = "rogue";
832 gamename = "DarkPlaces-Nehahra";
833 gamedirname = "nehahra";
835 case GAME_TRANSFUSION:
836 gamename = "Transfusion";
837 gamedirname = "transfusion";
840 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
846 extern void Mathlib_Init(void);
855 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
856 qbyte swaptest[2] = {1,0};
858 // set the byte swapping variables in a portable manner
859 if ( *(short *)swaptest == 1)
861 BigShort = ShortSwap;
862 LittleShort = ShortNoSwap;
864 LittleLong = LongNoSwap;
865 BigFloat = FloatSwap;
866 LittleFloat = FloatNoSwap;
870 BigShort = ShortNoSwap;
871 LittleShort = ShortSwap;
872 BigLong = LongNoSwap;
873 LittleLong = LongSwap;
874 BigFloat = FloatNoSwap;
875 LittleFloat = FloatSwap;
879 pak_mempool = Mem_AllocPool("paks");
881 Cvar_RegisterVariable (®istered);
882 Cvar_RegisterVariable (&cmdline);
883 Cmd_AddCommand ("path", COM_Path_f);
887 COM_InitFilesystem ();
888 COM_CheckRegistered ();
898 does a varargs printf into a temp buffer, so I don't need to have
899 varargs versions of all text functions.
900 FIXME: make this buffer size safe someday
903 char *va(const char *format, ...)
906 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
907 static char string[8][1024], *s;
908 static int stringindex = 0;
910 s = string[stringindex];
911 stringindex = (stringindex + 1) & 7;
912 va_start (argptr, format);
913 vsprintf (s, format,argptr);
921 =============================================================================
925 =============================================================================
937 char name[MAX_QPATH];
938 int filepos, filelen;
941 typedef struct pack_s
943 char filename[MAX_OSPATH];
957 int filepos, filelen;
967 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
968 #define MAX_FILES_IN_PACK 65536
970 pack_t *packlist = NULL;
973 char com_cachedir[MAX_OSPATH];
975 char com_gamedir[MAX_OSPATH];
977 typedef struct searchpath_s
979 // only one of filename / pack will be used
980 char filename[MAX_OSPATH];
982 struct searchpath_s *next;
985 searchpath_t *com_searchpaths;
993 void COM_Path_f (void)
997 Con_Printf ("Current search path:\n");
998 for (s=com_searchpaths ; s ; s=s->next)
1002 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1005 Con_Printf ("%s\n", s->filename);
1013 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1016 void COM_CreatePath (char *path)
1020 for (ofs = path+1 ; *ofs ; ofs++)
1022 if (*ofs == '/' || *ofs == '\\')
1024 // create the directory
1038 The filename will be prefixed by the current game directory
1041 qboolean COM_WriteFile (const char *filename, void *data, int len)
1044 char name[MAX_OSPATH];
1046 sprintf (name, "%s/%s", com_gamedir, filename);
1048 // LordHavoc: added this
1049 // create directories up to the file
1050 COM_CreatePath (name);
1052 handle = Sys_FileOpenWrite (name);
1055 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1059 Con_DPrintf ("COM_WriteFile: %s\n", name);
1060 Sys_FileWrite (handle, data, len);
1061 Sys_FileClose (handle);
1070 Copies a file over from the net to the local cache, creating any directories
1071 needed. This is for the convenience of developers using ISDN from home.
1074 void COM_CopyFile (char *netpath, char *cachepath)
1076 int in, out, remaining, count;
1079 remaining = Sys_FileOpenRead (netpath, &in);
1080 COM_CreatePath (cachepath); // create directories up to the cache file
1081 out = Sys_FileOpenWrite (cachepath);
1085 if (remaining < sizeof(buf))
1088 count = sizeof(buf);
1089 Sys_FileRead (in, buf, count);
1090 Sys_FileWrite (out, buf, count);
1095 Sys_FileClose (out);
1103 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1105 int fd = open (path, O_RDONLY);
1106 unsigned char id[2], len_bytes[4];
1110 Sys_Error ("Couldn't open %s", path);
1113 if (offs < 0 || len < 0)
1117 len = lseek (fd, 0, SEEK_END);
1118 lseek (fd, 0, SEEK_SET);
1120 lseek (fd, offs, SEEK_SET);
1124 if (id[0] == 0x1f && id[1] == 0x8b)
1126 lseek (fd, offs + len - 4, SEEK_SET);
1127 read (fd, len_bytes, 4);
1128 len = ((len_bytes[3] << 24)
1129 | (len_bytes[2] << 16)
1130 | (len_bytes[1] << 8)
1134 lseek (fd, offs, SEEK_SET);
1138 setmode (fd, O_BINARY);
1141 return Qdopen (fd, "rbz");
1143 return Qdopen (fd, "rb");
1150 Finds the file in the search path.
1151 Sets com_filesize and one of handle or file
1154 int COM_FindFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1156 searchpath_t *search;
1157 char netpath[MAX_OSPATH];
1159 char cachepath[MAX_OSPATH];
1163 int i, findtime, filenamelen;
1164 char gzfilename[MAX_OSPATH];
1166 filenamelen = strlen (filename);
1167 sprintf (gzfilename, "%s.gz", filename);
1170 Sys_Error ("COM_FindFile: file not set");
1173 // search through the path, one element at a time
1175 search = com_searchpaths;
1177 for ( ; search ; search = search->next)
1179 // is the element a pak file?
1182 // look through all the pak file elements
1184 for (i=0 ; i<pak->numfiles ; i++)
1185 if (!strcmp (pak->files[i].name, filename)
1186 || !strcmp (pak->files[i].name, gzfilename))
1189 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1190 // open a new file on the pakfile
1191 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1192 return com_filesize;
1197 sprintf (netpath, "%s/%s",search->filename, filename);
1199 findtime = Sys_FileTime (netpath);
1204 // see if the file needs to be updated in the cache
1205 if (com_cachedir[0])
1208 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1209 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1211 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1213 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1216 cachetime = Sys_FileTime (cachepath);
1218 if (cachetime < findtime)
1219 COM_CopyFile (netpath, cachepath);
1220 strcpy (netpath, cachepath);
1224 Sys_Printf ("FindFile: %s\n",netpath);
1225 *file = COM_OpenRead (netpath, -1, -1, zip);
1226 return com_filesize;
1231 Sys_Printf ("FindFile: can't find %s\n", filename);
1243 If the requested file is inside a packfile, a new QFile * will be opened
1247 int COM_FOpenFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1249 return COM_FindFile (filename, file, quiet, zip);
1257 Filename are reletive to the quake directory.
1258 Always appends a 0 byte.
1263 qbyte *COM_LoadFile (const char *path, qboolean quiet)
1270 buf = NULL; // quiet compiler warning
1273 // look for it in the filesystem or pack files
1274 len = COM_FOpenFile (path, &h, quiet, true);
1280 // extract the filename base name for hunk tag
1281 COM_FileBase (path, base);
1283 buf = Mem_Alloc(tempmempool, len+1);
1285 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1287 ((qbyte *)buf)[len] = 0;
1289 Qread (h, buf, len);
1299 Takes an explicit (not game tree related) path to a pak file.
1301 Loads the header and directory, adding the files at the beginning
1302 of the list so they override previous pack files.
1305 pack_t *COM_LoadPackFile (const char *packfile)
1307 dpackheader_t header;
1308 int i, numpackfiles, packhandle;
1310 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1313 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1316 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1317 if (memcmp(header.id, "PACK", 4))
1318 Sys_Error ("%s is not a packfile", packfile);
1319 header.dirofs = LittleLong (header.dirofs);
1320 header.dirlen = LittleLong (header.dirlen);
1322 if (header.dirlen % sizeof(dpackfile_t))
1323 Sys_Error ("%s has an invalid directory size", packfile);
1325 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1327 if (numpackfiles > MAX_FILES_IN_PACK)
1328 Sys_Error ("%s has %i files", packfile, numpackfiles);
1330 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1331 strcpy (pack->filename, packfile);
1332 pack->handle = packhandle;
1333 pack->numfiles = numpackfiles;
1334 pack->mempool = Mem_AllocPool(packfile);
1335 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1336 pack->next = packlist;
1339 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1340 Sys_FileSeek (packhandle, header.dirofs);
1341 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1343 // parse the directory
1344 for (i = 0;i < numpackfiles;i++)
1346 strcpy (pack->files[i].name, info[i].name);
1347 pack->files[i].filepos = LittleLong(info[i].filepos);
1348 pack->files[i].filelen = LittleLong(info[i].filelen);
1353 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1360 COM_AddGameDirectory
1362 Sets com_gamedir, adds the directory to the head of the path,
1363 then loads and adds pak1.pak pak2.pak ...
1366 void COM_AddGameDirectory (char *dir)
1368 stringlist_t *list, *current;
1369 searchpath_t *search;
1371 char pakfile[MAX_OSPATH];
1373 strcpy (com_gamedir, dir);
1376 // add the directory to the search path
1378 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1379 strcpy (search->filename, dir);
1380 search->next = com_searchpaths;
1381 com_searchpaths = search;
1383 // add any paks in the directory
1384 list = listdirectory(dir);
1385 for (current = list;current;current = current->next)
1387 if (matchpattern(current->text, "*.pak", true))
1389 sprintf (pakfile, "%s/%s", dir, current->text);
1390 pak = COM_LoadPackFile (pakfile);
1393 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1395 search->next = com_searchpaths;
1396 com_searchpaths = search;
1399 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1402 freedirectory(list);
1410 void COM_InitFilesystem (void)
1413 searchpath_t *search;
1415 strcpy(com_basedir, ".");
1418 // Overrides the system supplied base directory (under GAMENAME)
1419 i = COM_CheckParm ("-basedir");
1420 if (i && i < com_argc-1)
1421 strcpy (com_basedir, com_argv[i+1]);
1423 i = strlen (com_basedir);
1424 if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1425 com_basedir[i-1] = 0;
1427 // start up with GAMENAME by default (id1)
1428 COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1431 com_modified = true;
1432 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1436 // Adds basedir/gamedir as an override game
1437 i = COM_CheckParm ("-game");
1438 if (i && i < com_argc-1)
1440 com_modified = true;
1441 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1444 // -path <dir or packfile> [<dir or packfile>] ...
1445 // Fully specifies the exact search path, overriding the generated one
1446 i = COM_CheckParm ("-path");
1449 com_modified = true;
1450 com_searchpaths = NULL;
1451 while (++i < com_argc)
1453 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1456 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1457 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1459 search->pack = COM_LoadPackFile (com_argv[i]);
1461 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1464 strcpy (search->filename, com_argv[i]);
1465 search->next = com_searchpaths;
1466 com_searchpaths = search;
1471 int COM_FileExists(const char *filename)
1473 searchpath_t *search;
1474 char netpath[MAX_OSPATH];
1478 for (search = com_searchpaths;search;search = search->next)
1483 for (i = 0;i < pak->numfiles;i++)
1484 if (!strcmp (pak->files[i].name, filename))
1489 sprintf (netpath, "%s/%s",search->filename, filename);
1490 findtime = Sys_FileTime (netpath);
1500 //======================================
1501 // LordHavoc: added these because they are useful
1503 void COM_ToLowerString(const char *in, char *out)
1507 if (*in >= 'A' && *in <= 'Z')
1508 *out++ = *in++ + 'a' - 'A';
1514 void COM_ToUpperString(const char *in, char *out)
1518 if (*in >= 'a' && *in <= 'z')
1519 *out++ = *in++ + 'A' - 'a';
1525 int COM_StringBeginsWith(const char *s, const char *match)
1527 for (;*s && *match;s++, match++)