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 = {0, "registered","0"};
41 cvar_t cmdline = {0, "cmdline","0"};
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 // LordHavoc: made commandline 1024 characters instead of 256
58 #define CMDLINE_LENGTH 1024
59 char com_cmdline[CMDLINE_LENGTH];
61 qboolean standard_quake = true, rogue = false, hipnotic = false, nehahra = false;
66 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.
68 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
69 only used during filesystem initialization.
71 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.
73 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
74 specified, when a file is found by the normal search path, it will be mirrored
75 into the cache directory, then opened there.
80 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.
84 //============================================================================
88 ============================================================================
90 LIBRARY REPLACEMENT FUNCTIONS
92 ============================================================================
96 void Q_memset (void *dest, int fill, int count)
100 if ( (((long)dest | count) & 3) == 0)
103 fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
104 for (i=0 ; i<count ; i++)
105 ((int *)dest)[i] = fill;
108 for (i=0 ; i<count ; i++)
109 ((byte *)dest)[i] = fill;
112 void Q_memcpy (void *dest, void *src, int count)
116 if (( ( (long)dest | (long)src | count) & 3) == 0 )
119 for (i=0 ; i<count ; i++)
120 ((int *)dest)[i] = ((int *)src)[i];
123 for (i=0 ; i<count ; i++)
124 ((byte *)dest)[i] = ((byte *)src)[i];
127 int Q_memcmp (void *m1, void *m2, int count)
132 if (((byte *)m1)[count] != ((byte *)m2)[count])
138 void Q_strcpy (char *dest, char *src)
147 void Q_strncpy (char *dest, char *src, int count)
149 while (*src && count--)
157 int Q_strlen (char *str)
168 char *Q_strrchr(char *s, char c)
170 int len = Q_strlen(s);
173 if (*--s == c) return s;
177 void Q_strcat (char *dest, char *src)
179 dest += Q_strlen(dest);
180 Q_strcpy (dest, src);
183 int Q_strcmp (char *s1, char *s2)
188 return -1; // strings not equal
190 return 0; // strings are equal
198 int Q_strncmp (char *s1, char *s2, int count)
205 return -1; // strings not equal
207 return 0; // strings are equal
215 int Q_strncasecmp (char *s1, char *s2, int n)
225 return 0; // strings are equal until end point
229 if (c1 >= 'a' && c1 <= 'z')
231 if (c2 >= 'a' && c2 <= 'z')
234 return -1; // strings not equal
237 return 0; // strings are equal
245 int Q_strcasecmp (char *s1, char *s2)
247 return Q_strncasecmp (s1, s2, 99999);
250 int Q_atoi (char *str)
269 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
275 if (c >= '0' && c <= '9')
276 val = (val<<4) + c - '0';
277 else if (c >= 'a' && c <= 'f')
278 val = (val<<4) + c - 'a' + 10;
279 else if (c >= 'A' && c <= 'F')
280 val = (val<<4) + c - 'A' + 10;
287 // check for character
291 return sign * str[1];
300 if (c <'0' || c > '9')
302 val = val*10 + c - '0';
309 float Q_atof (char *str)
329 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
335 if (c >= '0' && c <= '9')
336 val = (val*16) + c - '0';
337 else if (c >= 'a' && c <= 'f')
338 val = (val*16) + c - 'a' + 10;
339 else if (c >= 'A' && c <= 'F')
340 val = (val*16) + c - 'A' + 10;
347 // check for character
351 return sign * str[1];
367 if (c <'0' || c > '9')
369 val = val*10 + c - '0';
375 while (total > decimal)
386 ============================================================================
390 ============================================================================
393 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
394 short (*BigShort) (short l);
395 short (*LittleShort) (short l);
396 int (*BigLong) (int l);
397 int (*LittleLong) (int l);
398 float (*BigFloat) (float l);
399 float (*LittleFloat) (float l);
402 short ShortSwap (short l)
412 short ShortNoSwap (short l)
426 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
429 int LongNoSwap (int l)
434 float FloatSwap (float f)
444 dat2.b[0] = dat1.b[3];
445 dat2.b[1] = dat1.b[2];
446 dat2.b[2] = dat1.b[1];
447 dat2.b[3] = dat1.b[0];
451 float FloatNoSwap (float f)
457 ==============================================================================
461 Handles byte ordering and avoids alignment errors
462 ==============================================================================
469 void MSG_WriteChar (sizebuf_t *sb, int c)
474 // if (c < -128 || c > 127)
475 // Sys_Error ("MSG_WriteChar: range error");
478 buf = SZ_GetSpace (sb, 1);
482 void MSG_WriteByte (sizebuf_t *sb, int c)
487 // if (c < 0 || c > 255)
488 // Sys_Error ("MSG_WriteByte: range error");
491 buf = SZ_GetSpace (sb, 1);
495 void MSG_WriteShort (sizebuf_t *sb, int c)
500 // if (c < ((short)0x8000) || c > (short)0x7fff)
501 // Sys_Error ("MSG_WriteShort: range error");
504 buf = SZ_GetSpace (sb, 2);
509 void MSG_WriteLong (sizebuf_t *sb, int c)
513 buf = SZ_GetSpace (sb, 4);
515 buf[1] = (c>>8)&0xff;
516 buf[2] = (c>>16)&0xff;
520 void MSG_WriteFloat (sizebuf_t *sb, float f)
530 dat.l = LittleLong (dat.l);
532 SZ_Write (sb, &dat.l, 4);
535 void MSG_WriteString (sizebuf_t *sb, char *s)
538 SZ_Write (sb, "", 1);
540 SZ_Write (sb, s, strlen(s)+1);
543 // used by server (always dpprotocol)
544 // moved to common.h as #define
546 void MSG_WriteFloatCoord (sizebuf_t *sb, float f)
548 MSG_WriteFloat(sb, f);
553 void MSG_WriteCoord (sizebuf_t *sb, float f)
556 MSG_WriteFloat(sb, f);
558 MSG_WriteShort (sb, (int)(f*8.0f + 0.5f));
561 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
563 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
566 // LordHavoc: round to nearest value, rather than rounding down, fixes crosshair problem
567 void MSG_WriteAngle (sizebuf_t *sb, float f)
569 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
576 qboolean msg_badread;
578 void MSG_BeginReading (void)
585 // returns -1 and sets msg_badread if no more characters are available
586 int MSG_ReadChar (void)
590 // LordHavoc: minor optimization
591 if (msg_readcount >= net_message.cursize)
592 // if (msg_readcount+1 > net_message.cursize)
598 c = (signed char)net_message.data[msg_readcount];
604 int MSG_ReadByte (void)
608 // LordHavoc: minor optimization
609 if (msg_readcount >= net_message.cursize)
610 // if (msg_readcount+1 > net_message.cursize)
616 c = (unsigned char)net_message.data[msg_readcount];
623 int MSG_ReadShort (void)
627 if (msg_readcount+2 > net_message.cursize)
633 c = (short)(net_message.data[msg_readcount]
634 + (net_message.data[msg_readcount+1]<<8));
641 int MSG_ReadLong (void)
645 if (msg_readcount+4 > net_message.cursize)
651 c = net_message.data[msg_readcount]
652 + (net_message.data[msg_readcount+1]<<8)
653 + (net_message.data[msg_readcount+2]<<16)
654 + (net_message.data[msg_readcount+3]<<24);
661 float MSG_ReadFloat (void)
670 dat.b[0] = net_message.data[msg_readcount];
671 dat.b[1] = net_message.data[msg_readcount+1];
672 dat.b[2] = net_message.data[msg_readcount+2];
673 dat.b[3] = net_message.data[msg_readcount+3];
676 dat.l = LittleLong (dat.l);
681 char *MSG_ReadString (void)
683 static char string[2048];
690 if (c == -1 || c == 0)
694 } while (l < sizeof(string)-1);
701 // used by server (always dpprotocol)
702 // moved to common.h as #define
704 float MSG_ReadFloatCoord (void)
706 return MSG_ReadFloat();
711 float MSG_ReadCoord (void)
714 return MSG_ReadFloat();
716 return MSG_ReadShort() * (1.0f/8.0f);
720 float MSG_ReadCoord (void)
722 return MSG_ReadShort() * (1.0f/8.0f);
725 float MSG_ReadAngle (void)
727 return MSG_ReadChar() * (360.0f/256.0f);
730 float MSG_ReadPreciseAngle (void)
732 return MSG_ReadShort() * (360.0f/65536);
737 //===========================================================================
739 void SZ_Alloc (sizebuf_t *buf, int startsize)
743 buf->data = Hunk_AllocName (startsize, "sizebuf");
744 buf->maxsize = startsize;
749 void SZ_Free (sizebuf_t *buf)
751 // Z_Free (buf->data);
757 void SZ_Clear (sizebuf_t *buf)
762 void *SZ_GetSpace (sizebuf_t *buf, int length)
766 if (buf->cursize + length > buf->maxsize)
768 if (!buf->allowoverflow)
769 Host_Error ("SZ_GetSpace: overflow without allowoverflow set - use -zone on the commandline for more zone memory, default: 128k (quake original default was 48k)");
771 if (length > buf->maxsize)
772 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
774 buf->overflowed = true;
775 Con_Printf ("SZ_GetSpace: overflow");
779 data = buf->data + buf->cursize;
780 buf->cursize += length;
785 void SZ_Write (sizebuf_t *buf, void *data, int length)
787 memcpy (SZ_GetSpace(buf,length),data,length);
790 void SZ_Print (sizebuf_t *buf, char *data)
794 len = strlen(data)+1;
796 // byte * cast to keep VC++ happy
797 if (buf->data[buf->cursize-1])
798 memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
800 memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
804 //============================================================================
812 char *COM_SkipPath (char *pathname)
831 // LordHavoc: replacement for severely broken COM_StripExtension that was used in original quake.
832 void COM_StripExtension (char *in, char *out)
839 if ((*in == '/') || (*in == '\\') || (*in == ':'))
852 char *COM_FileExtension (char *in)
854 static char exten[8];
857 while (*in && *in != '.')
862 for (i=0 ; i<7 && *in ; i++,in++)
873 void COM_FileBase (char *in, char *out)
892 strcpy (out,"?model?");
907 void COM_DefaultExtension (char *path, char *extension)
911 // if path doesn't have a .EXT, append extension
912 // (extension should include the .)
914 src = path + strlen(path) - 1;
916 while (*src != '/' && src != path)
919 return; // it has an extension
923 strcat (path, extension);
931 Parse a token out of a string
934 char *COM_Parse (char *data)
947 while ( (c = *data) <= ' ')
950 return NULL; // end of file;
955 if (c=='/' && data[1] == '/')
957 while (*data && *data != '\n')
963 // handle quoted strings specially
980 // parse single characters
981 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
989 // parse a regular word
996 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1009 Returns the position (1 to argc-1) in the program's argument list
1010 where the given parameter apears, or 0 if not present
1013 int COM_CheckParm (char *parm)
1017 for (i=1 ; i<com_argc ; i++)
1020 continue; // NEXTSTEP sometimes clears appkit vars.
1021 if (!strcmp (parm,com_argv[i]))
1032 Looks for the pop.txt file and verifies it.
1033 Sets the "registered" cvar.
1034 Immediately exits out if an alternate game was attempted to be started without
1038 void COM_CheckRegistered (void)
1040 Cvar_Set ("cmdline", com_cmdline);
1042 // static_registered = 0;
1044 if (!Sys_FileTime("gfx/pop.lmp"))
1047 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1049 Con_Printf ("Playing shareware version.\n");
1051 // Sys_Error ("This dedicated server requires a full registered copy of Quake");
1053 // Con_Printf ("Playing shareware version.\n");
1054 // if (com_modified)
1055 // Sys_Error ("You must have the registered version to use modified games");
1059 // Cvar_Set ("cmdline", com_cmdline);
1060 Cvar_Set ("registered", "1");
1061 // static_registered = 1;
1062 Con_Printf ("Playing registered version.\n");
1066 void COM_Path_f (void);
1074 void COM_InitArgv (int argc, char **argv)
1079 // reconstitute the command line for the cmdline externally visible cvar
1082 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1086 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1088 com_cmdline[n++] = argv[j][i++];
1091 if (n < (CMDLINE_LENGTH - 1))
1092 com_cmdline[n++] = ' ';
1101 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1104 largv[com_argc] = argv[com_argc];
1105 if (!strcmp ("-safe", argv[com_argc]))
1111 // force all the safe-mode switches. Note that we reserved extra space in
1112 // case we need to add these, so we don't need an overflow check
1113 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1115 largv[com_argc] = safeargvs[i];
1120 largv[com_argc] = argvdummy;
1125 standard_quake = false;
1127 if (COM_CheckParm ("-rogue"))
1130 standard_quake = false;
1133 if (COM_CheckParm ("-hipnotic"))
1136 standard_quake = false;
1139 if (COM_CheckParm ("-nehahra"))
1142 standard_quake = false;
1148 unsigned int qmalloctotal_alloc, qmalloctotal_alloccount, qmalloctotal_free, qmalloctotal_freecount;
1150 void *qmalloc(unsigned int size)
1153 qmalloctotal_alloc += size;
1154 qmalloctotal_alloccount++;
1155 mem = malloc(size+sizeof(unsigned int));
1159 return (void *)(mem + 1);
1162 void qfree(void *mem)
1168 m--; // back up to size
1169 qmalloctotal_free += *m; // size
1170 qmalloctotal_freecount++;
1174 extern void GL_TextureStats_PrintTotal(void);
1175 extern int hunk_low_used, hunk_high_used, hunk_size;
1176 void COM_Memstats_f(void)
1178 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);
1179 GL_TextureStats_PrintTotal();
1180 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);
1184 extern void Mathlib_Init(void);
1191 void COM_Init (char *basedir)
1193 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
1194 byte swaptest[2] = {1,0};
1196 // set the byte swapping variables in a portable manner
1197 if ( *(short *)swaptest == 1)
1199 BigShort = ShortSwap;
1200 LittleShort = ShortNoSwap;
1202 LittleLong = LongNoSwap;
1203 BigFloat = FloatSwap;
1204 LittleFloat = FloatNoSwap;
1208 BigShort = ShortNoSwap;
1209 LittleShort = ShortSwap;
1210 BigLong = LongNoSwap;
1211 LittleLong = LongSwap;
1212 BigFloat = FloatNoSwap;
1213 LittleFloat = FloatSwap;
1217 Cvar_RegisterVariable (®istered);
1218 Cvar_RegisterVariable (&cmdline);
1219 Cmd_AddCommand ("path", COM_Path_f);
1220 Cmd_AddCommand ("memstats", COM_Memstats_f);
1224 COM_InitFilesystem ();
1225 COM_CheckRegistered ();
1233 does a varargs printf into a temp buffer, so I don't need to have
1234 varargs versions of all text functions.
1235 FIXME: make this buffer size safe someday
1238 char *va(char *format, ...)
1241 static char string[1024];
1243 va_start (argptr, format);
1244 vsprintf (string, format,argptr);
1251 /// just for debugging
1252 int memsearch (byte *start, int count, int search)
1256 for (i=0 ; i<count ; i++)
1257 if (start[i] == search)
1263 =============================================================================
1267 =============================================================================
1279 char name[MAX_QPATH];
1280 int filepos, filelen;
1283 typedef struct pack_s
1285 char filename[MAX_OSPATH];
1297 int filepos, filelen;
1307 // LordHavoc: was 2048, increased to 16384 and changed info[MAX_PACK_FILES] to a temporary malloc to avoid stack overflows
1308 #define MAX_FILES_IN_PACK 16384
1311 char com_cachedir[MAX_OSPATH];
1313 char com_gamedir[MAX_OSPATH];
1315 typedef struct searchpath_s
1317 char filename[MAX_OSPATH];
1318 pack_t *pack; // only one of filename / pack will be used
1319 struct searchpath_s *next;
1322 searchpath_t *com_searchpaths;
1330 void COM_Path_f (void)
1334 Con_Printf ("Current search path:\n");
1335 for (s=com_searchpaths ; s ; s=s->next)
1339 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1342 Con_Printf ("%s\n", s->filename);
1350 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1353 void COM_CreatePath (char *path)
1357 for (ofs = path+1 ; *ofs ; ofs++)
1359 if (*ofs == '/' || *ofs == '\\' || *ofs == ':')
1360 { // create the directory
1374 The filename will be prefixed by the current game directory
1377 void COM_WriteFile (char *filename, void *data, int len)
1380 char name[MAX_OSPATH];
1382 sprintf (name, "%s/%s", com_gamedir, filename);
1384 // LordHavoc: added this
1385 COM_CreatePath (name); // create directories up to the file
1387 handle = Sys_FileOpenWrite (name);
1390 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1394 Sys_Printf ("COM_WriteFile: %s\n", name);
1395 Sys_FileWrite (handle, data, len);
1396 Sys_FileClose (handle);
1404 Copies a file over from the net to the local cache, creating any directories
1405 needed. This is for the convenience of developers using ISDN from home.
1408 void COM_CopyFile (char *netpath, char *cachepath)
1411 int remaining, count;
1414 remaining = Sys_FileOpenRead (netpath, &in);
1415 COM_CreatePath (cachepath); // create directories up to the cache file
1416 out = Sys_FileOpenWrite (cachepath);
1420 if (remaining < sizeof(buf))
1423 count = sizeof(buf);
1424 Sys_FileRead (in, buf, count);
1425 Sys_FileWrite (out, buf, count);
1430 Sys_FileClose (out);
1438 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1440 int fd = open (path, O_RDONLY);
1441 unsigned char id[2];
1442 unsigned char len_bytes[4];
1446 Sys_Error ("Couldn't open %s", path);
1449 if (offs < 0 || len < 0)
1453 len = lseek (fd, 0, SEEK_END);
1454 lseek (fd, 0, SEEK_SET);
1456 lseek (fd, offs, SEEK_SET);
1460 if (id[0] == 0x1f && id[1] == 0x8b)
1462 lseek (fd, offs + len - 4, SEEK_SET);
1463 read (fd, len_bytes, 4);
1464 len = ((len_bytes[3] << 24)
1465 | (len_bytes[2] << 16)
1466 | (len_bytes[1] << 8)
1470 lseek (fd, offs, SEEK_SET);
1474 setmode (fd, O_BINARY);
1477 return Qdopen (fd, "rbz");
1479 return Qdopen (fd, "rb");
1486 Finds the file in the search path.
1487 Sets com_filesize and one of handle or file
1490 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1492 searchpath_t *search;
1493 char netpath[MAX_OSPATH];
1495 char cachepath[MAX_OSPATH];
1501 char gzfilename[MAX_OSPATH];
1504 filenamelen = strlen (filename);
1505 sprintf (gzfilename, "%s.gz", filename);
1508 Sys_Error ("COM_FindFile: file not set");
1511 // search through the path, one element at a time
1513 search = com_searchpaths;
1515 // { // gross hack to use quake 1 progs with quake 2 maps
1516 // if (!strcmp(filename, "progs.dat"))
1517 // search = search->next;
1520 for ( ; search ; search = search->next)
1522 // is the element a pak file?
1525 // look through all the pak file elements
1527 for (i=0 ; i<pak->numfiles ; i++)
1528 if (!strcmp (pak->files[i].name, filename)
1529 || !strcmp (pak->files[i].name, gzfilename))
1532 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1533 // open a new file on the pakfile
1534 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1535 return com_filesize;
1540 // check a file in the directory tree
1541 // if (!static_registered)
1542 // { // if not a registered version, don't ever go beyond base
1543 // if ( strchr (filename, '/') || strchr (filename,'\\'))
1547 sprintf (netpath, "%s/%s",search->filename, filename);
1549 findtime = Sys_FileTime (netpath);
1554 // see if the file needs to be updated in the cache
1555 if (com_cachedir[0])
1558 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1559 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1561 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1563 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1566 cachetime = Sys_FileTime (cachepath);
1568 if (cachetime < findtime)
1569 COM_CopyFile (netpath, cachepath);
1570 strcpy (netpath, cachepath);
1575 Sys_Printf ("FindFile: %s\n",netpath);
1576 *file = COM_OpenRead (netpath, -1, -1, zip);
1577 return com_filesize;
1583 Sys_Printf ("FindFile: can't find %s\n", filename);
1595 If the requested file is inside a packfile, a new QFile * will be opened
1599 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1601 return COM_FindFile (filename, file, quiet, zip);
1609 Filename are reletive to the quake directory.
1610 Always appends a 0 byte.
1613 cache_user_t *loadcache;
1616 byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
1623 buf = NULL; // quiet compiler warning
1626 // look for it in the filesystem or pack files
1627 len = COM_FOpenFile (path, &h, quiet, true);
1633 // extract the filename base name for hunk tag
1634 COM_FileBase (path, base);
1639 buf = Hunk_AllocName (len+1, va("%s (file)", path));
1641 Sys_Error ("COM_LoadFile: not enough hunk space for %s (size %i)", path, len);
1644 // buf = Z_Malloc (len+1);
1646 // Sys_Error ("COM_LoadFile: not enough zone space for %s (size %i)", path, len);
1649 // buf = Cache_Alloc (loadcache, len+1, base);
1651 // Sys_Error ("COM_LoadFile: not enough cache space for %s (size %i)", path, len);
1654 buf = qmalloc (len+1);
1656 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1659 Sys_Error ("COM_LoadFile: bad usehunk");
1663 ((byte *)buf)[len] = 0;
1665 Qread (h, buf, len);
1671 byte *COM_LoadHunkFile (char *path, qboolean quiet)
1673 return COM_LoadFile (path, 1, quiet);
1676 // LordHavoc: returns malloc'd memory
1677 byte *COM_LoadMallocFile (char *path, qboolean quiet)
1679 return COM_LoadFile (path, 5, quiet);
1683 void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
1686 COM_LoadFile (path, 3, quiet);
1694 Takes an explicit (not game tree related) path to a pak file.
1696 Loads the header and directory, adding the files at the beginning
1697 of the list so they override previous pack files.
1700 pack_t *COM_LoadPackFile (char *packfile)
1702 dpackheader_t header;
1704 packfile_t *newfiles;
1708 // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
1711 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1713 // Con_Printf ("Couldn't open %s\n", packfile);
1716 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1717 if (header.id[0] != 'P' || header.id[1] != 'A'
1718 || header.id[2] != 'C' || header.id[3] != 'K')
1719 Sys_Error ("%s is not a packfile", packfile);
1720 header.dirofs = LittleLong (header.dirofs);
1721 header.dirlen = LittleLong (header.dirlen);
1723 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1725 if (numpackfiles > MAX_FILES_IN_PACK)
1726 Sys_Error ("%s has %i files", packfile, numpackfiles);
1728 newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "pack file-table");
1730 info = qmalloc(sizeof(*info)*MAX_FILES_IN_PACK);
1731 Sys_FileSeek (packhandle, header.dirofs);
1732 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1734 // parse the directory
1735 for (i=0 ; i<numpackfiles ; i++)
1737 strcpy (newfiles[i].name, info[i].name);
1738 newfiles[i].filepos = LittleLong(info[i].filepos);
1739 newfiles[i].filelen = LittleLong(info[i].filelen);
1743 pack = Hunk_AllocName (sizeof (pack_t), packfile);
1744 strcpy (pack->filename, packfile);
1745 pack->handle = packhandle;
1746 pack->numfiles = numpackfiles;
1747 pack->files = newfiles;
1749 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1756 COM_AddGameDirectory
1758 Sets com_gamedir, adds the directory to the head of the path,
1759 then loads and adds pak1.pak pak2.pak ...
1762 void COM_AddGameDirectory (char *dir)
1765 searchpath_t *search;
1767 char pakfile[MAX_OSPATH];
1769 strcpy (com_gamedir, dir);
1772 // add the directory to the search path
1774 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1775 strcpy (search->filename, dir);
1776 search->next = com_searchpaths;
1777 com_searchpaths = search;
1780 // add any pak files in the format pak0.pak pak1.pak, ...
1784 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1785 pak = COM_LoadPackFile (pakfile);
1788 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1790 search->next = com_searchpaths;
1791 com_searchpaths = search;
1795 // add the contents of the parms.txt file to the end of the command line
1805 void COM_InitFilesystem (void)
1808 char basedir[MAX_OSPATH];
1809 searchpath_t *search;
1813 // Overrides the system supplied base directory (under GAMENAME)
1815 i = COM_CheckParm ("-basedir");
1816 if (i && i < com_argc-1)
1817 strcpy (basedir, com_argv[i+1]);
1819 strcpy (basedir, host_parms.basedir);
1821 j = strlen (basedir);
1825 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1832 // Overrides the system supplied cache directory (NULL or /qcache)
1833 // -cachedir - will disable caching.
1835 i = COM_CheckParm ("-cachedir");
1836 if (i && i < com_argc-1)
1838 if (com_argv[i+1][0] == '-')
1839 com_cachedir[0] = 0;
1841 strcpy (com_cachedir, com_argv[i+1]);
1843 else if (host_parms.cachedir)
1844 strcpy (com_cachedir, host_parms.cachedir);
1846 com_cachedir[0] = 0;
1850 // start up with GAMENAME by default (id1)
1852 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1855 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1857 if (COM_CheckParm ("-rogue"))
1858 COM_AddGameDirectory (va("%s/rogue", basedir) );
1859 if (COM_CheckParm ("-hipnotic"))
1860 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1861 if (COM_CheckParm ("-nehahra"))
1862 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1867 // Adds basedir/gamedir as an override game
1869 i = COM_CheckParm ("-game");
1870 if (i && i < com_argc-1)
1872 com_modified = true;
1873 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1877 // -path <dir or packfile> [<dir or packfile>] ...
1878 // Fully specifies the exact search path, overriding the generated one
1880 i = COM_CheckParm ("-path");
1883 com_modified = true;
1884 com_searchpaths = NULL;
1885 while (++i < com_argc)
1887 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1890 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1891 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1893 search->pack = COM_LoadPackFile (com_argv[i]);
1895 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1898 strcpy (search->filename, com_argv[i]);
1899 search->next = com_searchpaths;
1900 com_searchpaths = search;
1904 // if (COM_CheckParm ("-proghack"))
1908 int COM_FileExists(char *filename)
1910 searchpath_t *search;
1911 char netpath[MAX_OSPATH];
1916 for (search = com_searchpaths;search;search = search->next)
1921 for (i = 0;i < pak->numfiles;i++)
1922 if (!strcmp (pak->files[i].name, filename))
1927 sprintf (netpath, "%s/%s",search->filename, filename);
1928 findtime = Sys_FileTime (netpath);
1938 //======================================
1939 // LordHavoc: added these because they are useful
1941 void COM_ToLowerString(char *in, char *out)
1945 if (*in >= 'A' && *in <= 'Z')
1946 *out++ = *in++ + 'a' - 'A';
1952 void COM_ToUpperString(char *in, char *out)
1956 if (*in >= 'a' && *in <= 'z')
1957 *out++ = *in++ + 'A' - 'a';