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];
53 char com_modname[MAX_OSPATH];
58 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.
60 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
61 only used during filesystem initialization.
63 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.
65 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
66 specified, when a file is found by the normal search path, it will be mirrored
67 into the cache directory, then opened there.
72 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.
76 //============================================================================
80 ============================================================================
82 LIBRARY REPLACEMENT FUNCTIONS
84 ============================================================================
87 int Q_strncasecmp (const char *s1, const char *s2, int n)
97 return 0; // strings are equal until end point
101 if (c1 >= 'a' && c1 <= 'z')
103 if (c2 >= 'a' && c2 <= 'z')
106 return -1; // strings not equal
109 return 0; // strings are equal
115 int Q_strcasecmp (const char *s1, const char *s2)
117 return Q_strncasecmp (s1, s2, 99999);
121 ============================================================================
125 ============================================================================
128 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
129 short (*BigShort) (short l);
130 short (*LittleShort) (short l);
131 int (*BigLong) (int l);
132 int (*LittleLong) (int l);
133 float (*BigFloat) (float l);
134 float (*LittleFloat) (float l);
137 short ShortSwap (short l)
147 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
148 short ShortNoSwap (short l)
163 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
166 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
167 int LongNoSwap (int l)
173 float FloatSwap (float f)
183 dat2.b[0] = dat1.b[3];
184 dat2.b[1] = dat1.b[2];
185 dat2.b[2] = dat1.b[1];
186 dat2.b[3] = dat1.b[0];
190 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
191 float FloatNoSwap (float f)
198 ==============================================================================
202 Handles byte ordering and avoids alignment errors
203 ==============================================================================
210 void MSG_WriteChar (sizebuf_t *sb, int c)
214 buf = SZ_GetSpace (sb, 1);
218 void MSG_WriteByte (sizebuf_t *sb, int c)
222 buf = SZ_GetSpace (sb, 1);
226 void MSG_WriteShort (sizebuf_t *sb, int c)
230 buf = SZ_GetSpace (sb, 2);
235 void MSG_WriteLong (sizebuf_t *sb, int c)
239 buf = SZ_GetSpace (sb, 4);
241 buf[1] = (c>>8)&0xff;
242 buf[2] = (c>>16)&0xff;
246 void MSG_WriteFloat (sizebuf_t *sb, float f)
256 dat.l = LittleLong (dat.l);
258 SZ_Write (sb, &dat.l, 4);
261 void MSG_WriteString (sizebuf_t *sb, const char *s)
264 SZ_Write (sb, "", 1);
266 SZ_Write (sb, s, strlen(s)+1);
269 // used by server (always latest dpprotocol)
270 void MSG_WriteDPCoord (sizebuf_t *sb, float f)
273 MSG_WriteShort (sb, (int)(f + 0.5f));
275 MSG_WriteShort (sb, (int)(f - 0.5f));
278 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
281 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
283 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
286 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
287 void MSG_WriteAngle (sizebuf_t *sb, float f)
290 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
292 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
299 qboolean msg_badread;
301 void MSG_BeginReading (void)
307 int MSG_ReadShort (void)
311 if (msg_readcount+2 > net_message.cursize)
317 c = (short)(net_message.data[msg_readcount]
318 + (net_message.data[msg_readcount+1]<<8));
325 int MSG_ReadLong (void)
329 if (msg_readcount+4 > net_message.cursize)
335 c = net_message.data[msg_readcount]
336 + (net_message.data[msg_readcount+1]<<8)
337 + (net_message.data[msg_readcount+2]<<16)
338 + (net_message.data[msg_readcount+3]<<24);
345 float MSG_ReadFloat (void)
354 dat.b[0] = net_message.data[msg_readcount];
355 dat.b[1] = net_message.data[msg_readcount+1];
356 dat.b[2] = net_message.data[msg_readcount+2];
357 dat.b[3] = net_message.data[msg_readcount+3];
360 dat.l = LittleLong (dat.l);
365 char *MSG_ReadString (void)
367 static char string[2048];
374 if (c == -1 || c == 0)
378 } while (l < (int)sizeof(string)-1);
385 // used by server (always latest dpprotocol)
386 float MSG_ReadDPCoord (void)
388 return (signed short) MSG_ReadShort();
392 float MSG_ReadCoord (void)
394 if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
395 return (signed short) MSG_ReadShort();
396 else if (dpprotocol == DPPROTOCOL_VERSION1)
397 return MSG_ReadFloat();
399 return MSG_ReadShort() * (1.0f/8.0f);
403 //===========================================================================
405 void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name)
409 buf->mempool = Mem_AllocPool(name);
410 buf->data = Mem_Alloc(buf->mempool, startsize);
411 buf->maxsize = startsize;
416 void SZ_Free (sizebuf_t *buf)
418 Mem_FreePool(&buf->mempool);
424 void SZ_Clear (sizebuf_t *buf)
429 void *SZ_GetSpace (sizebuf_t *buf, int length)
433 if (buf->cursize + length > buf->maxsize)
435 if (!buf->allowoverflow)
436 Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
438 if (length > buf->maxsize)
439 Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
441 buf->overflowed = true;
442 Con_Printf ("SZ_GetSpace: overflow\n");
446 data = buf->data + buf->cursize;
447 buf->cursize += length;
452 void SZ_Write (sizebuf_t *buf, const void *data, int length)
454 memcpy (SZ_GetSpace(buf,length),data,length);
457 void SZ_Print (sizebuf_t *buf, const char *data)
460 len = strlen(data)+1;
462 // byte * cast to keep VC++ happy
463 if (buf->data[buf->cursize-1])
464 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
466 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
469 static char *hexchar = "0123456789ABCDEF";
470 void SZ_HexDumpToConsole(const sizebuf_t *buf)
474 char *cur, *flushpointer;
476 flushpointer = text + 512;
477 for (i = 0;i < buf->cursize;i++)
481 *cur++ = hexchar[(i >> 12) & 15];
482 *cur++ = hexchar[(i >> 8) & 15];
483 *cur++ = hexchar[(i >> 4) & 15];
484 *cur++ = hexchar[(i >> 0) & 15];
488 else if ((i & 15) == 15)
494 *cur++ = hexchar[(buf->data[i] >> 4) & 15] | 0x80;
495 *cur++ = hexchar[(buf->data[i] >> 0) & 15] | 0x80;
499 *cur++ = hexchar[(buf->data[i] >> 4) & 15];
500 *cur++ = hexchar[(buf->data[i] >> 0) & 15];
502 if (cur >= flushpointer)
505 Con_Printf("%s", text);
514 Con_Printf("%s", text);
519 //============================================================================
527 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
528 void COM_StripExtension (const char *in, char *out)
535 else if (*in == '/' || *in == '\\' || *in == ':')
550 char *COM_FileExtension (const char *in)
552 static char exten[8];
555 while (*in && *in != '.')
560 for (i=0 ; i<7 && *in ; i++,in++)
571 void COM_FileBase (const char *in, char *out)
573 const char *slash, *dot, *s;
589 strcpy (out,"?model?");
604 void COM_DefaultExtension (char *path, const char *extension)
608 // if path doesn't have a .EXT, append extension
609 // (extension should include the .)
611 src = path + strlen(path) - 1;
613 while (*src != '/' && src != path)
616 return; // it has an extension
620 strcat (path, extension);
628 Parse a token out of a string
631 int COM_ParseToken (const char **datapointer)
635 const char *data = *datapointer;
648 while ((c = *data) <= ' ')
660 if (c=='/' && data[1] == '/')
662 while (*data && *data != '\n')
668 // handle quoted strings specially
686 // parse single characters
687 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
692 *datapointer = data+1;
696 // parse a regular word
703 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
717 Returns the position (1 to argc-1) in the program's argument list
718 where the given parameter apears, or 0 if not present
721 int COM_CheckParm (const char *parm)
725 for (i=1 ; i<com_argc ; i++)
728 continue; // NEXTSTEP sometimes clears appkit vars.
729 if (!strcmp (parm,com_argv[i]))
740 Looks for the pop.txt file and verifies it.
741 Sets the "registered" cvar.
742 Immediately exits out if an alternate game was attempted to be started without
746 void COM_CheckRegistered (void)
748 Cvar_Set ("cmdline", com_cmdline);
750 if (!Sys_FileTime("gfx/pop.lmp"))
753 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
755 Con_Printf ("Playing shareware version.\n");
759 Cvar_Set ("registered", "1");
760 Con_Printf ("Playing registered version.\n");
764 void COM_Path_f (void);
772 void COM_InitArgv (void)
775 // reconstitute the command line for the cmdline externally visible cvar
777 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
780 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
781 com_cmdline[n++] = com_argv[j][i++];
782 if (n < (CMDLINE_LENGTH - 1))
783 com_cmdline[n++] = ' ';
790 void COM_InitGameType (void)
792 char name[MAX_OSPATH];
793 COM_StripExtension(com_argv[0], name);
794 COM_ToLowerString(name, name);
796 if (strstr(name, "transfusion"))
797 gamemode = GAME_TRANSFUSION;
798 else if (strstr(name, "nexiuz"))
799 gamemode = GAME_NEXIUZ;
800 else if (strstr(name, "nehahra"))
801 gamemode = GAME_NEHAHRA;
802 else if (strstr(name, "hipnotic"))
803 gamemode = GAME_HIPNOTIC;
804 else if (strstr(name, "rogue"))
805 gamemode = GAME_ROGUE;
807 gamemode = GAME_NORMAL;
809 if (COM_CheckParm ("-transfusion"))
810 gamemode = GAME_TRANSFUSION;
811 else if (COM_CheckParm ("-nexiuz"))
812 gamemode = GAME_NEXIUZ;
813 else if (COM_CheckParm ("-nehahra"))
814 gamemode = GAME_NEHAHRA;
815 else if (COM_CheckParm ("-hipnotic"))
816 gamemode = GAME_HIPNOTIC;
817 else if (COM_CheckParm ("-rogue"))
818 gamemode = GAME_ROGUE;
819 else if (COM_CheckParm ("-quake"))
820 gamemode = GAME_NORMAL;
825 gamename = "DarkPlaces-Quake";
829 gamename = "Darkplaces-Hipnotic";
830 gamedirname = "hipnotic";
833 gamename = "Darkplaces-Rogue";
834 gamedirname = "rogue";
837 gamename = "DarkPlaces-Nehahra";
838 gamedirname = "nehahra";
842 gamedirname = "data";
844 case GAME_TRANSFUSION:
845 gamename = "Transfusion";
846 gamedirname = "transfusion";
849 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
855 extern void Mathlib_Init(void);
864 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
865 qbyte swaptest[2] = {1,0};
867 // set the byte swapping variables in a portable manner
868 if ( *(short *)swaptest == 1)
870 BigShort = ShortSwap;
871 LittleShort = ShortNoSwap;
873 LittleLong = LongNoSwap;
874 BigFloat = FloatSwap;
875 LittleFloat = FloatNoSwap;
879 BigShort = ShortNoSwap;
880 LittleShort = ShortSwap;
881 BigLong = LongNoSwap;
882 LittleLong = LongSwap;
883 BigFloat = FloatNoSwap;
884 LittleFloat = FloatSwap;
888 pak_mempool = Mem_AllocPool("paks");
890 Cvar_RegisterVariable (®istered);
891 Cvar_RegisterVariable (&cmdline);
892 Cmd_AddCommand ("path", COM_Path_f);
896 COM_InitFilesystem ();
897 COM_CheckRegistered ();
907 does a varargs printf into a temp buffer, so I don't need to have
908 varargs versions of all text functions.
909 FIXME: make this buffer size safe someday
912 char *va(const char *format, ...)
915 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
916 static char string[8][1024], *s;
917 static int stringindex = 0;
919 s = string[stringindex];
920 stringindex = (stringindex + 1) & 7;
921 va_start (argptr, format);
922 vsprintf (s, format,argptr);
930 =============================================================================
934 =============================================================================
946 char name[MAX_QPATH];
947 int filepos, filelen;
950 typedef struct pack_s
952 char filename[MAX_OSPATH];
966 int filepos, filelen;
976 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
977 #define MAX_FILES_IN_PACK 65536
979 pack_t *packlist = NULL;
982 char com_cachedir[MAX_OSPATH];
984 char com_gamedir[MAX_OSPATH];
986 typedef struct searchpath_s
988 // only one of filename / pack will be used
989 char filename[MAX_OSPATH];
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 (const char *filename, void *data, int len)
1053 char name[MAX_OSPATH];
1055 sprintf (name, "%s/%s", com_gamedir, filename);
1057 // LordHavoc: added this
1058 // create directories up to the file
1059 COM_CreatePath (name);
1061 handle = Sys_FileOpenWrite (name);
1064 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1068 Con_DPrintf ("COM_WriteFile: %s\n", name);
1069 Sys_FileWrite (handle, data, len);
1070 Sys_FileClose (handle);
1079 Copies a file over from the net to the local cache, creating any directories
1080 needed. This is for the convenience of developers using ISDN from home.
1083 void COM_CopyFile (char *netpath, char *cachepath)
1085 int in, out, 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 < (int)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], len_bytes[4];
1119 Sys_Error ("Couldn't open %s", path);
1122 if (offs < 0 || len < 0)
1126 len = lseek (fd, 0, SEEK_END);
1127 lseek (fd, 0, SEEK_SET);
1129 lseek (fd, offs, SEEK_SET);
1133 if (id[0] == 0x1f && id[1] == 0x8b)
1135 lseek (fd, offs + len - 4, SEEK_SET);
1136 read (fd, len_bytes, 4);
1137 len = ((len_bytes[3] << 24)
1138 | (len_bytes[2] << 16)
1139 | (len_bytes[1] << 8)
1143 lseek (fd, offs, SEEK_SET);
1147 setmode (fd, O_BINARY);
1150 return Qdopen (fd, "rbz");
1152 return Qdopen (fd, "rb");
1159 Finds the file in the search path.
1160 Sets com_filesize and one of handle or file
1163 int COM_FindFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1165 searchpath_t *search;
1166 char netpath[MAX_OSPATH];
1168 char cachepath[MAX_OSPATH];
1172 int i, findtime, filenamelen;
1173 char gzfilename[MAX_OSPATH];
1175 filenamelen = strlen (filename);
1176 sprintf (gzfilename, "%s.gz", filename);
1179 Sys_Error ("COM_FindFile: file not set");
1182 // search through the path, one element at a time
1184 search = com_searchpaths;
1186 for ( ; search ; search = search->next)
1188 // is the element a pak file?
1191 // look through all the pak file elements
1193 for (i=0 ; i<pak->numfiles ; i++)
1194 if (!strcmp (pak->files[i].name, filename)
1195 || !strcmp (pak->files[i].name, gzfilename))
1198 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1199 // open a new file on the pakfile
1200 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1201 return com_filesize;
1206 sprintf (netpath, "%s/%s",search->filename, filename);
1208 findtime = Sys_FileTime (netpath);
1213 // see if the file needs to be updated in the cache
1214 if (com_cachedir[0])
1217 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1218 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1220 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1222 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1225 cachetime = Sys_FileTime (cachepath);
1227 if (cachetime < findtime)
1228 COM_CopyFile (netpath, cachepath);
1229 strcpy (netpath, cachepath);
1233 Sys_Printf ("FindFile: %s\n",netpath);
1234 *file = COM_OpenRead (netpath, -1, -1, zip);
1235 return com_filesize;
1240 Sys_Printf ("FindFile: can't find %s\n", filename);
1252 If the requested file is inside a packfile, a new QFile * will be opened
1256 int COM_FOpenFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1258 return COM_FindFile (filename, file, quiet, zip);
1266 Filename are reletive to the quake directory.
1267 Always appends a 0 byte.
1272 qbyte *COM_LoadFile (const char *path, qboolean quiet)
1279 buf = NULL; // quiet compiler warning
1282 // look for it in the filesystem or pack files
1283 len = COM_FOpenFile (path, &h, quiet, true);
1289 // extract the filename base name for hunk tag
1290 COM_FileBase (path, base);
1292 buf = Mem_Alloc(tempmempool, len+1);
1294 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1296 ((qbyte *)buf)[len] = 0;
1298 Qread (h, buf, len);
1308 Takes an explicit (not game tree related) path to a pak file.
1310 Loads the header and directory, adding the files at the beginning
1311 of the list so they override previous pack files.
1314 pack_t *COM_LoadPackFile (const char *packfile)
1316 dpackheader_t header;
1317 int i, numpackfiles, packhandle;
1319 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1322 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1325 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1326 if (memcmp(header.id, "PACK", 4))
1327 Sys_Error ("%s is not a packfile", packfile);
1328 header.dirofs = LittleLong (header.dirofs);
1329 header.dirlen = LittleLong (header.dirlen);
1331 if (header.dirlen % sizeof(dpackfile_t))
1332 Sys_Error ("%s has an invalid directory size", packfile);
1334 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1336 if (numpackfiles > MAX_FILES_IN_PACK)
1337 Sys_Error ("%s has %i files", packfile, numpackfiles);
1339 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1340 strcpy (pack->filename, packfile);
1341 pack->handle = packhandle;
1342 pack->numfiles = numpackfiles;
1343 pack->mempool = Mem_AllocPool(packfile);
1344 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1345 pack->next = packlist;
1348 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1349 Sys_FileSeek (packhandle, header.dirofs);
1350 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1352 // parse the directory
1353 for (i = 0;i < numpackfiles;i++)
1355 strcpy (pack->files[i].name, info[i].name);
1356 pack->files[i].filepos = LittleLong(info[i].filepos);
1357 pack->files[i].filelen = LittleLong(info[i].filelen);
1362 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1369 COM_AddGameDirectory
1371 Sets com_gamedir, adds the directory to the head of the path,
1372 then loads and adds pak1.pak pak2.pak ...
1375 void COM_AddGameDirectory (char *dir)
1377 stringlist_t *list, *current;
1378 searchpath_t *search;
1380 char pakfile[MAX_OSPATH];
1382 strcpy (com_gamedir, dir);
1385 // add the directory to the search path
1387 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1388 strcpy (search->filename, dir);
1389 search->next = com_searchpaths;
1390 com_searchpaths = search;
1392 // add any paks in the directory
1393 list = listdirectory(dir);
1394 for (current = list;current;current = current->next)
1396 if (matchpattern(current->text, "*.pak", true))
1398 sprintf (pakfile, "%s/%s", dir, current->text);
1399 pak = COM_LoadPackFile (pakfile);
1402 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1404 search->next = com_searchpaths;
1405 com_searchpaths = search;
1408 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1411 freedirectory(list);
1419 void COM_InitFilesystem (void)
1422 searchpath_t *search;
1424 strcpy(com_basedir, ".");
1427 // Overrides the system supplied base directory (under GAMENAME)
1428 i = COM_CheckParm ("-basedir");
1429 if (i && i < com_argc-1)
1430 strcpy (com_basedir, com_argv[i+1]);
1432 i = strlen (com_basedir);
1433 if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1434 com_basedir[i-1] = 0;
1436 // start up with GAMENAME by default (id1)
1437 strcpy(com_modname, GAMENAME);
1438 COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1441 com_modified = true;
1442 strcpy(com_modname, gamedirname);
1443 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1447 // Adds basedir/gamedir as an override game
1448 i = COM_CheckParm ("-game");
1449 if (i && i < com_argc-1)
1451 com_modified = true;
1452 strcpy(com_modname, com_argv[i+1]);
1453 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1456 // -path <dir or packfile> [<dir or packfile>] ...
1457 // Fully specifies the exact search path, overriding the generated one
1458 i = COM_CheckParm ("-path");
1461 com_modified = true;
1462 com_searchpaths = NULL;
1463 while (++i < com_argc)
1465 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1468 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1469 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1471 search->pack = COM_LoadPackFile (com_argv[i]);
1473 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1476 strcpy (search->filename, com_argv[i]);
1477 search->next = com_searchpaths;
1478 com_searchpaths = search;
1483 int COM_FileExists(const char *filename)
1485 searchpath_t *search;
1486 char netpath[MAX_OSPATH];
1490 for (search = com_searchpaths;search;search = search->next)
1495 for (i = 0;i < pak->numfiles;i++)
1496 if (!strcmp (pak->files[i].name, filename))
1501 sprintf (netpath, "%s/%s",search->filename, filename);
1502 findtime = Sys_FileTime (netpath);
1512 //======================================
1513 // LordHavoc: added these because they are useful
1515 void COM_ToLowerString(const char *in, char *out)
1519 if (*in >= 'A' && *in <= 'Z')
1520 *out++ = *in++ + 'a' - 'A';
1526 void COM_ToUpperString(const char *in, char *out)
1530 if (*in >= 'a' && *in <= 'z')
1531 *out++ = *in++ + 'A' - 'a';
1537 int COM_StringBeginsWith(const char *s, const char *match)
1539 for (;*s && *match;s++, match++)