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");
439 if (length > buf->maxsize)
440 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
442 buf->overflowed = true;
443 Con_Printf ("SZ_GetSpace: overflow");
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
472 //============================================================================
480 char *COM_SkipPath (char *pathname)
499 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
500 void COM_StripExtension (char *in, char *out)
507 else if (*in == '/' || *in == '\\' || *in == ':')
522 char *COM_FileExtension (char *in)
524 static char exten[8];
527 while (*in && *in != '.')
532 for (i=0 ; i<7 && *in ; i++,in++)
543 void COM_FileBase (char *in, char *out)
562 strcpy (out,"?model?");
577 void COM_DefaultExtension (char *path, char *extension)
581 // if path doesn't have a .EXT, append extension
582 // (extension should include the .)
584 src = path + strlen(path) - 1;
586 while (*src != '/' && src != path)
589 return; // it has an extension
593 strcat (path, extension);
601 Parse a token out of a string
604 char *COM_Parse (char *data)
617 while ( (c = *data) <= ' ')
620 return NULL; // end of file;
625 if (c=='/' && data[1] == '/')
627 while (*data && *data != '\n')
633 // handle quoted strings specially
650 // parse single characters
651 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
659 // parse a regular word
666 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
679 Returns the position (1 to argc-1) in the program's argument list
680 where the given parameter apears, or 0 if not present
683 int COM_CheckParm (char *parm)
687 for (i=1 ; i<com_argc ; i++)
690 continue; // NEXTSTEP sometimes clears appkit vars.
691 if (!strcmp (parm,com_argv[i]))
702 Looks for the pop.txt file and verifies it.
703 Sets the "registered" cvar.
704 Immediately exits out if an alternate game was attempted to be started without
708 void COM_CheckRegistered (void)
710 Cvar_Set ("cmdline", com_cmdline);
712 if (!Sys_FileTime("gfx/pop.lmp"))
715 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
717 Con_Printf ("Playing shareware version.\n");
721 Cvar_Set ("registered", "1");
722 Con_Printf ("Playing registered version.\n");
726 void COM_Path_f (void);
734 void COM_InitArgv (void)
737 // reconstitute the command line for the cmdline externally visible cvar
739 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
742 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
743 com_cmdline[n++] = com_argv[j][i++];
744 if (n < (CMDLINE_LENGTH - 1))
745 com_cmdline[n++] = ' ';
752 void COM_InitGameType (void)
754 char name[MAX_OSPATH];
755 COM_StripExtension(com_argv[0], name);
756 COM_ToLowerString(name, name);
758 if (strstr(name, "transfusion"))
759 gamemode = GAME_TRANSFUSION;
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 ("-nehahra"))
772 gamemode = GAME_NEHAHRA;
773 else if (COM_CheckParm ("-hipnotic"))
774 gamemode = GAME_HIPNOTIC;
775 else if (COM_CheckParm ("-rogue"))
776 gamemode = GAME_ROGUE;
777 else if (COM_CheckParm ("-quake"))
778 gamemode = GAME_NORMAL;
783 gamename = "DarkPlaces-Quake";
787 gamename = "Darkplaces-Hipnotic";
788 gamedirname = "hipnotic";
791 gamename = "Darkplaces-Rogue";
792 gamedirname = "rogue";
795 gamename = "DarkPlaces-Nehahra";
796 gamedirname = "nehahra";
798 case GAME_TRANSFUSION:
799 gamename = "Transfusion";
800 gamedirname = "transfusion";
803 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
809 extern void Mathlib_Init(void);
818 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
819 qbyte swaptest[2] = {1,0};
821 // set the byte swapping variables in a portable manner
822 if ( *(short *)swaptest == 1)
824 BigShort = ShortSwap;
825 LittleShort = ShortNoSwap;
827 LittleLong = LongNoSwap;
828 BigFloat = FloatSwap;
829 LittleFloat = FloatNoSwap;
833 BigShort = ShortNoSwap;
834 LittleShort = ShortSwap;
835 BigLong = LongNoSwap;
836 LittleLong = LongSwap;
837 BigFloat = FloatNoSwap;
838 LittleFloat = FloatSwap;
842 pak_mempool = Mem_AllocPool("paks");
844 Cvar_RegisterVariable (®istered);
845 Cvar_RegisterVariable (&cmdline);
846 Cmd_AddCommand ("path", COM_Path_f);
850 COM_InitFilesystem ();
851 COM_CheckRegistered ();
859 does a varargs printf into a temp buffer, so I don't need to have
860 varargs versions of all text functions.
861 FIXME: make this buffer size safe someday
864 char *va(char *format, ...)
867 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
868 static char string[8][1024], *s;
869 static int stringindex = 0;
871 s = string[stringindex];
872 stringindex = (stringindex + 1) & 7;
873 va_start (argptr, format);
874 vsprintf (s, format,argptr);
882 =============================================================================
886 =============================================================================
898 char name[MAX_QPATH];
899 int filepos, filelen;
902 typedef struct pack_s
904 char filename[MAX_OSPATH];
918 int filepos, filelen;
928 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
929 #define MAX_FILES_IN_PACK 65536
931 pack_t *packlist = NULL;
934 char com_cachedir[MAX_OSPATH];
936 char com_gamedir[MAX_OSPATH];
938 typedef struct searchpath_s
940 char filename[MAX_OSPATH];
941 pack_t *pack; // only one of filename / pack will be used
942 struct searchpath_s *next;
945 searchpath_t *com_searchpaths;
953 void COM_Path_f (void)
957 Con_Printf ("Current search path:\n");
958 for (s=com_searchpaths ; s ; s=s->next)
962 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
965 Con_Printf ("%s\n", s->filename);
973 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
976 void COM_CreatePath (char *path)
980 for (ofs = path+1 ; *ofs ; ofs++)
982 if (*ofs == '/' || *ofs == '\\')
984 // create the directory
998 The filename will be prefixed by the current game directory
1001 qboolean COM_WriteFile (char *filename, void *data, int len)
1004 char name[MAX_OSPATH];
1006 sprintf (name, "%s/%s", com_gamedir, filename);
1008 // LordHavoc: added this
1009 COM_CreatePath (name); // create directories up to the file
1011 handle = Sys_FileOpenWrite (name);
1014 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1018 Con_DPrintf ("COM_WriteFile: %s\n", name);
1019 Sys_FileWrite (handle, data, len);
1020 Sys_FileClose (handle);
1029 Copies a file over from the net to the local cache, creating any directories
1030 needed. This is for the convenience of developers using ISDN from home.
1033 void COM_CopyFile (char *netpath, char *cachepath)
1036 int remaining, count;
1039 remaining = Sys_FileOpenRead (netpath, &in);
1040 COM_CreatePath (cachepath); // create directories up to the cache file
1041 out = Sys_FileOpenWrite (cachepath);
1045 if (remaining < sizeof(buf))
1048 count = sizeof(buf);
1049 Sys_FileRead (in, buf, count);
1050 Sys_FileWrite (out, buf, count);
1055 Sys_FileClose (out);
1063 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1065 int fd = open (path, O_RDONLY);
1066 unsigned char id[2];
1067 unsigned char len_bytes[4];
1071 Sys_Error ("Couldn't open %s", path);
1074 if (offs < 0 || len < 0)
1078 len = lseek (fd, 0, SEEK_END);
1079 lseek (fd, 0, SEEK_SET);
1081 lseek (fd, offs, SEEK_SET);
1085 if (id[0] == 0x1f && id[1] == 0x8b)
1087 lseek (fd, offs + len - 4, SEEK_SET);
1088 read (fd, len_bytes, 4);
1089 len = ((len_bytes[3] << 24)
1090 | (len_bytes[2] << 16)
1091 | (len_bytes[1] << 8)
1095 lseek (fd, offs, SEEK_SET);
1099 setmode (fd, O_BINARY);
1102 return Qdopen (fd, "rbz");
1104 return Qdopen (fd, "rb");
1111 Finds the file in the search path.
1112 Sets com_filesize and one of handle or file
1115 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1117 searchpath_t *search;
1118 char netpath[MAX_OSPATH];
1120 char cachepath[MAX_OSPATH];
1126 char gzfilename[MAX_OSPATH];
1129 filenamelen = strlen (filename);
1130 sprintf (gzfilename, "%s.gz", filename);
1133 Sys_Error ("COM_FindFile: file not set");
1136 // search through the path, one element at a time
1138 search = com_searchpaths;
1140 for ( ; search ; search = search->next)
1142 // is the element a pak file?
1145 // look through all the pak file elements
1147 for (i=0 ; i<pak->numfiles ; i++)
1148 if (!strcmp (pak->files[i].name, filename)
1149 || !strcmp (pak->files[i].name, gzfilename))
1152 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1153 // open a new file on the pakfile
1154 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1155 return com_filesize;
1160 sprintf (netpath, "%s/%s",search->filename, filename);
1162 findtime = Sys_FileTime (netpath);
1167 // see if the file needs to be updated in the cache
1168 if (com_cachedir[0])
1171 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1172 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1174 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1176 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1179 cachetime = Sys_FileTime (cachepath);
1181 if (cachetime < findtime)
1182 COM_CopyFile (netpath, cachepath);
1183 strcpy (netpath, cachepath);
1188 Sys_Printf ("FindFile: %s\n",netpath);
1189 *file = COM_OpenRead (netpath, -1, -1, zip);
1190 return com_filesize;
1196 Sys_Printf ("FindFile: can't find %s\n", filename);
1208 If the requested file is inside a packfile, a new QFile * will be opened
1212 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1214 return COM_FindFile (filename, file, quiet, zip);
1222 Filename are reletive to the quake directory.
1223 Always appends a 0 byte.
1228 qbyte *COM_LoadFile (char *path, qboolean quiet)
1235 buf = NULL; // quiet compiler warning
1238 // look for it in the filesystem or pack files
1239 len = COM_FOpenFile (path, &h, quiet, true);
1245 // extract the filename base name for hunk tag
1246 COM_FileBase (path, base);
1248 buf = Mem_Alloc(tempmempool, len+1);
1250 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1252 ((qbyte *)buf)[len] = 0;
1254 Qread (h, buf, len);
1264 Takes an explicit (not game tree related) path to a pak file.
1266 Loads the header and directory, adding the files at the beginning
1267 of the list so they override previous pack files.
1270 pack_t *COM_LoadPackFile (char *packfile)
1272 dpackheader_t header;
1277 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1280 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1283 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1284 if (memcmp(header.id, "PACK", 4))
1285 Sys_Error ("%s is not a packfile", packfile);
1286 header.dirofs = LittleLong (header.dirofs);
1287 header.dirlen = LittleLong (header.dirlen);
1289 if (header.dirlen % sizeof(dpackfile_t))
1290 Sys_Error ("%s has an invalid directory size", packfile);
1292 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1294 if (numpackfiles > MAX_FILES_IN_PACK)
1295 Sys_Error ("%s has %i files", packfile, numpackfiles);
1297 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1298 strcpy (pack->filename, packfile);
1299 pack->handle = packhandle;
1300 pack->numfiles = numpackfiles;
1301 pack->mempool = Mem_AllocPool(packfile);
1302 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1303 pack->next = packlist;
1306 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1307 Sys_FileSeek (packhandle, header.dirofs);
1308 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1310 // parse the directory
1311 for (i = 0;i < numpackfiles;i++)
1313 strcpy (pack->files[i].name, info[i].name);
1314 pack->files[i].filepos = LittleLong(info[i].filepos);
1315 pack->files[i].filelen = LittleLong(info[i].filelen);
1320 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1327 COM_AddGameDirectory
1329 Sets com_gamedir, adds the directory to the head of the path,
1330 then loads and adds pak1.pak pak2.pak ...
1333 void COM_AddGameDirectory (char *dir)
1335 stringlist_t *list, *current;
1336 searchpath_t *search;
1338 char pakfile[MAX_OSPATH];
1340 strcpy (com_gamedir, dir);
1343 // add the directory to the search path
1345 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1346 strcpy (search->filename, dir);
1347 search->next = com_searchpaths;
1348 com_searchpaths = search;
1350 // add any paks in the directory
1351 list = listdirectory(dir);
1352 for (current = list;current;current = current->next)
1354 if (matchpattern(current->text, "*.pak"))
1356 sprintf (pakfile, "%s/%s", dir, current->text);
1357 pak = COM_LoadPackFile (pakfile);
1360 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1362 search->next = com_searchpaths;
1363 com_searchpaths = search;
1366 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1369 freedirectory(list);
1377 void COM_InitFilesystem (void)
1380 searchpath_t *search;
1382 strcpy(com_basedir, ".");
1385 // Overrides the system supplied base directory (under GAMENAME)
1386 i = COM_CheckParm ("-basedir");
1387 if (i && i < com_argc-1)
1388 strcpy (com_basedir, com_argv[i+1]);
1390 i = strlen (com_basedir);
1391 if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1392 com_basedir[i-1] = 0;
1394 // start up with GAMENAME by default (id1)
1395 COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1398 com_modified = true;
1399 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1403 // Adds basedir/gamedir as an override game
1404 i = COM_CheckParm ("-game");
1405 if (i && i < com_argc-1)
1407 com_modified = true;
1408 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1411 // -path <dir or packfile> [<dir or packfile>] ...
1412 // Fully specifies the exact search path, overriding the generated one
1413 i = COM_CheckParm ("-path");
1416 com_modified = true;
1417 com_searchpaths = NULL;
1418 while (++i < com_argc)
1420 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1423 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1424 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1426 search->pack = COM_LoadPackFile (com_argv[i]);
1428 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1431 strcpy (search->filename, com_argv[i]);
1432 search->next = com_searchpaths;
1433 com_searchpaths = search;
1438 int COM_FileExists(char *filename)
1440 searchpath_t *search;
1441 char netpath[MAX_OSPATH];
1446 for (search = com_searchpaths;search;search = search->next)
1451 for (i = 0;i < pak->numfiles;i++)
1452 if (!strcmp (pak->files[i].name, filename))
1457 sprintf (netpath, "%s/%s",search->filename, filename);
1458 findtime = Sys_FileTime (netpath);
1468 //======================================
1469 // LordHavoc: added these because they are useful
1471 void COM_ToLowerString(char *in, char *out)
1475 if (*in >= 'A' && *in <= 'Z')
1476 *out++ = *in++ + 'a' - 'A';
1482 void COM_ToUpperString(char *in, char *out)
1486 if (*in >= 'a' && *in <= 'z')
1487 *out++ = *in++ + 'A' - 'a';
1493 int COM_StringBeginsWith(const char *s, const char *match)
1495 for (;*s && *match;s++, match++)