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 Com_HexDumpToConsole(const qbyte *data, int size)
474 char *cur, *flushpointer;
477 flushpointer = text + 512;
478 for (i = 0;i < size;)
484 *cur++ = hexchar[(i >> 12) & 15];
485 *cur++ = hexchar[(i >> 8) & 15];
486 *cur++ = hexchar[(i >> 4) & 15];
487 *cur++ = hexchar[(i >> 0) & 15];
489 for (j = 0;j < n;j++)
493 *cur++ = hexchar[(d[j] >> 4) & 15] | 0x80;
494 *cur++ = hexchar[(d[j] >> 0) & 15] | 0x80;
498 *cur++ = hexchar[(d[j] >> 4) & 15];
499 *cur++ = hexchar[(d[j] >> 0) & 15];
507 for (j = 0;j < n;j++)
508 *cur++ = (d[j] >= ' ' && d[j] <= 0x7E) ? d[j] : '.';
513 if (cur >= flushpointer || i >= size)
516 Con_Printf("%s", text);
522 void SZ_HexDumpToConsole(const sizebuf_t *buf)
524 Com_HexDumpToConsole(buf->data, buf->cursize);
528 //============================================================================
536 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
537 void COM_StripExtension (const char *in, char *out)
544 else if (*in == '/' || *in == '\\' || *in == ':')
559 char *COM_FileExtension (const char *in)
561 static char exten[8];
564 while (*in && *in != '.')
569 for (i=0 ; i<7 && *in ; i++,in++)
580 void COM_FileBase (const char *in, char *out)
582 const char *slash, *dot, *s;
598 strcpy (out,"?model?");
613 void COM_DefaultExtension (char *path, const char *extension)
617 // if path doesn't have a .EXT, append extension
618 // (extension should include the .)
620 src = path + strlen(path) - 1;
622 while (*src != '/' && src != path)
625 return; // it has an extension
629 strcat (path, extension);
637 Parse a token out of a string
640 int COM_ParseToken (const char **datapointer)
644 const char *data = *datapointer;
657 while ((c = *data) <= ' ')
669 if (c=='/' && data[1] == '/')
671 while (*data && *data != '\n')
677 // handle quoted strings specially
695 // parse single characters
696 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
701 *datapointer = data+1;
705 // parse a regular word
712 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
726 Returns the position (1 to argc-1) in the program's argument list
727 where the given parameter apears, or 0 if not present
730 int COM_CheckParm (const char *parm)
734 for (i=1 ; i<com_argc ; i++)
737 continue; // NEXTSTEP sometimes clears appkit vars.
738 if (!strcmp (parm,com_argv[i]))
749 Looks for the pop.txt file and verifies it.
750 Sets the "registered" cvar.
751 Immediately exits out if an alternate game was attempted to be started without
755 void COM_CheckRegistered (void)
757 Cvar_Set ("cmdline", com_cmdline);
759 if (!Sys_FileTime("gfx/pop.lmp"))
762 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
764 Con_Printf ("Playing shareware version.\n");
768 Cvar_Set ("registered", "1");
769 Con_Printf ("Playing registered version.\n");
773 void COM_Path_f (void);
781 void COM_InitArgv (void)
784 // reconstitute the command line for the cmdline externally visible cvar
786 for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
789 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
790 com_cmdline[n++] = com_argv[j][i++];
791 if (n < (CMDLINE_LENGTH - 1))
792 com_cmdline[n++] = ' ';
799 void COM_InitGameType (void)
801 char name[MAX_OSPATH];
802 COM_StripExtension(com_argv[0], name);
803 COM_ToLowerString(name, name);
805 if (strstr(name, "transfusion"))
806 gamemode = GAME_TRANSFUSION;
807 else if (strstr(name, "nexiuz"))
808 gamemode = GAME_NEXIUZ;
809 else if (strstr(name, "nehahra"))
810 gamemode = GAME_NEHAHRA;
811 else if (strstr(name, "hipnotic"))
812 gamemode = GAME_HIPNOTIC;
813 else if (strstr(name, "rogue"))
814 gamemode = GAME_ROGUE;
816 gamemode = GAME_NORMAL;
818 if (COM_CheckParm ("-transfusion"))
819 gamemode = GAME_TRANSFUSION;
820 else if (COM_CheckParm ("-nexiuz"))
821 gamemode = GAME_NEXIUZ;
822 else if (COM_CheckParm ("-nehahra"))
823 gamemode = GAME_NEHAHRA;
824 else if (COM_CheckParm ("-hipnotic"))
825 gamemode = GAME_HIPNOTIC;
826 else if (COM_CheckParm ("-rogue"))
827 gamemode = GAME_ROGUE;
828 else if (COM_CheckParm ("-quake"))
829 gamemode = GAME_NORMAL;
834 gamename = "DarkPlaces-Quake";
838 gamename = "Darkplaces-Hipnotic";
839 gamedirname = "hipnotic";
842 gamename = "Darkplaces-Rogue";
843 gamedirname = "rogue";
846 gamename = "DarkPlaces-Nehahra";
847 gamedirname = "nehahra";
851 gamedirname = "data";
853 case GAME_TRANSFUSION:
854 gamename = "Transfusion";
855 gamedirname = "transfusion";
858 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
864 extern void Mathlib_Init(void);
873 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
874 qbyte swaptest[2] = {1,0};
876 // set the byte swapping variables in a portable manner
877 if ( *(short *)swaptest == 1)
879 BigShort = ShortSwap;
880 LittleShort = ShortNoSwap;
882 LittleLong = LongNoSwap;
883 BigFloat = FloatSwap;
884 LittleFloat = FloatNoSwap;
888 BigShort = ShortNoSwap;
889 LittleShort = ShortSwap;
890 BigLong = LongNoSwap;
891 LittleLong = LongSwap;
892 BigFloat = FloatNoSwap;
893 LittleFloat = FloatSwap;
897 pak_mempool = Mem_AllocPool("paks");
899 Cvar_RegisterVariable (®istered);
900 Cvar_RegisterVariable (&cmdline);
901 Cmd_AddCommand ("path", COM_Path_f);
905 COM_InitFilesystem ();
906 COM_CheckRegistered ();
916 does a varargs printf into a temp buffer, so I don't need to have
917 varargs versions of all text functions.
918 FIXME: make this buffer size safe someday
921 char *va(const char *format, ...)
924 // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
925 static char string[8][1024], *s;
926 static int stringindex = 0;
928 s = string[stringindex];
929 stringindex = (stringindex + 1) & 7;
930 va_start (argptr, format);
931 vsprintf (s, format,argptr);
939 =============================================================================
943 =============================================================================
955 char name[MAX_QPATH];
956 int filepos, filelen;
959 typedef struct pack_s
961 char filename[MAX_OSPATH];
975 int filepos, filelen;
985 // LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
986 #define MAX_FILES_IN_PACK 65536
988 pack_t *packlist = NULL;
991 char com_cachedir[MAX_OSPATH];
993 char com_gamedir[MAX_OSPATH];
995 typedef struct searchpath_s
997 // only one of filename / pack will be used
998 char filename[MAX_OSPATH];
1000 struct searchpath_s *next;
1003 searchpath_t *com_searchpaths;
1011 void COM_Path_f (void)
1015 Con_Printf ("Current search path:\n");
1016 for (s=com_searchpaths ; s ; s=s->next)
1020 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1023 Con_Printf ("%s\n", s->filename);
1031 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1034 void COM_CreatePath (char *path)
1038 for (ofs = path+1 ; *ofs ; ofs++)
1040 if (*ofs == '/' || *ofs == '\\')
1042 // create the directory
1056 The filename will be prefixed by the current game directory
1059 qboolean COM_WriteFile (const char *filename, void *data, int len)
1062 char name[MAX_OSPATH];
1064 sprintf (name, "%s/%s", com_gamedir, filename);
1066 // LordHavoc: added this
1067 // create directories up to the file
1068 COM_CreatePath (name);
1070 handle = Sys_FileOpenWrite (name);
1073 Con_Printf ("COM_WriteFile: failed on %s\n", name);
1077 Con_DPrintf ("COM_WriteFile: %s\n", name);
1078 Sys_FileWrite (handle, data, len);
1079 Sys_FileClose (handle);
1088 Copies a file over from the net to the local cache, creating any directories
1089 needed. This is for the convenience of developers using ISDN from home.
1092 void COM_CopyFile (char *netpath, char *cachepath)
1094 int in, out, remaining, count;
1097 remaining = Sys_FileOpenRead (netpath, &in);
1098 COM_CreatePath (cachepath); // create directories up to the cache file
1099 out = Sys_FileOpenWrite (cachepath);
1103 if (remaining < (int)sizeof(buf))
1106 count = sizeof(buf);
1107 Sys_FileRead (in, buf, count);
1108 Sys_FileWrite (out, buf, count);
1113 Sys_FileClose (out);
1121 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1123 int fd = open (path, O_RDONLY);
1124 unsigned char id[2], len_bytes[4];
1128 Sys_Error ("Couldn't open %s", path);
1131 if (offs < 0 || len < 0)
1135 len = lseek (fd, 0, SEEK_END);
1136 lseek (fd, 0, SEEK_SET);
1138 lseek (fd, offs, SEEK_SET);
1142 if (id[0] == 0x1f && id[1] == 0x8b)
1144 lseek (fd, offs + len - 4, SEEK_SET);
1145 read (fd, len_bytes, 4);
1146 len = ((len_bytes[3] << 24)
1147 | (len_bytes[2] << 16)
1148 | (len_bytes[1] << 8)
1152 lseek (fd, offs, SEEK_SET);
1156 setmode (fd, O_BINARY);
1159 return Qdopen (fd, "rbz");
1161 return Qdopen (fd, "rb");
1168 Finds the file in the search path.
1169 Sets com_filesize and one of handle or file
1172 int COM_FindFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1174 searchpath_t *search;
1175 char netpath[MAX_OSPATH];
1177 char cachepath[MAX_OSPATH];
1181 int i, findtime, filenamelen;
1182 char gzfilename[MAX_OSPATH];
1184 filenamelen = strlen (filename);
1185 sprintf (gzfilename, "%s.gz", filename);
1188 Sys_Error ("COM_FindFile: file not set");
1191 // search through the path, one element at a time
1193 search = com_searchpaths;
1195 for ( ; search ; search = search->next)
1197 // is the element a pak file?
1200 // look through all the pak file elements
1202 for (i=0 ; i<pak->numfiles ; i++)
1203 if (!strcmp (pak->files[i].name, filename)
1204 || !strcmp (pak->files[i].name, gzfilename))
1207 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1208 // open a new file on the pakfile
1209 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1210 return com_filesize;
1215 sprintf (netpath, "%s/%s",search->filename, filename);
1217 findtime = Sys_FileTime (netpath);
1222 // see if the file needs to be updated in the cache
1223 if (com_cachedir[0])
1226 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1227 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1229 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1231 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1234 cachetime = Sys_FileTime (cachepath);
1236 if (cachetime < findtime)
1237 COM_CopyFile (netpath, cachepath);
1238 strcpy (netpath, cachepath);
1242 Sys_Printf ("FindFile: %s\n",netpath);
1243 *file = COM_OpenRead (netpath, -1, -1, zip);
1244 return com_filesize;
1249 Sys_Printf ("FindFile: can't find %s\n", filename);
1261 If the requested file is inside a packfile, a new QFile * will be opened
1265 int COM_FOpenFile (const char *filename, QFile **file, qboolean quiet, qboolean zip)
1267 return COM_FindFile (filename, file, quiet, zip);
1275 Filename are reletive to the quake directory.
1276 Always appends a 0 byte.
1281 qbyte *COM_LoadFile (const char *path, qboolean quiet)
1288 buf = NULL; // quiet compiler warning
1291 // look for it in the filesystem or pack files
1292 len = COM_FOpenFile (path, &h, quiet, true);
1298 // extract the filename base name for hunk tag
1299 COM_FileBase (path, base);
1301 buf = Mem_Alloc(tempmempool, len+1);
1303 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1305 ((qbyte *)buf)[len] = 0;
1307 Qread (h, buf, len);
1317 Takes an explicit (not game tree related) path to a pak file.
1319 Loads the header and directory, adding the files at the beginning
1320 of the list so they override previous pack files.
1323 pack_t *COM_LoadPackFile (const char *packfile)
1325 dpackheader_t header;
1326 int i, numpackfiles, packhandle;
1328 // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
1331 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1334 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1335 if (memcmp(header.id, "PACK", 4))
1336 Sys_Error ("%s is not a packfile", packfile);
1337 header.dirofs = LittleLong (header.dirofs);
1338 header.dirlen = LittleLong (header.dirlen);
1340 if (header.dirlen % sizeof(dpackfile_t))
1341 Sys_Error ("%s has an invalid directory size", packfile);
1343 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1345 if (numpackfiles > MAX_FILES_IN_PACK)
1346 Sys_Error ("%s has %i files", packfile, numpackfiles);
1348 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
1349 strcpy (pack->filename, packfile);
1350 pack->handle = packhandle;
1351 pack->numfiles = numpackfiles;
1352 pack->mempool = Mem_AllocPool(packfile);
1353 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
1354 pack->next = packlist;
1357 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
1358 Sys_FileSeek (packhandle, header.dirofs);
1359 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1361 // parse the directory
1362 for (i = 0;i < numpackfiles;i++)
1364 strcpy (pack->files[i].name, info[i].name);
1365 pack->files[i].filepos = LittleLong(info[i].filepos);
1366 pack->files[i].filelen = LittleLong(info[i].filelen);
1371 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1378 COM_AddGameDirectory
1380 Sets com_gamedir, adds the directory to the head of the path,
1381 then loads and adds pak1.pak pak2.pak ...
1384 void COM_AddGameDirectory (char *dir)
1386 stringlist_t *list, *current;
1387 searchpath_t *search;
1389 char pakfile[MAX_OSPATH];
1391 strcpy (com_gamedir, dir);
1394 // add the directory to the search path
1396 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1397 strcpy (search->filename, dir);
1398 search->next = com_searchpaths;
1399 com_searchpaths = search;
1401 // add any paks in the directory
1402 list = listdirectory(dir);
1403 for (current = list;current;current = current->next)
1405 if (matchpattern(current->text, "*.pak", true))
1407 sprintf (pakfile, "%s/%s", dir, current->text);
1408 pak = COM_LoadPackFile (pakfile);
1411 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1413 search->next = com_searchpaths;
1414 com_searchpaths = search;
1417 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1420 freedirectory(list);
1428 void COM_InitFilesystem (void)
1431 searchpath_t *search;
1433 strcpy(com_basedir, ".");
1436 // Overrides the system supplied base directory (under GAMENAME)
1437 i = COM_CheckParm ("-basedir");
1438 if (i && i < com_argc-1)
1439 strcpy (com_basedir, com_argv[i+1]);
1441 i = strlen (com_basedir);
1442 if (i > 0 && (com_basedir[i-1] == '\\' || com_basedir[i-1] == '/'))
1443 com_basedir[i-1] = 0;
1445 // start up with GAMENAME by default (id1)
1446 strcpy(com_modname, GAMENAME);
1447 COM_AddGameDirectory (va("%s/"GAMENAME, com_basedir));
1450 com_modified = true;
1451 strcpy(com_modname, gamedirname);
1452 COM_AddGameDirectory (va("%s/%s", com_basedir, gamedirname));
1456 // Adds basedir/gamedir as an override game
1457 i = COM_CheckParm ("-game");
1458 if (i && i < com_argc-1)
1460 com_modified = true;
1461 strcpy(com_modname, com_argv[i+1]);
1462 COM_AddGameDirectory (va("%s/%s", com_basedir, com_argv[i+1]));
1465 // -path <dir or packfile> [<dir or packfile>] ...
1466 // Fully specifies the exact search path, overriding the generated one
1467 i = COM_CheckParm ("-path");
1470 com_modified = true;
1471 com_searchpaths = NULL;
1472 while (++i < com_argc)
1474 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1477 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
1478 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1480 search->pack = COM_LoadPackFile (com_argv[i]);
1482 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1485 strcpy (search->filename, com_argv[i]);
1486 search->next = com_searchpaths;
1487 com_searchpaths = search;
1492 int COM_FileExists(const char *filename)
1494 searchpath_t *search;
1495 char netpath[MAX_OSPATH];
1499 for (search = com_searchpaths;search;search = search->next)
1504 for (i = 0;i < pak->numfiles;i++)
1505 if (!strcmp (pak->files[i].name, filename))
1510 sprintf (netpath, "%s/%s",search->filename, filename);
1511 findtime = Sys_FileTime (netpath);
1521 //======================================
1522 // LordHavoc: added these because they are useful
1524 void COM_ToLowerString(const char *in, char *out)
1528 if (*in >= 'A' && *in <= 'Z')
1529 *out++ = *in++ + 'a' - 'A';
1535 void COM_ToUpperString(const char *in, char *out)
1539 if (*in >= 'a' && *in <= 'z')
1540 *out++ = *in++ + 'A' - 'a';
1546 int COM_StringBeginsWith(const char *s, const char *match)
1548 for (;*s && *match;s++, match++)