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", "-dibonly"};
40 cvar_t registered = {"registered","0"};
41 cvar_t cmdline = {"cmdline","0", false, true};
43 qboolean com_modified; // set true if using non-id files
47 int static_registered = 1; // only for startup check, then set
49 qboolean msg_suppress_1 = 0;
51 void COM_InitFilesystem (void);
57 #define CMDLINE_LENGTH 256
58 char com_cmdline[CMDLINE_LENGTH];
60 qboolean standard_quake = true, rogue = false, hipnotic = false, nehahra = false;
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 ============================================================================
95 void Q_memset (void *dest, int fill, int count)
99 if ( (((long)dest | count) & 3) == 0)
102 fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
103 for (i=0 ; i<count ; i++)
104 ((int *)dest)[i] = fill;
107 for (i=0 ; i<count ; i++)
108 ((byte *)dest)[i] = fill;
111 void Q_memcpy (void *dest, void *src, int count)
115 if (( ( (long)dest | (long)src | count) & 3) == 0 )
118 for (i=0 ; i<count ; i++)
119 ((int *)dest)[i] = ((int *)src)[i];
122 for (i=0 ; i<count ; i++)
123 ((byte *)dest)[i] = ((byte *)src)[i];
126 int Q_memcmp (void *m1, void *m2, int count)
131 if (((byte *)m1)[count] != ((byte *)m2)[count])
137 void Q_strcpy (char *dest, char *src)
146 void Q_strncpy (char *dest, char *src, int count)
148 while (*src && count--)
156 int Q_strlen (char *str)
167 char *Q_strrchr(char *s, char c)
169 int len = Q_strlen(s);
172 if (*--s == c) return s;
176 void Q_strcat (char *dest, char *src)
178 dest += Q_strlen(dest);
179 Q_strcpy (dest, src);
182 int Q_strcmp (char *s1, char *s2)
187 return -1; // strings not equal
189 return 0; // strings are equal
197 int Q_strncmp (char *s1, char *s2, int count)
204 return -1; // strings not equal
206 return 0; // strings are equal
214 int Q_strncasecmp (char *s1, char *s2, int n)
224 return 0; // strings are equal until end point
228 if (c1 >= 'a' && c1 <= 'z')
230 if (c2 >= 'a' && c2 <= 'z')
233 return -1; // strings not equal
236 return 0; // strings are equal
244 int Q_strcasecmp (char *s1, char *s2)
246 return Q_strncasecmp (s1, s2, 99999);
249 int Q_atoi (char *str)
268 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
274 if (c >= '0' && c <= '9')
275 val = (val<<4) + c - '0';
276 else if (c >= 'a' && c <= 'f')
277 val = (val<<4) + c - 'a' + 10;
278 else if (c >= 'A' && c <= 'F')
279 val = (val<<4) + c - 'A' + 10;
286 // check for character
290 return sign * str[1];
299 if (c <'0' || c > '9')
301 val = val*10 + c - '0';
308 float Q_atof (char *str)
328 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
334 if (c >= '0' && c <= '9')
335 val = (val*16) + c - '0';
336 else if (c >= 'a' && c <= 'f')
337 val = (val*16) + c - 'a' + 10;
338 else if (c >= 'A' && c <= 'F')
339 val = (val*16) + c - 'A' + 10;
346 // check for character
350 return sign * str[1];
366 if (c <'0' || c > '9')
368 val = val*10 + c - '0';
374 while (total > decimal)
385 ============================================================================
389 ============================================================================
392 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
393 short (*BigShort) (short l);
394 short (*LittleShort) (short l);
395 int (*BigLong) (int l);
396 int (*LittleLong) (int l);
397 float (*BigFloat) (float l);
398 float (*LittleFloat) (float l);
401 short ShortSwap (short l)
411 short ShortNoSwap (short l)
425 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
428 int LongNoSwap (int l)
433 float FloatSwap (float f)
443 dat2.b[0] = dat1.b[3];
444 dat2.b[1] = dat1.b[2];
445 dat2.b[2] = dat1.b[1];
446 dat2.b[3] = dat1.b[0];
450 float FloatNoSwap (float f)
456 ==============================================================================
460 Handles byte ordering and avoids alignment errors
461 ==============================================================================
468 void MSG_WriteChar (sizebuf_t *sb, int c)
473 // if (c < -128 || c > 127)
474 // Sys_Error ("MSG_WriteChar: range error");
477 buf = SZ_GetSpace (sb, 1);
481 void MSG_WriteByte (sizebuf_t *sb, int c)
486 // if (c < 0 || c > 255)
487 // Sys_Error ("MSG_WriteByte: range error");
490 buf = SZ_GetSpace (sb, 1);
494 void MSG_WriteShort (sizebuf_t *sb, int c)
499 // if (c < ((short)0x8000) || c > (short)0x7fff)
500 // Sys_Error ("MSG_WriteShort: range error");
503 buf = SZ_GetSpace (sb, 2);
508 void MSG_WriteLong (sizebuf_t *sb, int c)
512 buf = SZ_GetSpace (sb, 4);
514 buf[1] = (c>>8)&0xff;
515 buf[2] = (c>>16)&0xff;
519 void MSG_WriteFloat (sizebuf_t *sb, float f)
529 dat.l = LittleLong (dat.l);
531 SZ_Write (sb, &dat.l, 4);
534 void MSG_WriteString (sizebuf_t *sb, char *s)
537 SZ_Write (sb, "", 1);
539 SZ_Write (sb, s, strlen(s)+1);
542 // used by server (always dpprotocol)
543 // moved to common.h as #define
545 void MSG_WriteFloatCoord (sizebuf_t *sb, float f)
547 MSG_WriteFloat(sb, f);
552 void MSG_WriteCoord (sizebuf_t *sb, float f)
555 MSG_WriteFloat(sb, f);
557 MSG_WriteShort (sb, (int)(f*8.0f + 0.5f));
560 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
562 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
565 // LordHavoc: round to nearest value, rather than rounding down, fixes crosshair problem
566 void MSG_WriteAngle (sizebuf_t *sb, float f)
568 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
575 qboolean msg_badread;
577 void MSG_BeginReading (void)
584 // returns -1 and sets msg_badread if no more characters are available
585 int MSG_ReadChar (void)
589 // LordHavoc: minor optimization
590 if (msg_readcount >= net_message.cursize)
591 // if (msg_readcount+1 > net_message.cursize)
597 c = (signed char)net_message.data[msg_readcount];
603 int MSG_ReadByte (void)
607 // LordHavoc: minor optimization
608 if (msg_readcount >= net_message.cursize)
609 // if (msg_readcount+1 > net_message.cursize)
615 c = (unsigned char)net_message.data[msg_readcount];
622 int MSG_ReadShort (void)
626 if (msg_readcount+2 > net_message.cursize)
632 c = (short)(net_message.data[msg_readcount]
633 + (net_message.data[msg_readcount+1]<<8));
640 int MSG_ReadLong (void)
644 if (msg_readcount+4 > net_message.cursize)
650 c = net_message.data[msg_readcount]
651 + (net_message.data[msg_readcount+1]<<8)
652 + (net_message.data[msg_readcount+2]<<16)
653 + (net_message.data[msg_readcount+3]<<24);
660 float MSG_ReadFloat (void)
669 dat.b[0] = net_message.data[msg_readcount];
670 dat.b[1] = net_message.data[msg_readcount+1];
671 dat.b[2] = net_message.data[msg_readcount+2];
672 dat.b[3] = net_message.data[msg_readcount+3];
675 dat.l = LittleLong (dat.l);
680 char *MSG_ReadString (void)
682 static char string[2048];
689 if (c == -1 || c == 0)
693 } while (l < sizeof(string)-1);
700 // used by server (always dpprotocol)
701 // moved to common.h as #define
703 float MSG_ReadFloatCoord (void)
705 return MSG_ReadFloat();
710 float MSG_ReadCoord (void)
713 return MSG_ReadFloat();
715 return MSG_ReadShort() * (1.0f/8.0f);
719 float MSG_ReadCoord (void)
721 return MSG_ReadShort() * (1.0f/8.0f);
724 float MSG_ReadAngle (void)
726 return MSG_ReadChar() * (360.0f/256.0f);
729 float MSG_ReadPreciseAngle (void)
731 return MSG_ReadShort() * (360.0f/65536);
736 //===========================================================================
738 void SZ_Alloc (sizebuf_t *buf, int startsize)
742 buf->data = Hunk_AllocName (startsize, "sizebuf");
743 buf->maxsize = startsize;
748 void SZ_Free (sizebuf_t *buf)
750 // Z_Free (buf->data);
756 void SZ_Clear (sizebuf_t *buf)
761 void *SZ_GetSpace (sizebuf_t *buf, int length)
765 if (buf->cursize + length > buf->maxsize)
767 if (!buf->allowoverflow)
768 Host_Error ("SZ_GetSpace: overflow without allowoverflow set - use -zone on the commandline for more zone memory, default: 128k (quake original default was 48k)");
770 if (length > buf->maxsize)
771 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
773 buf->overflowed = true;
774 Con_Printf ("SZ_GetSpace: overflow");
778 data = buf->data + buf->cursize;
779 buf->cursize += length;
784 void SZ_Write (sizebuf_t *buf, void *data, int length)
786 memcpy (SZ_GetSpace(buf,length),data,length);
789 void SZ_Print (sizebuf_t *buf, char *data)
793 len = strlen(data)+1;
795 // byte * cast to keep VC++ happy
796 if (buf->data[buf->cursize-1])
797 memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
799 memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
803 //============================================================================
811 char *COM_SkipPath (char *pathname)
830 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
831 void COM_StripExtension (char *in, char *out)
838 if ((*in == '/') || (*in == '\\') || (*in == ':'))
851 char *COM_FileExtension (char *in)
853 static char exten[8];
856 while (*in && *in != '.')
861 for (i=0 ; i<7 && *in ; i++,in++)
872 void COM_FileBase (char *in, char *out)
891 strcpy (out,"?model?");
906 void COM_DefaultExtension (char *path, char *extension)
910 // if path doesn't have a .EXT, append extension
911 // (extension should include the .)
913 src = path + strlen(path) - 1;
915 while (*src != '/' && src != path)
918 return; // it has an extension
922 strcat (path, extension);
930 Parse a token out of a string
933 char *COM_Parse (char *data)
946 while ( (c = *data) <= ' ')
949 return NULL; // end of file;
954 if (c=='/' && data[1] == '/')
956 while (*data && *data != '\n')
962 // handle quoted strings specially
979 // parse single characters
980 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
988 // parse a regular word
995 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1008 Returns the position (1 to argc-1) in the program's argument list
1009 where the given parameter apears, or 0 if not present
1012 int COM_CheckParm (char *parm)
1016 for (i=1 ; i<com_argc ; i++)
1019 continue; // NEXTSTEP sometimes clears appkit vars.
1020 if (!strcmp (parm,com_argv[i]))
1031 Looks for the pop.txt file and verifies it.
1032 Sets the "registered" cvar.
1033 Immediately exits out if an alternate game was attempted to be started without
1037 void COM_CheckRegistered (void)
1039 Cvar_Set ("cmdline", com_cmdline);
1041 static_registered = 0;
1043 if (!Sys_FileTime("gfx/pop.lmp"))
1046 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1048 Con_Printf ("Playing shareware version.\n");
1050 // Sys_Error ("This dedicated server requires a full registered copy of Quake");
1052 // Con_Printf ("Playing shareware version.\n");
1053 // if (com_modified)
1054 // Sys_Error ("You must have the registered version to use modified games");
1058 // Cvar_Set ("cmdline", com_cmdline);
1059 Cvar_Set ("registered", "1");
1060 static_registered = 1;
1061 Con_Printf ("Playing registered version.\n");
1065 void COM_Path_f (void);
1073 void COM_InitArgv (int argc, char **argv)
1078 // reconstitute the command line for the cmdline externally visible cvar
1081 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1085 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1087 com_cmdline[n++] = argv[j][i++];
1090 if (n < (CMDLINE_LENGTH - 1))
1091 com_cmdline[n++] = ' ';
1100 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1103 largv[com_argc] = argv[com_argc];
1104 if (!strcmp ("-safe", argv[com_argc]))
1110 // force all the safe-mode switches. Note that we reserved extra space in
1111 // case we need to add these, so we don't need an overflow check
1112 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1114 largv[com_argc] = safeargvs[i];
1119 largv[com_argc] = argvdummy;
1124 standard_quake = false;
1126 if (COM_CheckParm ("-rogue"))
1129 standard_quake = false;
1132 if (COM_CheckParm ("-hipnotic"))
1135 standard_quake = false;
1138 if (COM_CheckParm ("-nehahra"))
1141 standard_quake = false;
1147 unsigned int qmalloctotal_alloc, qmalloctotal_alloccount, qmalloctotal_free, qmalloctotal_freecount;
1149 void *qmalloc(unsigned int size)
1152 qmalloctotal_alloc += size;
1153 qmalloctotal_alloccount++;
1154 mem = malloc(size+sizeof(unsigned int));
1158 return (void *)(mem + 1);
1161 void qfree(void *mem)
1167 m--; // back up to size
1168 qmalloctotal_free += *m; // size
1169 qmalloctotal_freecount++;
1173 extern void GL_TextureStats_PrintTotal(void);
1174 extern int hunk_low_used, hunk_high_used, hunk_size;
1175 void COM_Memstats_f(void)
1177 Con_Printf("%i malloc calls totalling %i bytes (%.4gMB)\n%i free calls totalling %i bytes (%.4gMB)\n%i bytes (%.4gMB) currently allocated\n", qmalloctotal_alloccount, qmalloctotal_alloc, qmalloctotal_alloc / 1048576.0, qmalloctotal_freecount, qmalloctotal_free, qmalloctotal_free / 1048576.0, qmalloctotal_alloc - qmalloctotal_free, (qmalloctotal_alloc - qmalloctotal_free) / 1048576.0);
1178 GL_TextureStats_PrintTotal();
1179 Con_Printf ("%i bytes (%.4gMB) of %.4gMB hunk in use\n", hunk_low_used + hunk_high_used, (hunk_low_used + hunk_high_used) / 1048576.0, hunk_size / 1048576.0);
1188 void COM_Init (char *basedir)
1190 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
1191 byte swaptest[2] = {1,0};
1193 // set the byte swapping variables in a portable manner
1194 if ( *(short *)swaptest == 1)
1196 BigShort = ShortSwap;
1197 LittleShort = ShortNoSwap;
1199 LittleLong = LongNoSwap;
1200 BigFloat = FloatSwap;
1201 LittleFloat = FloatNoSwap;
1205 BigShort = ShortNoSwap;
1206 LittleShort = ShortSwap;
1207 BigLong = LongNoSwap;
1208 LittleLong = LongSwap;
1209 BigFloat = FloatNoSwap;
1210 LittleFloat = FloatSwap;
1214 Cvar_RegisterVariable (®istered);
1215 Cvar_RegisterVariable (&cmdline);
1216 Cmd_AddCommand ("path", COM_Path_f);
1217 Cmd_AddCommand ("memstats", COM_Memstats_f);
1219 COM_InitFilesystem ();
1220 COM_CheckRegistered ();
1228 does a varargs printf into a temp buffer, so I don't need to have
1229 varargs versions of all text functions.
1230 FIXME: make this buffer size safe someday
1233 char *va(char *format, ...)
1236 static char string[1024];
1238 va_start (argptr, format);
1239 vsprintf (string, format,argptr);
1246 /// just for debugging
1247 int memsearch (byte *start, int count, int search)
1251 for (i=0 ; i<count ; i++)
1252 if (start[i] == search)
1258 =============================================================================
1262 =============================================================================
1274 char name[MAX_QPATH];
1275 int filepos, filelen;
1278 typedef struct pack_s
1280 char filename[MAX_OSPATH];
1292 int filepos, filelen;
1302 // LordHavoc: was 2048, increased to 16384 and changed info[MAX_PACK_FILES] to a temporary malloc to avoid stack overflows
1303 #define MAX_FILES_IN_PACK 16384
1306 char com_cachedir[MAX_OSPATH];
1308 char com_gamedir[MAX_OSPATH];
1310 typedef struct searchpath_s
1312 char filename[MAX_OSPATH];
1313 pack_t *pack; // only one of filename / pack will be used
1314 struct searchpath_s *next;
1317 searchpath_t *com_searchpaths;
1325 void COM_Path_f (void)
1329 Con_Printf ("Current search path:\n");
1330 for (s=com_searchpaths ; s ; s=s->next)
1334 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1337 Con_Printf ("%s\n", s->filename);
1345 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1348 void COM_CreatePath (char *path)
1352 for (ofs = path+1 ; *ofs ; ofs++)
1354 if (*ofs == '/' || *ofs == '\\' || *ofs == ':')
1355 { // create the directory
1369 The filename will be prefixed by the current game directory
1372 void COM_WriteFile (char *filename, void *data, int len)
1375 char name[MAX_OSPATH];
1377 sprintf (name, "%s/%s", com_gamedir, filename);
1379 // LordHavoc: added this
1380 COM_CreatePath (name); // create directories up to the file
1382 handle = Sys_FileOpenWrite (name);
1385 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1389 Sys_Printf ("COM_WriteFile: %s\n", name);
1390 Sys_FileWrite (handle, data, len);
1391 Sys_FileClose (handle);
1399 Copies a file over from the net to the local cache, creating any directories
1400 needed. This is for the convenience of developers using ISDN from home.
1403 void COM_CopyFile (char *netpath, char *cachepath)
1406 int remaining, count;
1409 remaining = Sys_FileOpenRead (netpath, &in);
1410 COM_CreatePath (cachepath); // create directories up to the cache file
1411 out = Sys_FileOpenWrite (cachepath);
1415 if (remaining < sizeof(buf))
1418 count = sizeof(buf);
1419 Sys_FileRead (in, buf, count);
1420 Sys_FileWrite (out, buf, count);
1425 Sys_FileClose (out);
1433 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1435 int fd = open (path, O_RDONLY);
1436 unsigned char id[2];
1437 unsigned char len_bytes[4];
1441 Sys_Error ("Couldn't open %s", path);
1444 if (offs < 0 || len < 0)
1448 len = lseek (fd, 0, SEEK_END);
1449 lseek (fd, 0, SEEK_SET);
1451 lseek (fd, offs, SEEK_SET);
1455 if (id[0] == 0x1f && id[1] == 0x8b)
1457 lseek (fd, offs + len - 4, SEEK_SET);
1458 read (fd, len_bytes, 4);
1459 len = ((len_bytes[3] << 24)
1460 | (len_bytes[2] << 16)
1461 | (len_bytes[1] << 8)
1465 lseek (fd, offs, SEEK_SET);
1469 setmode (fd, O_BINARY);
1472 return Qdopen (fd, "rbz");
1474 return Qdopen (fd, "rb");
1481 Finds the file in the search path.
1482 Sets com_filesize and one of handle or file
1485 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1487 searchpath_t *search;
1488 char netpath[MAX_OSPATH];
1490 char cachepath[MAX_OSPATH];
1496 char gzfilename[MAX_OSPATH];
1499 filenamelen = strlen (filename);
1500 sprintf (gzfilename, "%s.gz", filename);
1503 Sys_Error ("COM_FindFile: file not set");
1506 // search through the path, one element at a time
1508 search = com_searchpaths;
1510 { // gross hack to use quake 1 progs with quake 2 maps
1511 if (!strcmp(filename, "progs.dat"))
1512 search = search->next;
1515 for ( ; search ; search = search->next)
1517 // is the element a pak file?
1520 // look through all the pak file elements
1522 for (i=0 ; i<pak->numfiles ; i++)
1523 if (!strcmp (pak->files[i].name, filename)
1524 || !strcmp (pak->files[i].name, gzfilename))
1527 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1528 // open a new file on the pakfile
1529 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1530 return com_filesize;
1535 // check a file in the directory tree
1536 // if (!static_registered)
1537 // { // if not a registered version, don't ever go beyond base
1538 // if ( strchr (filename, '/') || strchr (filename,'\\'))
1542 sprintf (netpath, "%s/%s",search->filename, filename);
1544 findtime = Sys_FileTime (netpath);
1549 // see if the file needs to be updated in the cache
1550 if (com_cachedir[0])
1553 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1554 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1556 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1558 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1561 cachetime = Sys_FileTime (cachepath);
1563 if (cachetime < findtime)
1564 COM_CopyFile (netpath, cachepath);
1565 strcpy (netpath, cachepath);
1570 Sys_Printf ("FindFile: %s\n",netpath);
1571 *file = COM_OpenRead (netpath, -1, -1, zip);
1572 return com_filesize;
1578 Sys_Printf ("FindFile: can't find %s\n", filename);
1590 If the requested file is inside a packfile, a new QFile * will be opened
1594 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1596 return COM_FindFile (filename, file, quiet, zip);
1604 Filename are reletive to the quake directory.
1605 Always appends a 0 byte.
1608 cache_user_t *loadcache;
1611 byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
1618 buf = NULL; // quiet compiler warning
1621 // look for it in the filesystem or pack files
1622 len = COM_FOpenFile (path, &h, quiet, true);
1628 // extract the filename base name for hunk tag
1629 COM_FileBase (path, base);
1634 buf = Hunk_AllocName (len+1, va("%s (file)", path));
1636 Sys_Error ("COM_LoadFile: not enough hunk space for %s (size %i)", path, len);
1639 // buf = Z_Malloc (len+1);
1641 // Sys_Error ("COM_LoadFile: not enough zone space for %s (size %i)", path, len);
1644 // buf = Cache_Alloc (loadcache, len+1, base);
1646 // Sys_Error ("COM_LoadFile: not enough cache space for %s (size %i)", path, len);
1649 buf = qmalloc (len+1);
1651 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1654 Sys_Error ("COM_LoadFile: bad usehunk");
1658 ((byte *)buf)[len] = 0;
1660 Qread (h, buf, len);
1666 byte *COM_LoadHunkFile (char *path, qboolean quiet)
1668 return COM_LoadFile (path, 1, quiet);
1671 // LordHavoc: returns malloc'd memory
1672 byte *COM_LoadMallocFile (char *path, qboolean quiet)
1674 return COM_LoadFile (path, 5, quiet);
1678 void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
1681 COM_LoadFile (path, 3, quiet);
1689 Takes an explicit (not game tree related) path to a pak file.
1691 Loads the header and directory, adding the files at the beginning
1692 of the list so they override previous pack files.
1695 pack_t *COM_LoadPackFile (char *packfile)
1697 dpackheader_t header;
1699 packfile_t *newfiles;
1703 // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
1706 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1708 // Con_Printf ("Couldn't open %s\n", packfile);
1711 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1712 if (header.id[0] != 'P' || header.id[1] != 'A'
1713 || header.id[2] != 'C' || header.id[3] != 'K')
1714 Sys_Error ("%s is not a packfile", packfile);
1715 header.dirofs = LittleLong (header.dirofs);
1716 header.dirlen = LittleLong (header.dirlen);
1718 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1720 if (numpackfiles > MAX_FILES_IN_PACK)
1721 Sys_Error ("%s has %i files", packfile, numpackfiles);
1723 newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "pack file-table");
1725 info = qmalloc(sizeof(*info)*MAX_FILES_IN_PACK);
1726 Sys_FileSeek (packhandle, header.dirofs);
1727 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1729 // parse the directory
1730 for (i=0 ; i<numpackfiles ; i++)
1732 strcpy (newfiles[i].name, info[i].name);
1733 newfiles[i].filepos = LittleLong(info[i].filepos);
1734 newfiles[i].filelen = LittleLong(info[i].filelen);
1738 pack = Hunk_AllocName (sizeof (pack_t), packfile);
1739 strcpy (pack->filename, packfile);
1740 pack->handle = packhandle;
1741 pack->numfiles = numpackfiles;
1742 pack->files = newfiles;
1744 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1751 COM_AddGameDirectory
1753 Sets com_gamedir, adds the directory to the head of the path,
1754 then loads and adds pak1.pak pak2.pak ...
1757 void COM_AddGameDirectory (char *dir)
1760 searchpath_t *search;
1762 char pakfile[MAX_OSPATH];
1764 strcpy (com_gamedir, dir);
1767 // add the directory to the search path
1769 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1770 strcpy (search->filename, dir);
1771 search->next = com_searchpaths;
1772 com_searchpaths = search;
1775 // add any pak files in the format pak0.pak pak1.pak, ...
1779 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1780 pak = COM_LoadPackFile (pakfile);
1783 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1785 search->next = com_searchpaths;
1786 com_searchpaths = search;
1790 // add the contents of the parms.txt file to the end of the command line
1800 void COM_InitFilesystem (void)
1803 char basedir[MAX_OSPATH];
1804 searchpath_t *search;
1808 // Overrides the system supplied base directory (under GAMENAME)
1810 i = COM_CheckParm ("-basedir");
1811 if (i && i < com_argc-1)
1812 strcpy (basedir, com_argv[i+1]);
1814 strcpy (basedir, host_parms.basedir);
1816 j = strlen (basedir);
1820 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1827 // Overrides the system supplied cache directory (NULL or /qcache)
1828 // -cachedir - will disable caching.
1830 i = COM_CheckParm ("-cachedir");
1831 if (i && i < com_argc-1)
1833 if (com_argv[i+1][0] == '-')
1834 com_cachedir[0] = 0;
1836 strcpy (com_cachedir, com_argv[i+1]);
1838 else if (host_parms.cachedir)
1839 strcpy (com_cachedir, host_parms.cachedir);
1841 com_cachedir[0] = 0;
1845 // start up with GAMENAME by default (id1)
1847 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1850 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1852 if (COM_CheckParm ("-rogue"))
1853 COM_AddGameDirectory (va("%s/rogue", basedir) );
1854 if (COM_CheckParm ("-hipnotic"))
1855 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1856 if (COM_CheckParm ("-nehahra"))
1857 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1862 // Adds basedir/gamedir as an override game
1864 i = COM_CheckParm ("-game");
1865 if (i && i < com_argc-1)
1867 com_modified = true;
1868 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1872 // -path <dir or packfile> [<dir or packfile>] ...
1873 // Fully specifies the exact search path, overriding the generated one
1875 i = COM_CheckParm ("-path");
1878 com_modified = true;
1879 com_searchpaths = NULL;
1880 while (++i < com_argc)
1882 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1885 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1886 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1888 search->pack = COM_LoadPackFile (com_argv[i]);
1890 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1893 strcpy (search->filename, com_argv[i]);
1894 search->next = com_searchpaths;
1895 com_searchpaths = search;
1899 if (COM_CheckParm ("-proghack"))
1903 int COM_FileExists(char *filename)
1905 searchpath_t *search;
1906 char netpath[MAX_OSPATH];
1911 for (search = com_searchpaths;search;search = search->next)
1916 for (i = 0;i < pak->numfiles;i++)
1917 if (!strcmp (pak->files[i].name, filename))
1922 sprintf (netpath, "%s/%s",search->filename, filename);
1923 findtime = Sys_FileTime (netpath);
1933 //======================================
1934 // LordHavoc: added these because they are useful
1936 void COM_ToLowerString(char *in, char *out)
1940 if (*in >= 'A' && *in <= 'Z')
1941 *out++ = *in++ + 'a' - 'A';
1947 void COM_ToUpperString(char *in, char *out)
1951 if (*in >= 'a' && *in <= 'z')
1952 *out++ = *in++ + 'A' - 'a';