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);
47 // LordHavoc: made commandline 1024 characters instead of 256
48 #define CMDLINE_LENGTH 1024
49 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 (char *s1, 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 (char *s1, 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, 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, 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");
437 if (length > buf->maxsize)
438 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
440 buf->overflowed = true;
441 Con_Printf ("SZ_GetSpace: overflow");
445 data = buf->data + buf->cursize;
446 buf->cursize += length;
451 void SZ_Write (sizebuf_t *buf, void *data, int length)
453 memcpy (SZ_GetSpace(buf,length),data,length);
456 void SZ_Print (sizebuf_t *buf, 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
470 //============================================================================
478 char *COM_SkipPath (char *pathname)
497 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
498 void COM_StripExtension (char *in, char *out)
505 else if (*in == '/' || *in == '\\' || *in == ':')
518 char *COM_FileExtension (char *in)
520 static char exten[8];
523 while (*in && *in != '.')
528 for (i=0 ; i<7 && *in ; i++,in++)
539 void COM_FileBase (char *in, char *out)
558 strcpy (out,"?model?");
573 void COM_DefaultExtension (char *path, char *extension)
577 // if path doesn't have a .EXT, append extension
578 // (extension should include the .)
580 src = path + strlen(path) - 1;
582 while (*src != '/' && src != path)
585 return; // it has an extension
589 strcat (path, extension);
597 Parse a token out of a string
600 char *COM_Parse (char *data)
613 while ( (c = *data) <= ' ')
616 return NULL; // end of file;
621 if (c=='/' && data[1] == '/')
623 while (*data && *data != '\n')
629 // handle quoted strings specially
646 // parse single characters
647 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
655 // parse a regular word
662 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
675 Returns the position (1 to argc-1) in the program's argument list
676 where the given parameter apears, or 0 if not present
679 int COM_CheckParm (char *parm)
683 for (i=1 ; i<com_argc ; i++)
686 continue; // NEXTSTEP sometimes clears appkit vars.
687 if (!strcmp (parm,com_argv[i]))
698 Looks for the pop.txt file and verifies it.
699 Sets the "registered" cvar.
700 Immediately exits out if an alternate game was attempted to be started without
704 void COM_CheckRegistered (void)
706 Cvar_Set ("cmdline", com_cmdline);
708 if (!Sys_FileTime("gfx/pop.lmp"))
711 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
713 Con_Printf ("Playing shareware version.\n");
717 Cvar_Set ("registered", "1");
718 Con_Printf ("Playing registered version.\n");
722 void COM_Path_f (void);
730 void COM_InitArgv (void)
733 // reconstitute the command line for the cmdline externally visible cvar
735 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
738 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
739 com_cmdline[n++] = com_argv[j][i++];
740 if (n < (CMDLINE_LENGTH - 1))
741 com_cmdline[n++] = ' ';
748 void COM_InitGameType (void)
751 COM_StripExtension(com_argv[0], name);
752 COM_ToLowerString(name, name);
754 if (strstr(name, "transfusion"))
755 gamemode = GAME_TRANSFUSION;
756 else if (strstr(name, "zymotic"))
757 gamemode = GAME_ZYMOTIC;
758 else if (strstr(name, "fiendarena"))
759 gamemode = GAME_FIENDARENA;
760 else if (strstr(name, "nehahra"))
761 gamemode = GAME_NEHAHRA;
762 else if (strstr(name, "hipnotic"))
763 gamemode = GAME_HIPNOTIC;
764 else if (strstr(name, "rogue"))
765 gamemode = GAME_ROGUE;
767 gamemode = GAME_NORMAL;
769 if (COM_CheckParm ("-transfusion"))
770 gamemode = GAME_TRANSFUSION;
771 else if (COM_CheckParm ("-zymotic"))
772 gamemode = GAME_ZYMOTIC;
773 else if (COM_CheckParm ("-fiendarena"))
774 gamemode = GAME_FIENDARENA;
775 else if (COM_CheckParm ("-nehahra"))
776 gamemode = GAME_NEHAHRA;
777 else if (COM_CheckParm ("-hipnotic"))
778 gamemode = GAME_HIPNOTIC;
779 else if (COM_CheckParm ("-rogue"))
780 gamemode = GAME_ROGUE;
781 else if (COM_CheckParm ("-quake"))
782 gamemode = GAME_NORMAL;
787 if (registered.integer)
788 gamename = "DarkPlaces-Quake";
790 gamename = "DarkPlaces-SharewareQuake";
793 gamename = "Darkplaces-Hipnotic";
796 gamename = "Darkplaces-Rogue";
799 gamename = "DarkPlaces-Nehahra";
801 case GAME_FIENDARENA:
802 gamename = "FiendArena";
805 gamename = "Zymotic";
807 case GAME_TRANSFUSION:
808 gamename = "Transfusion";
811 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
817 extern void Mathlib_Init(void);
826 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
827 qbyte swaptest[2] = {1,0};
829 // set the byte swapping variables in a portable manner
830 if ( *(short *)swaptest == 1)
832 BigShort = ShortSwap;
833 LittleShort = ShortNoSwap;
835 LittleLong = LongNoSwap;
836 BigFloat = FloatSwap;
837 LittleFloat = FloatNoSwap;
841 BigShort = ShortNoSwap;
842 LittleShort = ShortSwap;
843 BigLong = LongNoSwap;
844 LittleLong = LongSwap;
845 BigFloat = FloatNoSwap;
846 LittleFloat = FloatSwap;
850 pak_mempool = Mem_AllocPool("paks");
852 Cvar_RegisterVariable (®istered);
853 Cvar_RegisterVariable (&cmdline);
854 Cmd_AddCommand ("path", COM_Path_f);
858 COM_InitFilesystem ();
859 COM_CheckRegistered ();
867 does a varargs printf into a temp buffer, so I don't need to have
868 varargs versions of all text functions.
869 FIXME: make this buffer size safe someday
872 char *va(char *format, ...)
875 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
876 static char string[8][1024], *s;
877 static int stringindex = 0;
879 s = string[stringindex];
880 stringindex = (stringindex + 1) & 7;
881 va_start (argptr, format);
882 vsprintf (s, format,argptr);
890 =============================================================================
894 =============================================================================
906 char name[MAX_QPATH];
907 int filepos, filelen;
910 typedef struct pack_s
912 char filename[MAX_OSPATH];
926 int filepos, filelen;
936 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
937 #define MAX_FILES_IN_PACK 65536
939 pack_t *packlist = NULL;
942 char com_cachedir[MAX_OSPATH];
944 char com_gamedir[MAX_OSPATH];
946 typedef struct searchpath_s
948 char filename[MAX_OSPATH];
949 pack_t *pack; // only one of filename / pack will be used
950 struct searchpath_s *next;
953 searchpath_t *com_searchpaths;
961 void COM_Path_f (void)
965 Con_Printf ("Current search path:\n");
966 for (s=com_searchpaths ; s ; s=s->next)
970 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
973 Con_Printf ("%s\n", s->filename);
981 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
984 void COM_CreatePath (char *path)
988 for (ofs = path+1 ; *ofs ; ofs++)
990 if (*ofs == '/' || *ofs == '\\')
992 // create the directory
1006 The filename will be prefixed by the current game directory
1009 qboolean COM_WriteFile (char *filename, void *data, int len)
1012 char name[MAX_OSPATH];
1014 sprintf (name, "%s/%s", com_gamedir, filename);
1016 // LordHavoc: added this
1017 COM_CreatePath (name); // create directories up to the file
1019 handle = Sys_FileOpenWrite (name);
1022 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1026 Con_DPrintf ("COM_WriteFile: %s\n", name);
1027 Sys_FileWrite (handle, data, len);
1028 Sys_FileClose (handle);
1037 Copies a file over from the net to the local cache, creating any directories
1038 needed. This is for the convenience of developers using ISDN from home.
1041 void COM_CopyFile (char *netpath, char *cachepath)
1044 int remaining, count;
1047 remaining = Sys_FileOpenRead (netpath, &in);
1048 COM_CreatePath (cachepath); // create directories up to the cache file
1049 out = Sys_FileOpenWrite (cachepath);
1053 if (remaining < sizeof(buf))
1056 count = sizeof(buf);
1057 Sys_FileRead (in, buf, count);
1058 Sys_FileWrite (out, buf, count);
1063 Sys_FileClose (out);
1071 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1073 int fd = open (path, O_RDONLY);
1074 unsigned char id[2];
1075 unsigned char len_bytes[4];
1079 Sys_Error ("Couldn't open %s", path);
1082 if (offs < 0 || len < 0)
1086 len = lseek (fd, 0, SEEK_END);
1087 lseek (fd, 0, SEEK_SET);
1089 lseek (fd, offs, SEEK_SET);
1093 if (id[0] == 0x1f && id[1] == 0x8b)
1095 lseek (fd, offs + len - 4, SEEK_SET);
1096 read (fd, len_bytes, 4);
1097 len = ((len_bytes[3] << 24)
1098 | (len_bytes[2] << 16)
1099 | (len_bytes[1] << 8)
1103 lseek (fd, offs, SEEK_SET);
1107 setmode (fd, O_BINARY);
1110 return Qdopen (fd, "rbz");
1112 return Qdopen (fd, "rb");
1119 Finds the file in the search path.
1120 Sets com_filesize and one of handle or file
1123 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1125 searchpath_t *search;
1126 char netpath[MAX_OSPATH];
1128 char cachepath[MAX_OSPATH];
1134 char gzfilename[MAX_OSPATH];
1137 filenamelen = strlen (filename);
1138 sprintf (gzfilename, "%s.gz", filename);
1141 Sys_Error ("COM_FindFile: file not set");
1144 // search through the path, one element at a time
1146 search = com_searchpaths;
1148 for ( ; search ; search = search->next)
1150 // is the element a pak file?
1153 // look through all the pak file elements
1155 for (i=0 ; i<pak->numfiles ; i++)
1156 if (!strcmp (pak->files[i].name, filename)
1157 || !strcmp (pak->files[i].name, gzfilename))
1160 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1161 // open a new file on the pakfile
1162 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1163 return com_filesize;
1168 sprintf (netpath, "%s/%s",search->filename, filename);
1170 findtime = Sys_FileTime (netpath);
1175 // see if the file needs to be updated in the cache
1176 if (com_cachedir[0])
1179 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1180 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1182 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1184 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1187 cachetime = Sys_FileTime (cachepath);
1189 if (cachetime < findtime)
1190 COM_CopyFile (netpath, cachepath);
1191 strcpy (netpath, cachepath);
1196 Sys_Printf ("FindFile: %s\n",netpath);
1197 *file = COM_OpenRead (netpath, -1, -1, zip);
1198 return com_filesize;
1204 Sys_Printf ("FindFile: can't find %s\n", filename);
1216 If the requested file is inside a packfile, a new QFile * will be opened
1220 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1222 return COM_FindFile (filename, file, quiet, zip);
1230 Filename are reletive to the quake directory.
1231 Always appends a 0 byte.
1236 qbyte *COM_LoadFile (char *path, qboolean quiet)
1243 buf = NULL; // quiet compiler warning
1246 // look for it in the filesystem or pack files
1247 len = COM_FOpenFile (path, &h, quiet, true);
1253 // extract the filename base name for hunk tag
1254 COM_FileBase (path, base);
1256 buf = Mem_Alloc(tempmempool, len+1);
1258 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1260 ((qbyte *)buf)[len] = 0;
1262 Qread (h, buf, len);
1272 Takes an explicit (not game tree related) path to a pak file.
1274 Loads the header and directory, adding the files at the beginning
1275 of the list so they override previous pack files.
1278 pack_t *COM_LoadPackFile (char *packfile)
1280 dpackheader_t header;
1285 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1288 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1291 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1292 if (memcmp(header.id, "PACK", 4))
1293 Sys_Error ("%s is not a packfile", packfile);
1294 header.dirofs = LittleLong (header.dirofs);
1295 header.dirlen = LittleLong (header.dirlen);
1297 if (header.dirlen % sizeof(dpackfile_t))
1298 Sys_Error ("%s has an invalid directory size", packfile);
1300 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1302 if (numpackfiles > MAX_FILES_IN_PACK)
1303 Sys_Error ("%s has %i files", packfile, numpackfiles);
1305 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1306 strcpy (pack->filename, packfile);
1307 pack->handle = packhandle;
1308 pack->numfiles = numpackfiles;
1309 pack->mempool = Mem_AllocPool(packfile);
1310 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1311 pack->next = packlist;
1314 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1315 Sys_FileSeek (packhandle, header.dirofs);
1316 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1318 // parse the directory
1319 for (i = 0;i < numpackfiles;i++)
1321 strcpy (pack->files[i].name, info[i].name);
1322 pack->files[i].filepos = LittleLong(info[i].filepos);
1323 pack->files[i].filelen = LittleLong(info[i].filelen);
1328 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1335 COM_AddGameDirectory
1337 Sets com_gamedir, adds the directory to the head of the path,
1338 then loads and adds pak1.pak pak2.pak ...
1341 void COM_AddGameDirectory (char *dir)
1343 stringlist_t *list, *current;
1344 searchpath_t *search;
1346 char pakfile[MAX_OSPATH];
1348 strcpy (com_gamedir, dir);
1351 // add the directory to the search path
1353 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1354 strcpy (search->filename, dir);
1355 search->next = com_searchpaths;
1356 com_searchpaths = search;
1358 // add any paks in the directory
1359 list = listdirectory(dir);
1360 for (current = list;current;current = current->next)
1362 if (matchpattern(current->text, "*.pak"))
1364 sprintf (pakfile, "%s/%s", dir, current->text);
1365 pak = COM_LoadPackFile (pakfile);
1368 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1370 search->next = com_searchpaths;
1371 com_searchpaths = search;
1374 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1377 freedirectory(list);
1385 void COM_InitFilesystem (void)
1388 char basedir[MAX_OSPATH];
1389 searchpath_t *search;
1393 // Overrides the system supplied base directory (under GAMENAME)
1395 i = COM_CheckParm ("-basedir");
1396 if (i && i < com_argc-1)
1397 strcpy (basedir, com_argv[i+1]);
1399 strcpy (basedir, host_parms.basedir);
1401 j = strlen (basedir);
1405 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1412 // Overrides the system supplied cache directory (NULL or /qcache)
1413 // -cachedir - will disable caching.
1415 i = COM_CheckParm ("-cachedir");
1416 if (i && i < com_argc-1)
1418 if (com_argv[i+1][0] == '-')
1419 com_cachedir[0] = 0;
1421 strcpy (com_cachedir, com_argv[i+1]);
1423 else if (host_parms.cachedir)
1424 strcpy (com_cachedir, host_parms.cachedir);
1426 com_cachedir[0] = 0;
1429 // start up with GAMENAME by default (id1)
1430 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1437 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1440 COM_AddGameDirectory (va("%s/rogue", basedir) );
1443 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1445 case GAME_FIENDARENA:
1446 COM_AddGameDirectory (va("%s/fiendarena", basedir) );
1449 COM_AddGameDirectory (va("%s/zymotic", basedir) );
1451 case GAME_TRANSFUSION:
1452 COM_AddGameDirectory (va("%s/transfusion", basedir) );
1455 Sys_Error("COM_InitFilesystem: unknown gamemode %i\n", gamemode);
1461 // Adds basedir/gamedir as an override game
1463 i = COM_CheckParm ("-game");
1464 if (i && i < com_argc-1)
1466 com_modified = true;
1467 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1471 // -path <dir or packfile> [<dir or packfile>] ...
1472 // Fully specifies the exact search path, overriding the generated one
1474 i = COM_CheckParm ("-path");
1477 com_modified = true;
1478 com_searchpaths = NULL;
1479 while (++i < com_argc)
1481 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1484 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1485 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1487 search->pack = COM_LoadPackFile (com_argv[i]);
1489 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1492 strcpy (search->filename, com_argv[i]);
1493 search->next = com_searchpaths;
1494 com_searchpaths = search;
1499 int COM_FileExists(char *filename)
1501 searchpath_t *search;
1502 char netpath[MAX_OSPATH];
1507 for (search = com_searchpaths;search;search = search->next)
1512 for (i = 0;i < pak->numfiles;i++)
1513 if (!strcmp (pak->files[i].name, filename))
1518 sprintf (netpath, "%s/%s",search->filename, filename);
1519 findtime = Sys_FileTime (netpath);
1529 //======================================
1530 // LordHavoc: added these because they are useful
1532 void COM_ToLowerString(char *in, char *out)
1536 if (*in >= 'A' && *in <= 'Z')
1537 *out++ = *in++ + 'a' - 'A';
1543 void COM_ToUpperString(char *in, char *out)
1547 if (*in >= 'a' && *in <= 'z')
1548 *out++ = *in++ + 'A' - 'a';
1554 int COM_StringBeginsWith(const char *s, const char *match)
1556 for (;*s && *match;s++, match++)