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 #define NUM_SAFE_ARGVS 7
34 static char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
35 static char *argvdummy = " ";
37 static char *safeargvs[NUM_SAFE_ARGVS] =
38 {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-window"};
40 cvar_t registered = {0, "registered","0"};
41 cvar_t cmdline = {0, "cmdline","0"};
43 mempool_t *pak_mempool;
45 qboolean com_modified; // set true if using non-id files
47 qboolean msg_suppress_1 = 0;
49 void COM_InitFilesystem (void);
55 // LordHavoc: made commandline 1024 characters instead of 256
56 #define CMDLINE_LENGTH 1024
57 char com_cmdline[CMDLINE_LENGTH];
65 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.
67 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
68 only used during filesystem initialization.
70 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.
72 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
73 specified, when a file is found by the normal search path, it will be mirrored
74 into the cache directory, then opened there.
79 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.
83 //============================================================================
87 ============================================================================
89 LIBRARY REPLACEMENT FUNCTIONS
91 ============================================================================
94 int Q_strncasecmp (char *s1, char *s2, int n)
104 return 0; // strings are equal until end point
108 if (c1 >= 'a' && c1 <= 'z')
110 if (c2 >= 'a' && c2 <= 'z')
113 return -1; // strings not equal
116 return 0; // strings are equal
122 int Q_strcasecmp (char *s1, char *s2)
124 return Q_strncasecmp (s1, s2, 99999);
128 ============================================================================
132 ============================================================================
135 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
136 short (*BigShort) (short l);
137 short (*LittleShort) (short l);
138 int (*BigLong) (int l);
139 int (*LittleLong) (int l);
140 float (*BigFloat) (float l);
141 float (*LittleFloat) (float l);
144 short ShortSwap (short l)
154 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
155 short ShortNoSwap (short l)
170 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
173 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
174 int LongNoSwap (int l)
180 float FloatSwap (float f)
190 dat2.b[0] = dat1.b[3];
191 dat2.b[1] = dat1.b[2];
192 dat2.b[2] = dat1.b[1];
193 dat2.b[3] = dat1.b[0];
197 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
198 float FloatNoSwap (float f)
205 ==============================================================================
209 Handles byte ordering and avoids alignment errors
210 ==============================================================================
217 void MSG_WriteChar (sizebuf_t *sb, int c)
221 buf = SZ_GetSpace (sb, 1);
225 void MSG_WriteByte (sizebuf_t *sb, int c)
229 buf = SZ_GetSpace (sb, 1);
233 void MSG_WriteShort (sizebuf_t *sb, int c)
237 buf = SZ_GetSpace (sb, 2);
242 void MSG_WriteLong (sizebuf_t *sb, int c)
246 buf = SZ_GetSpace (sb, 4);
248 buf[1] = (c>>8)&0xff;
249 buf[2] = (c>>16)&0xff;
253 void MSG_WriteFloat (sizebuf_t *sb, float f)
263 dat.l = LittleLong (dat.l);
265 SZ_Write (sb, &dat.l, 4);
268 void MSG_WriteString (sizebuf_t *sb, char *s)
271 SZ_Write (sb, "", 1);
273 SZ_Write (sb, s, strlen(s)+1);
276 // used by server (always latest dpprotocol)
277 void MSG_WriteDPCoord (sizebuf_t *sb, float f)
280 MSG_WriteShort (sb, (int)(f + 0.5f));
282 MSG_WriteShort (sb, (int)(f - 0.5f));
285 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
288 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
290 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
293 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
294 void MSG_WriteAngle (sizebuf_t *sb, float f)
297 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
299 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
306 qboolean msg_badread;
308 void MSG_BeginReading (void)
314 int MSG_ReadShort (void)
318 if (msg_readcount+2 > net_message.cursize)
324 c = (short)(net_message.data[msg_readcount]
325 + (net_message.data[msg_readcount+1]<<8));
332 int MSG_ReadLong (void)
336 if (msg_readcount+4 > net_message.cursize)
342 c = net_message.data[msg_readcount]
343 + (net_message.data[msg_readcount+1]<<8)
344 + (net_message.data[msg_readcount+2]<<16)
345 + (net_message.data[msg_readcount+3]<<24);
352 float MSG_ReadFloat (void)
361 dat.b[0] = net_message.data[msg_readcount];
362 dat.b[1] = net_message.data[msg_readcount+1];
363 dat.b[2] = net_message.data[msg_readcount+2];
364 dat.b[3] = net_message.data[msg_readcount+3];
367 dat.l = LittleLong (dat.l);
372 char *MSG_ReadString (void)
374 static char string[2048];
381 if (c == -1 || c == 0)
385 } while (l < sizeof(string)-1);
392 // used by server (always latest dpprotocol)
393 float MSG_ReadDPCoord (void)
395 return (signed short) MSG_ReadShort();
399 float MSG_ReadCoord (void)
401 if (dpprotocol == DPPROTOCOL_VERSION2 || dpprotocol == DPPROTOCOL_VERSION3)
402 return (signed short) MSG_ReadShort();
403 else if (dpprotocol == DPPROTOCOL_VERSION1)
404 return MSG_ReadFloat();
406 return MSG_ReadShort() * (1.0f/8.0f);
410 //===========================================================================
412 void SZ_Alloc (sizebuf_t *buf, int startsize, char *name)
416 buf->mempool = Mem_AllocPool(name);
417 buf->data = Mem_Alloc(buf->mempool, startsize);
418 buf->maxsize = startsize;
423 void SZ_Free (sizebuf_t *buf)
425 Mem_FreePool(&buf->mempool);
431 void SZ_Clear (sizebuf_t *buf)
436 void *SZ_GetSpace (sizebuf_t *buf, int length)
440 if (buf->cursize + length > buf->maxsize)
442 if (!buf->allowoverflow)
443 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
445 if (length > buf->maxsize)
446 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
448 buf->overflowed = true;
449 Con_Printf ("SZ_GetSpace: overflow");
453 data = buf->data + buf->cursize;
454 buf->cursize += length;
459 void SZ_Write (sizebuf_t *buf, void *data, int length)
461 memcpy (SZ_GetSpace(buf,length),data,length);
464 void SZ_Print (sizebuf_t *buf, char *data)
468 len = strlen(data)+1;
470 // byte * cast to keep VC++ happy
471 if (buf->data[buf->cursize-1])
472 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
474 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
478 //============================================================================
486 char *COM_SkipPath (char *pathname)
505 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
506 void COM_StripExtension (char *in, char *out)
513 else if (*in == '/' || *in == '\\' || *in == ':')
526 char *COM_FileExtension (char *in)
528 static char exten[8];
531 while (*in && *in != '.')
536 for (i=0 ; i<7 && *in ; i++,in++)
547 void COM_FileBase (char *in, char *out)
566 strcpy (out,"?model?");
581 void COM_DefaultExtension (char *path, char *extension)
585 // if path doesn't have a .EXT, append extension
586 // (extension should include the .)
588 src = path + strlen(path) - 1;
590 while (*src != '/' && src != path)
593 return; // it has an extension
597 strcat (path, extension);
605 Parse a token out of a string
608 char *COM_Parse (char *data)
621 while ( (c = *data) <= ' ')
624 return NULL; // end of file;
629 if (c=='/' && data[1] == '/')
631 while (*data && *data != '\n')
637 // handle quoted strings specially
654 // parse single characters
655 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
663 // parse a regular word
670 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
683 Returns the position (1 to argc-1) in the program's argument list
684 where the given parameter apears, or 0 if not present
687 int COM_CheckParm (char *parm)
691 for (i=1 ; i<com_argc ; i++)
694 continue; // NEXTSTEP sometimes clears appkit vars.
695 if (!strcmp (parm,com_argv[i]))
706 Looks for the pop.txt file and verifies it.
707 Sets the "registered" cvar.
708 Immediately exits out if an alternate game was attempted to be started without
712 void COM_CheckRegistered (void)
714 Cvar_Set ("cmdline", com_cmdline);
716 if (!Sys_FileTime("gfx/pop.lmp"))
719 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
721 Con_Printf ("Playing shareware version.\n");
725 Cvar_Set ("registered", "1");
726 Con_Printf ("Playing registered version.\n");
730 void COM_Path_f (void);
738 void COM_InitArgv (int argc, char **argv)
743 // reconstitute the command line for the cmdline externally visible cvar
746 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
750 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
752 com_cmdline[n++] = argv[j][i++];
755 if (n < (CMDLINE_LENGTH - 1))
756 com_cmdline[n++] = ' ';
765 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
768 largv[com_argc] = argv[com_argc];
769 if (!strcmp ("-safe", argv[com_argc]))
775 // force all the safe-mode switches. Note that we reserved extra space in
776 // case we need to add these, so we don't need an overflow check
777 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
779 largv[com_argc] = safeargvs[i];
784 largv[com_argc] = argvdummy;
788 gamemode = GAME_BLOODBATH;
790 gamemode = GAME_ZYMOTIC;
792 gamemode = GAME_FIENDARENA;
794 gamemode = GAME_NEHAHRA;
796 if (COM_CheckParm ("-bloodbath"))
797 gamemode = GAME_BLOODBATH;
798 else if (COM_CheckParm ("-zymotic"))
799 gamemode = GAME_ZYMOTIC;
800 else if (COM_CheckParm ("-fiendarena"))
801 gamemode = GAME_FIENDARENA;
802 else if (COM_CheckParm ("-nehahra"))
803 gamemode = GAME_NEHAHRA;
804 else if (COM_CheckParm ("-hipnotic"))
805 gamemode = GAME_HIPNOTIC;
806 else if (COM_CheckParm ("-rogue"))
807 gamemode = GAME_ROGUE;
812 gamename = "DarkPlaces";
815 gamename = "Darkplaces-Hipnotic";
818 gamename = "Darkplaces-Rogue";
821 gamename = "DarkPlaces-Nehahra";
823 case GAME_FIENDARENA:
824 gamename = "FiendArena";
827 gamename = "Zymotic";
830 gamename = "BloodBath";
833 Sys_Error("COM_InitArgv: unknown gamemode %i\n", gamemode);
839 extern void Mathlib_Init(void);
848 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
849 qbyte swaptest[2] = {1,0};
851 // set the byte swapping variables in a portable manner
852 if ( *(short *)swaptest == 1)
854 BigShort = ShortSwap;
855 LittleShort = ShortNoSwap;
857 LittleLong = LongNoSwap;
858 BigFloat = FloatSwap;
859 LittleFloat = FloatNoSwap;
863 BigShort = ShortNoSwap;
864 LittleShort = ShortSwap;
865 BigLong = LongNoSwap;
866 LittleLong = LongSwap;
867 BigFloat = FloatNoSwap;
868 LittleFloat = FloatSwap;
872 pak_mempool = Mem_AllocPool("paks");
874 Cvar_RegisterVariable (®istered);
875 Cvar_RegisterVariable (&cmdline);
876 Cmd_AddCommand ("path", COM_Path_f);
880 COM_InitFilesystem ();
881 COM_CheckRegistered ();
889 does a varargs printf into a temp buffer, so I don't need to have
890 varargs versions of all text functions.
891 FIXME: make this buffer size safe someday
894 char *va(char *format, ...)
897 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
898 static char string[8][1024], *s;
899 static int stringindex = 0;
901 s = string[stringindex];
902 stringindex = (stringindex + 1) & 7;
903 va_start (argptr, format);
904 vsprintf (s, format,argptr);
912 =============================================================================
916 =============================================================================
928 char name[MAX_QPATH];
929 int filepos, filelen;
932 typedef struct pack_s
934 char filename[MAX_OSPATH];
948 int filepos, filelen;
958 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
959 #define MAX_FILES_IN_PACK 65536
961 pack_t *packlist = NULL;
964 char com_cachedir[MAX_OSPATH];
966 char com_gamedir[MAX_OSPATH];
968 typedef struct searchpath_s
970 char filename[MAX_OSPATH];
971 pack_t *pack; // only one of filename / pack will be used
972 struct searchpath_s *next;
975 searchpath_t *com_searchpaths;
983 void COM_Path_f (void)
987 Con_Printf ("Current search path:\n");
988 for (s=com_searchpaths ; s ; s=s->next)
992 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
995 Con_Printf ("%s\n", s->filename);
1003 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1006 void COM_CreatePath (char *path)
1010 for (ofs = path+1 ; *ofs ; ofs++)
1012 if (*ofs == '/' || *ofs == '\\')
1014 // create the directory
1028 The filename will be prefixed by the current game directory
1031 qboolean COM_WriteFile (char *filename, void *data, int len)
1034 char name[MAX_OSPATH];
1036 sprintf (name, "%s/%s", com_gamedir, filename);
1038 // LordHavoc: added this
1039 COM_CreatePath (name); // create directories up to the file
1041 handle = Sys_FileOpenWrite (name);
1044 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1048 Con_DPrintf ("COM_WriteFile: %s\n", name);
1049 Sys_FileWrite (handle, data, len);
1050 Sys_FileClose (handle);
1059 Copies a file over from the net to the local cache, creating any directories
1060 needed. This is for the convenience of developers using ISDN from home.
1063 void COM_CopyFile (char *netpath, char *cachepath)
1066 int remaining, count;
1069 remaining = Sys_FileOpenRead (netpath, &in);
1070 COM_CreatePath (cachepath); // create directories up to the cache file
1071 out = Sys_FileOpenWrite (cachepath);
1075 if (remaining < sizeof(buf))
1078 count = sizeof(buf);
1079 Sys_FileRead (in, buf, count);
1080 Sys_FileWrite (out, buf, count);
1085 Sys_FileClose (out);
1093 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1095 int fd = open (path, O_RDONLY);
1096 unsigned char id[2];
1097 unsigned char len_bytes[4];
1101 Sys_Error ("Couldn't open %s", path);
1104 if (offs < 0 || len < 0)
1108 len = lseek (fd, 0, SEEK_END);
1109 lseek (fd, 0, SEEK_SET);
1111 lseek (fd, offs, SEEK_SET);
1115 if (id[0] == 0x1f && id[1] == 0x8b)
1117 lseek (fd, offs + len - 4, SEEK_SET);
1118 read (fd, len_bytes, 4);
1119 len = ((len_bytes[3] << 24)
1120 | (len_bytes[2] << 16)
1121 | (len_bytes[1] << 8)
1125 lseek (fd, offs, SEEK_SET);
1129 setmode (fd, O_BINARY);
1132 return Qdopen (fd, "rbz");
1134 return Qdopen (fd, "rb");
1141 Finds the file in the search path.
1142 Sets com_filesize and one of handle or file
1145 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1147 searchpath_t *search;
1148 char netpath[MAX_OSPATH];
1150 char cachepath[MAX_OSPATH];
1156 char gzfilename[MAX_OSPATH];
1159 filenamelen = strlen (filename);
1160 sprintf (gzfilename, "%s.gz", filename);
1163 Sys_Error ("COM_FindFile: file not set");
1166 // search through the path, one element at a time
1168 search = com_searchpaths;
1170 for ( ; search ; search = search->next)
1172 // is the element a pak file?
1175 // look through all the pak file elements
1177 for (i=0 ; i<pak->numfiles ; i++)
1178 if (!strcmp (pak->files[i].name, filename)
1179 || !strcmp (pak->files[i].name, gzfilename))
1182 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1183 // open a new file on the pakfile
1184 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1185 return com_filesize;
1190 sprintf (netpath, "%s/%s",search->filename, filename);
1192 findtime = Sys_FileTime (netpath);
1197 // see if the file needs to be updated in the cache
1198 if (com_cachedir[0])
1201 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1202 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1204 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1206 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1209 cachetime = Sys_FileTime (cachepath);
1211 if (cachetime < findtime)
1212 COM_CopyFile (netpath, cachepath);
1213 strcpy (netpath, cachepath);
1218 Sys_Printf ("FindFile: %s\n",netpath);
1219 *file = COM_OpenRead (netpath, -1, -1, zip);
1220 return com_filesize;
1226 Sys_Printf ("FindFile: can't find %s\n", filename);
1238 If the requested file is inside a packfile, a new QFile * will be opened
1242 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1244 return COM_FindFile (filename, file, quiet, zip);
1252 Filename are reletive to the quake directory.
1253 Always appends a 0 byte.
1258 qbyte *COM_LoadFile (char *path, qboolean quiet)
1265 buf = NULL; // quiet compiler warning
1268 // look for it in the filesystem or pack files
1269 len = COM_FOpenFile (path, &h, quiet, true);
1275 // extract the filename base name for hunk tag
1276 COM_FileBase (path, base);
1278 buf = Mem_Alloc(tempmempool, len+1);
1280 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1282 ((qbyte *)buf)[len] = 0;
1284 Qread (h, buf, len);
1294 Takes an explicit (not game tree related) path to a pak file.
1296 Loads the header and directory, adding the files at the beginning
1297 of the list so they override previous pack files.
1300 pack_t *COM_LoadPackFile (char *packfile)
1302 dpackheader_t header;
1307 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1310 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1313 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1314 if (memcmp(header.id, "PACK", 4))
1315 Sys_Error ("%s is not a packfile", packfile);
1316 header.dirofs = LittleLong (header.dirofs);
1317 header.dirlen = LittleLong (header.dirlen);
1319 if (header.dirlen % sizeof(dpackfile_t))
1320 Sys_Error ("%s has an invalid directory size", packfile);
1322 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1324 if (numpackfiles > MAX_FILES_IN_PACK)
1325 Sys_Error ("%s has %i files", packfile, numpackfiles);
1327 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1328 strcpy (pack->filename, packfile);
1329 pack->handle = packhandle;
1330 pack->numfiles = numpackfiles;
1331 pack->mempool = Mem_AllocPool(packfile);
1332 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1333 pack->next = packlist;
1336 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1337 Sys_FileSeek (packhandle, header.dirofs);
1338 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1340 // parse the directory
1341 for (i = 0;i < numpackfiles;i++)
1343 strcpy (pack->files[i].name, info[i].name);
1344 pack->files[i].filepos = LittleLong(info[i].filepos);
1345 pack->files[i].filelen = LittleLong(info[i].filelen);
1350 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1357 COM_AddGameDirectory
1359 Sets com_gamedir, adds the directory to the head of the path,
1360 then loads and adds pak1.pak pak2.pak ...
1363 void COM_AddGameDirectory (char *dir)
1365 stringlist_t *list, *current;
1366 searchpath_t *search;
1368 char pakfile[MAX_OSPATH];
1370 strcpy (com_gamedir, dir);
1373 // add the directory to the search path
1375 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1376 strcpy (search->filename, dir);
1377 search->next = com_searchpaths;
1378 com_searchpaths = search;
1380 // add any paks in the directory
1381 list = listdirectory(dir);
1382 for (current = list;current;current = current->next)
1384 if (matchpattern(current->text, "*.pak"))
1386 sprintf (pakfile, "%s/%s", dir, current->text);
1387 pak = COM_LoadPackFile (pakfile);
1390 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1392 search->next = com_searchpaths;
1393 com_searchpaths = search;
1396 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1399 freedirectory(list);
1407 void COM_InitFilesystem (void)
1410 char basedir[MAX_OSPATH];
1411 searchpath_t *search;
1415 // Overrides the system supplied base directory (under GAMENAME)
1417 i = COM_CheckParm ("-basedir");
1418 if (i && i < com_argc-1)
1419 strcpy (basedir, com_argv[i+1]);
1421 strcpy (basedir, host_parms.basedir);
1423 j = strlen (basedir);
1427 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1434 // Overrides the system supplied cache directory (NULL or /qcache)
1435 // -cachedir - will disable caching.
1437 i = COM_CheckParm ("-cachedir");
1438 if (i && i < com_argc-1)
1440 if (com_argv[i+1][0] == '-')
1441 com_cachedir[0] = 0;
1443 strcpy (com_cachedir, com_argv[i+1]);
1445 else if (host_parms.cachedir)
1446 strcpy (com_cachedir, host_parms.cachedir);
1448 com_cachedir[0] = 0;
1451 // start up with GAMENAME by default (id1)
1452 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1459 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1462 COM_AddGameDirectory (va("%s/rogue", basedir) );
1465 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1467 case GAME_FIENDARENA:
1468 COM_AddGameDirectory (va("%s/fiendarena", basedir) );
1471 COM_AddGameDirectory (va("%s/zymotic", basedir) );
1473 case GAME_BLOODBATH:
1474 COM_AddGameDirectory (va("%s/bb", basedir) );
1477 Sys_Error("COM_InitFilesystem: unknown gamemode %i\n", gamemode);
1483 // Adds basedir/gamedir as an override game
1485 i = COM_CheckParm ("-game");
1486 if (i && i < com_argc-1)
1488 com_modified = true;
1489 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1493 // -path <dir or packfile> [<dir or packfile>] ...
1494 // Fully specifies the exact search path, overriding the generated one
1496 i = COM_CheckParm ("-path");
1499 com_modified = true;
1500 com_searchpaths = NULL;
1501 while (++i < com_argc)
1503 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1506 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1507 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1509 search->pack = COM_LoadPackFile (com_argv[i]);
1511 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1514 strcpy (search->filename, com_argv[i]);
1515 search->next = com_searchpaths;
1516 com_searchpaths = search;
1521 int COM_FileExists(char *filename)
1523 searchpath_t *search;
1524 char netpath[MAX_OSPATH];
1529 for (search = com_searchpaths;search;search = search->next)
1534 for (i = 0;i < pak->numfiles;i++)
1535 if (!strcmp (pak->files[i].name, filename))
1540 sprintf (netpath, "%s/%s",search->filename, filename);
1541 findtime = Sys_FileTime (netpath);
1551 //======================================
1552 // LordHavoc: added these because they are useful
1554 void COM_ToLowerString(char *in, char *out)
1558 if (*in >= 'A' && *in <= 'Z')
1559 *out++ = *in++ + 'a' - 'A';
1565 void COM_ToUpperString(char *in, char *out)
1569 if (*in >= 'a' && *in <= 'z')
1570 *out++ = *in++ + 'A' - 'a';