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\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, 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
471 static char *hexchar = "0123456789ABCDEF";
472 void SZ_HexDumpToConsole(sizebuf_t *buf)
476 char *cur, *flushpointer;
478 flushpointer = text + 512;
479 for (i = 0;i < buf->cursize;i++)
483 *cur++ = hexchar[(i >> 12) & 15];
484 *cur++ = hexchar[(i >> 8) & 15];
485 *cur++ = hexchar[(i >> 4) & 15];
486 *cur++ = hexchar[(i >> 0) & 15];
490 else if ((i & 15) == 15)
496 *cur++ = hexchar[(buf->data[i] >> 4) & 15] | 0x80;
497 *cur++ = hexchar[(buf->data[i] >> 0) & 15] | 0x80;
501 *cur++ = hexchar[(buf->data[i] >> 4) & 15];
502 *cur++ = hexchar[(buf->data[i] >> 0) & 15];
504 if (cur >= flushpointer)
507 Con_Printf("%s", text);
516 Con_Printf("%s", text);
521 //============================================================================
529 char *COM_SkipPath (char *pathname)
548 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
549 void COM_StripExtension (char *in, char *out)
556 else if (*in == '/' || *in == '\\' || *in == ':')
571 char *COM_FileExtension (char *in)
573 static char exten[8];
576 while (*in && *in != '.')
581 for (i=0 ; i<7 && *in ; i++,in++)
592 void COM_FileBase (char *in, char *out)
611 strcpy (out,"?model?");
626 void COM_DefaultExtension (char *path, char *extension)
630 // if path doesn't have a .EXT, append extension
631 // (extension should include the .)
633 src = path + strlen(path) - 1;
635 while (*src != '/' && src != path)
638 return; // it has an extension
642 strcat (path, extension);
650 Parse a token out of a string
653 char *COM_Parse (char *data)
666 while ( (c = *data) <= ' ')
669 return NULL; // end of file;
674 if (c=='/' && data[1] == '/')
676 while (*data && *data != '\n')
682 // handle quoted strings specially
699 // parse single characters
700 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
708 // parse a regular word
715 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
728 Returns the position (1 to argc-1) in the program's argument list
729 where the given parameter apears, or 0 if not present
732 int COM_CheckParm (char *parm)
736 for (i=1 ; i<com_argc ; i++)
739 continue; // NEXTSTEP sometimes clears appkit vars.
740 if (!strcmp (parm,com_argv[i]))
751 Looks for the pop.txt file and verifies it.
752 Sets the "registered" cvar.
753 Immediately exits out if an alternate game was attempted to be started without
757 void COM_CheckRegistered (void)
759 Cvar_Set ("cmdline", com_cmdline);
761 if (!Sys_FileTime("gfx/pop.lmp"))
764 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
766 Con_Printf ("Playing shareware version.\n");
770 Cvar_Set ("registered", "1");
771 Con_Printf ("Playing registered version.\n");
775 void COM_Path_f (void);
783 void COM_InitArgv (void)
786 // reconstitute the command line for the cmdline externally visible cvar
788 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
791 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
792 com_cmdline[n++] = com_argv[j][i++];
793 if (n < (CMDLINE_LENGTH - 1))
794 com_cmdline[n++] = ' ';
801 void COM_InitGameType (void)
803 char name[MAX_OSPATH];
804 COM_StripExtension(com_argv[0], name);
805 COM_ToLowerString(name, name);
807 if (strstr(name, "transfusion"))
808 gamemode = GAME_TRANSFUSION;
809 else if (strstr(name, "nehahra"))
810 gamemode = GAME_NEHAHRA;
811 else if (strstr(name, "hipnotic"))
812 gamemode = GAME_HIPNOTIC;
813 else if (strstr(name, "rogue"))
814 gamemode = GAME_ROGUE;
816 gamemode = GAME_NORMAL;
818 if (COM_CheckParm ("-transfusion"))
819 gamemode = GAME_TRANSFUSION;
820 else if (COM_CheckParm ("-nehahra"))
821 gamemode = GAME_NEHAHRA;
822 else if (COM_CheckParm ("-hipnotic"))
823 gamemode = GAME_HIPNOTIC;
824 else if (COM_CheckParm ("-rogue"))
825 gamemode = GAME_ROGUE;
826 else if (COM_CheckParm ("-quake"))
827 gamemode = GAME_NORMAL;
832 gamename = "DarkPlaces-Quake";
836 gamename = "Darkplaces-Hipnotic";
837 gamedirname = "hipnotic";
840 gamename = "Darkplaces-Rogue";
841 gamedirname = "rogue";
844 gamename = "DarkPlaces-Nehahra";
845 gamedirname = "nehahra";
847 case GAME_TRANSFUSION:
848 gamename = "Transfusion";
849 gamedirname = "transfusion";
852 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
858 extern void Mathlib_Init(void);
867 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
868 qbyte swaptest[2] = {1,0};
870 // set the byte swapping variables in a portable manner
871 if ( *(short *)swaptest == 1)
873 BigShort = ShortSwap;
874 LittleShort = ShortNoSwap;
876 LittleLong = LongNoSwap;
877 BigFloat = FloatSwap;
878 LittleFloat = FloatNoSwap;
882 BigShort = ShortNoSwap;
883 LittleShort = ShortSwap;
884 BigLong = LongNoSwap;
885 LittleLong = LongSwap;
886 BigFloat = FloatNoSwap;
887 LittleFloat = FloatSwap;
891 pak_mempool = Mem_AllocPool("paks");
893 Cvar_RegisterVariable (®istered);
894 Cvar_RegisterVariable (&cmdline);
895 Cmd_AddCommand ("path", COM_Path_f);
899 COM_InitFilesystem ();
900 COM_CheckRegistered ();
908 does a varargs printf into a temp buffer, so I don't need to have
909 varargs versions of all text functions.
910 FIXME: make this buffer size safe someday
913 char *va(char *format, ...)
916 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
917 static char string[8][1024], *s;
918 static int stringindex = 0;
920 s = string[stringindex];
921 stringindex = (stringindex + 1) & 7;
922 va_start (argptr, format);
923 vsprintf (s, format,argptr);
931 =============================================================================
935 =============================================================================
947 char name[MAX_QPATH];
948 int filepos, filelen;
951 typedef struct pack_s
953 char filename[MAX_OSPATH];
967 int filepos, filelen;
977 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
978 #define MAX_FILES_IN_PACK 65536
980 pack_t *packlist = NULL;
983 char com_cachedir[MAX_OSPATH];
985 char com_gamedir[MAX_OSPATH];
987 typedef struct searchpath_s
989 char filename[MAX_OSPATH];
990 pack_t *pack; // only one of filename / pack will be used
991 struct searchpath_s *next;
994 searchpath_t *com_searchpaths;
1002 void COM_Path_f (void)
1006 Con_Printf ("Current search path:\n");
1007 for (s=com_searchpaths ; s ; s=s->next)
1011 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1014 Con_Printf ("%s\n", s->filename);
1022 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1025 void COM_CreatePath (char *path)
1029 for (ofs = path+1 ; *ofs ; ofs++)
1031 if (*ofs == '/' || *ofs == '\\')
1033 // create the directory
1047 The filename will be prefixed by the current game directory
1050 qboolean COM_WriteFile (char *filename, void *data, int len)
1053 char name[MAX_OSPATH];
1055 sprintf (name, "%s/%s", com_gamedir, filename);
1057 // LordHavoc: added this
1058 COM_CreatePath (name); // create directories up to the file
1060 handle = Sys_FileOpenWrite (name);
1063 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1067 Con_DPrintf ("COM_WriteFile: %s\n", name);
1068 Sys_FileWrite (handle, data, len);
1069 Sys_FileClose (handle);
1078 Copies a file over from the net to the local cache, creating any directories
1079 needed. This is for the convenience of developers using ISDN from home.
1082 void COM_CopyFile (char *netpath, char *cachepath)
1085 int remaining, count;
1088 remaining = Sys_FileOpenRead (netpath, &in);
1089 COM_CreatePath (cachepath); // create directories up to the cache file
1090 out = Sys_FileOpenWrite (cachepath);
1094 if (remaining < sizeof(buf))
1097 count = sizeof(buf);
1098 Sys_FileRead (in, buf, count);
1099 Sys_FileWrite (out, buf, count);
1104 Sys_FileClose (out);
1112 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1114 int fd = open (path, O_RDONLY);
1115 unsigned char id[2];
1116 unsigned char len_bytes[4];
1120 Sys_Error ("Couldn't open %s", path);
1123 if (offs < 0 || len < 0)
1127 len = lseek (fd, 0, SEEK_END);
1128 lseek (fd, 0, SEEK_SET);
1130 lseek (fd, offs, SEEK_SET);
1134 if (id[0] == 0x1f && id[1] == 0x8b)
1136 lseek (fd, offs + len - 4, SEEK_SET);
1137 read (fd, len_bytes, 4);
1138 len = ((len_bytes[3] << 24)
1139 | (len_bytes[2] << 16)
1140 | (len_bytes[1] << 8)
1144 lseek (fd, offs, SEEK_SET);
1148 setmode (fd, O_BINARY);
1151 return Qdopen (fd, "rbz");
1153 return Qdopen (fd, "rb");
1160 Finds the file in the search path.
1161 Sets com_filesize and one of handle or file
1164 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1166 searchpath_t *search;
1167 char netpath[MAX_OSPATH];
1169 char cachepath[MAX_OSPATH];
1175 char gzfilename[MAX_OSPATH];
1178 filenamelen = strlen (filename);
1179 sprintf (gzfilename, "%s.gz", filename);
1182 Sys_Error ("COM_FindFile: file not set");
1185 // search through the path, one element at a time
1187 search = com_searchpaths;
1189 for ( ; search ; search = search->next)
1191 // is the element a pak file?
1194 // look through all the pak file elements
1196 for (i=0 ; i<pak->numfiles ; i++)
1197 if (!strcmp (pak->files[i].name, filename)
1198 || !strcmp (pak->files[i].name, gzfilename))
1201 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1202 // open a new file on the pakfile
1203 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1204 return com_filesize;
1209 sprintf (netpath, "%s/%s",search->filename, filename);
1211 findtime = Sys_FileTime (netpath);
1216 // see if the file needs to be updated in the cache
1217 if (com_cachedir[0])
1220 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1221 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1223 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1225 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1228 cachetime = Sys_FileTime (cachepath);
1230 if (cachetime < findtime)
1231 COM_CopyFile (netpath, cachepath);
1232 strcpy (netpath, cachepath);
1237 Sys_Printf ("FindFile: %s\n",netpath);
1238 *file = COM_OpenRead (netpath, -1, -1, zip);
1239 return com_filesize;
1245 Sys_Printf ("FindFile: can't find %s\n", filename);
1257 If the requested file is inside a packfile, a new QFile * will be opened
1261 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1263 return COM_FindFile (filename, file, quiet, zip);
1271 Filename are reletive to the quake directory.
1272 Always appends a 0 byte.
1277 qbyte *COM_LoadFile (char *path, qboolean quiet)
1284 buf = NULL; // quiet compiler warning
1287 // look for it in the filesystem or pack files
1288 len = COM_FOpenFile (path, &h, quiet, true);
1294 // extract the filename base name for hunk tag
1295 COM_FileBase (path, base);
1297 buf = Mem_Alloc(tempmempool, len+1);
1299 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1301 ((qbyte *)buf)[len] = 0;
1303 Qread (h, buf, len);
1313 Takes an explicit (not game tree related) path to a pak file.
1315 Loads the header and directory, adding the files at the beginning
1316 of the list so they override previous pack files.
1319 pack_t *COM_LoadPackFile (char *packfile)
1321 dpackheader_t header;
1326 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1329 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1332 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1333 if (memcmp(header.id, "PACK", 4))
1334 Sys_Error ("%s is not a packfile", packfile);
1335 header.dirofs = LittleLong (header.dirofs);
1336 header.dirlen = LittleLong (header.dirlen);
1338 if (header.dirlen % sizeof(dpackfile_t))
1339 Sys_Error ("%s has an invalid directory size", packfile);
1341 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1343 if (numpackfiles > MAX_FILES_IN_PACK)
1344 Sys_Error ("%s has %i files", packfile, numpackfiles);
1346 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1347 strcpy (pack->filename, packfile);
1348 pack->handle = packhandle;
1349 pack->numfiles = numpackfiles;
1350 pack->mempool = Mem_AllocPool(packfile);
1351 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1352 pack->next = packlist;
1355 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1356 Sys_FileSeek (packhandle, header.dirofs);
1357 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1359 // parse the directory
1360 for (i = 0;i < numpackfiles;i++)
1362 strcpy (pack->files[i].name, info[i].name);
1363 pack->files[i].filepos = LittleLong(info[i].filepos);
1364 pack->files[i].filelen = LittleLong(info[i].filelen);
1369 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1376 COM_AddGameDirectory
1378 Sets com_gamedir, adds the directory to the head of the path,
1379 then loads and adds pak1.pak pak2.pak ...
1382 void COM_AddGameDirectory (char *dir)
1384 stringlist_t *list, *current;
1385 searchpath_t *search;
1387 char pakfile[MAX_OSPATH];
1389 strcpy (com_gamedir, dir);
1392 // add the directory to the search path
1394 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1395 strcpy (search->filename, dir);
1396 search->next = com_searchpaths;
1397 com_searchpaths = search;
1399 // add any paks in the directory
1400 list = listdirectory(dir);
1401 for (current = list;current;current = current->next)
1403 if (matchpattern(current->text, "*.pak"))
1405 sprintf (pakfile, "%s/%s", dir, current->text);
1406 pak = COM_LoadPackFile (pakfile);
1409 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1411 search->next = com_searchpaths;
1412 com_searchpaths = search;
1415 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1418 freedirectory(list);
1426 void COM_InitFilesystem (void)
1429 searchpath_t *search;
1431 strcpy(com_basedir, ".");
1434 // Overrides the system supplied base directory (under GAMENAME)
1435 i = COM_CheckParm ("-basedir");
1436 if (i && i < com_argc-1)
1437 strcpy (com_basedir, com_argv[i+1]);
1439 i = strlen (com_basedir);
1440 if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1441 com_basedir[i-1] = 0;
1443 // start up with GAMENAME by default (id1)
1444 COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1447 com_modified = true;
1448 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1452 // Adds basedir/gamedir as an override game
1453 i = COM_CheckParm ("-game");
1454 if (i && i < com_argc-1)
1456 com_modified = true;
1457 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1460 // -path <dir or packfile> [<dir or packfile>] ...
1461 // Fully specifies the exact search path, overriding the generated one
1462 i = COM_CheckParm ("-path");
1465 com_modified = true;
1466 com_searchpaths = NULL;
1467 while (++i < com_argc)
1469 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1472 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1473 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1475 search->pack = COM_LoadPackFile (com_argv[i]);
1477 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1480 strcpy (search->filename, com_argv[i]);
1481 search->next = com_searchpaths;
1482 com_searchpaths = search;
1487 int COM_FileExists(char *filename)
1489 searchpath_t *search;
1490 char netpath[MAX_OSPATH];
1495 for (search = com_searchpaths;search;search = search->next)
1500 for (i = 0;i < pak->numfiles;i++)
1501 if (!strcmp (pak->files[i].name, filename))
1506 sprintf (netpath, "%s/%s",search->filename, filename);
1507 findtime = Sys_FileTime (netpath);
1517 //======================================
1518 // LordHavoc: added these because they are useful
1520 void COM_ToLowerString(char *in, char *out)
1524 if (*in >= 'A' && *in <= 'Z')
1525 *out++ = *in++ + 'a' - 'A';
1531 void COM_ToUpperString(char *in, char *out)
1535 if (*in >= 'a' && *in <= 'z')
1536 *out++ = *in++ + 'A' - 'a';
1542 int COM_StringBeginsWith(const char *s, const char *match)
1544 for (;*s && *match;s++, match++)