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
30 #define NUM_SAFE_ARGVS 7
32 static char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
33 static char *argvdummy = " ";
35 static char *safeargvs[NUM_SAFE_ARGVS] =
36 {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"};
38 cvar_t registered = {"registered","0"};
39 cvar_t cmdline = {"cmdline","0", false, true};
41 qboolean com_modified; // set true if using non-id files
45 int static_registered = 1; // only for startup check, then set
47 qboolean msg_suppress_1 = 0;
49 void COM_InitFilesystem (void);
51 // if a packfile directory differs from this, it is assumed to be hacked
52 #define PAK0_COUNT 339
53 #define PAK0_CRC 32981
59 #define CMDLINE_LENGTH 256
60 char com_cmdline[CMDLINE_LENGTH];
62 qboolean standard_quake = true, rogue = false, hipnotic = false, nehahra = false;
64 // this graphic needs to be in the pak file to use registered features
65 unsigned short pop[] =
67 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
68 ,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000
69 ,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000
70 ,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600
71 ,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563
72 ,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564
73 ,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564
74 ,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563
75 ,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500
76 ,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200
77 ,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000
78 ,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000
79 ,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000
80 ,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000
81 ,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000
82 ,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000
88 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.
90 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
91 only used during filesystem initialization.
93 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.
95 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
96 specified, when a file is found by the normal search path, it will be mirrored
97 into the cache directory, then opened there.
102 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.
106 //============================================================================
109 // ClearLink is used for new headnodes
110 void ClearLink (link_t *l)
112 l->prev = l->next = l;
115 void RemoveLink (link_t *l)
117 l->next->prev = l->prev;
118 l->prev->next = l->next;
121 void InsertLinkBefore (link_t *l, link_t *before)
124 l->prev = before->prev;
128 void InsertLinkAfter (link_t *l, link_t *after)
130 l->next = after->next;
137 ============================================================================
139 LIBRARY REPLACEMENT FUNCTIONS
141 ============================================================================
145 void Q_memset (void *dest, int fill, int count)
149 if ( (((long)dest | count) & 3) == 0)
152 fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
153 for (i=0 ; i<count ; i++)
154 ((int *)dest)[i] = fill;
157 for (i=0 ; i<count ; i++)
158 ((byte *)dest)[i] = fill;
161 void Q_memcpy (void *dest, void *src, int count)
165 if (( ( (long)dest | (long)src | count) & 3) == 0 )
168 for (i=0 ; i<count ; i++)
169 ((int *)dest)[i] = ((int *)src)[i];
172 for (i=0 ; i<count ; i++)
173 ((byte *)dest)[i] = ((byte *)src)[i];
176 int Q_memcmp (void *m1, void *m2, int count)
181 if (((byte *)m1)[count] != ((byte *)m2)[count])
187 void Q_strcpy (char *dest, char *src)
196 void Q_strncpy (char *dest, char *src, int count)
198 while (*src && count--)
206 int Q_strlen (char *str)
217 char *Q_strrchr(char *s, char c)
219 int len = Q_strlen(s);
222 if (*--s == c) return s;
226 void Q_strcat (char *dest, char *src)
228 dest += Q_strlen(dest);
229 Q_strcpy (dest, src);
232 int Q_strcmp (char *s1, char *s2)
237 return -1; // strings not equal
239 return 0; // strings are equal
247 int Q_strncmp (char *s1, char *s2, int count)
254 return -1; // strings not equal
256 return 0; // strings are equal
264 int Q_strncasecmp (char *s1, char *s2, int n)
274 return 0; // strings are equal until end point
278 if (c1 >= 'a' && c1 <= 'z')
280 if (c2 >= 'a' && c2 <= 'z')
283 return -1; // strings not equal
286 return 0; // strings are equal
294 int Q_strcasecmp (char *s1, char *s2)
296 return Q_strncasecmp (s1, s2, 99999);
299 int Q_atoi (char *str)
318 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
324 if (c >= '0' && c <= '9')
325 val = (val<<4) + c - '0';
326 else if (c >= 'a' && c <= 'f')
327 val = (val<<4) + c - 'a' + 10;
328 else if (c >= 'A' && c <= 'F')
329 val = (val<<4) + c - 'A' + 10;
336 // check for character
340 return sign * str[1];
349 if (c <'0' || c > '9')
351 val = val*10 + c - '0';
358 float Q_atof (char *str)
378 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
384 if (c >= '0' && c <= '9')
385 val = (val*16) + c - '0';
386 else if (c >= 'a' && c <= 'f')
387 val = (val*16) + c - 'a' + 10;
388 else if (c >= 'A' && c <= 'F')
389 val = (val*16) + c - 'A' + 10;
396 // check for character
400 return sign * str[1];
416 if (c <'0' || c > '9')
418 val = val*10 + c - '0';
424 while (total > decimal)
435 ============================================================================
439 ============================================================================
443 short (*BigShort) (short l);
444 short (*LittleShort) (short l);
445 int (*BigLong) (int l);
446 int (*LittleLong) (int l);
447 float (*BigFloat) (float l);
448 float (*LittleFloat) (float l);
451 short ShortSwap (short l)
461 short ShortNoSwap (short l)
475 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
478 int LongNoSwap (int l)
483 float FloatSwap (float f)
493 dat2.b[0] = dat1.b[3];
494 dat2.b[1] = dat1.b[2];
495 dat2.b[2] = dat1.b[1];
496 dat2.b[3] = dat1.b[0];
500 float FloatNoSwap (float f)
506 ==============================================================================
510 Handles byte ordering and avoids alignment errors
511 ==============================================================================
518 void MSG_WriteChar (sizebuf_t *sb, int c)
523 // if (c < -128 || c > 127)
524 // Sys_Error ("MSG_WriteChar: range error");
527 buf = SZ_GetSpace (sb, 1);
531 void MSG_WriteByte (sizebuf_t *sb, int c)
536 // if (c < 0 || c > 255)
537 // Sys_Error ("MSG_WriteByte: range error");
540 buf = SZ_GetSpace (sb, 1);
544 void MSG_WriteShort (sizebuf_t *sb, int c)
549 // if (c < ((short)0x8000) || c > (short)0x7fff)
550 // Sys_Error ("MSG_WriteShort: range error");
553 buf = SZ_GetSpace (sb, 2);
558 void MSG_WriteLong (sizebuf_t *sb, int c)
562 buf = SZ_GetSpace (sb, 4);
564 buf[1] = (c>>8)&0xff;
565 buf[2] = (c>>16)&0xff;
569 void MSG_WriteFloat (sizebuf_t *sb, float f)
579 dat.l = LittleLong (dat.l);
581 SZ_Write (sb, &dat.l, 4);
584 void MSG_WriteString (sizebuf_t *sb, char *s)
587 SZ_Write (sb, "", 1);
589 SZ_Write (sb, s, strlen(s)+1);
592 // used by server (always dpprotocol)
593 // moved to common.h as #define
595 void MSG_WriteFloatCoord (sizebuf_t *sb, float f)
597 MSG_WriteFloat(sb, f);
602 void MSG_WriteCoord (sizebuf_t *sb, float f)
605 MSG_WriteFloat(sb, f);
607 MSG_WriteShort (sb, (int)(f*8.0f + 0.5f));
610 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
612 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
615 // LordHavoc: round to nearest value, rather than rounding down, fixes crosshair problem
616 void MSG_WriteAngle (sizebuf_t *sb, float f)
618 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
625 qboolean msg_badread;
627 void MSG_BeginReading (void)
634 // returns -1 and sets msg_badread if no more characters are available
635 int MSG_ReadChar (void)
639 // LordHavoc: minor optimization
640 if (msg_readcount >= net_message.cursize)
641 // if (msg_readcount+1 > net_message.cursize)
647 c = (signed char)net_message.data[msg_readcount];
653 int MSG_ReadByte (void)
657 // LordHavoc: minor optimization
658 if (msg_readcount >= net_message.cursize)
659 // if (msg_readcount+1 > net_message.cursize)
665 c = (unsigned char)net_message.data[msg_readcount];
672 int MSG_ReadShort (void)
676 if (msg_readcount+2 > net_message.cursize)
682 c = (short)(net_message.data[msg_readcount]
683 + (net_message.data[msg_readcount+1]<<8));
690 int MSG_ReadLong (void)
694 if (msg_readcount+4 > net_message.cursize)
700 c = net_message.data[msg_readcount]
701 + (net_message.data[msg_readcount+1]<<8)
702 + (net_message.data[msg_readcount+2]<<16)
703 + (net_message.data[msg_readcount+3]<<24);
710 float MSG_ReadFloat (void)
719 dat.b[0] = net_message.data[msg_readcount];
720 dat.b[1] = net_message.data[msg_readcount+1];
721 dat.b[2] = net_message.data[msg_readcount+2];
722 dat.b[3] = net_message.data[msg_readcount+3];
725 dat.l = LittleLong (dat.l);
730 char *MSG_ReadString (void)
732 static char string[2048];
739 if (c == -1 || c == 0)
743 } while (l < sizeof(string)-1);
750 // used by server (always dpprotocol)
751 // moved to common.h as #define
753 float MSG_ReadFloatCoord (void)
755 return MSG_ReadFloat();
760 float MSG_ReadCoord (void)
763 return MSG_ReadFloat();
765 return MSG_ReadShort() * (1.0f/8.0f);
769 float MSG_ReadCoord (void)
771 return MSG_ReadShort() * (1.0f/8.0f);
774 float MSG_ReadAngle (void)
776 return MSG_ReadChar() * (360.0f/256.0f);
779 float MSG_ReadPreciseAngle (void)
781 return MSG_ReadShort() * (360.0f/65536);
786 //===========================================================================
788 void SZ_Alloc (sizebuf_t *buf, int startsize)
792 buf->data = Hunk_AllocName (startsize, "sizebuf");
793 buf->maxsize = startsize;
798 void SZ_Free (sizebuf_t *buf)
800 // Z_Free (buf->data);
806 void SZ_Clear (sizebuf_t *buf)
811 void *SZ_GetSpace (sizebuf_t *buf, int length)
815 if (buf->cursize + length > buf->maxsize)
817 if (!buf->allowoverflow)
818 Host_Error ("SZ_GetSpace: overflow without allowoverflow set - use -zone on the commandline for more zone memory, default: 128k (quake original default was 48k)");
820 if (length > buf->maxsize)
821 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
823 buf->overflowed = true;
824 Con_Printf ("SZ_GetSpace: overflow");
828 data = buf->data + buf->cursize;
829 buf->cursize += length;
834 void SZ_Write (sizebuf_t *buf, void *data, int length)
836 memcpy (SZ_GetSpace(buf,length),data,length);
839 void SZ_Print (sizebuf_t *buf, char *data)
843 len = strlen(data)+1;
845 // byte * cast to keep VC++ happy
846 if (buf->data[buf->cursize-1])
847 memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
849 memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
853 //============================================================================
861 char *COM_SkipPath (char *pathname)
880 void COM_StripExtension (char *in, char *out)
882 while (*in && *in != '.')
892 char *COM_FileExtension (char *in)
894 static char exten[8];
897 while (*in && *in != '.')
902 for (i=0 ; i<7 && *in ; i++,in++)
913 void COM_FileBase (char *in, char *out)
932 strcpy (out,"?model?");
947 void COM_DefaultExtension (char *path, char *extension)
951 // if path doesn't have a .EXT, append extension
952 // (extension should include the .)
954 src = path + strlen(path) - 1;
956 while (*src != '/' && src != path)
959 return; // it has an extension
963 strcat (path, extension);
971 Parse a token out of a string
974 char *COM_Parse (char *data)
987 while ( (c = *data) <= ' ')
990 return NULL; // end of file;
995 if (c=='/' && data[1] == '/')
997 while (*data && *data != '\n')
1003 // handle quoted strings specially
1020 // parse single characters
1021 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1029 // parse a regular word
1036 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1049 Returns the position (1 to argc-1) in the program's argument list
1050 where the given parameter apears, or 0 if not present
1053 int COM_CheckParm (char *parm)
1057 for (i=1 ; i<com_argc ; i++)
1060 continue; // NEXTSTEP sometimes clears appkit vars.
1061 if (!strcmp (parm,com_argv[i]))
1072 Looks for the pop.txt file and verifies it.
1073 Sets the "registered" cvar.
1074 Immediately exits out if an alternate game was attempted to be started without
1078 void COM_CheckRegistered (void)
1081 unsigned short check[128];
1084 Cvar_Set ("cmdline", com_cmdline);
1086 COM_FOpenFile("gfx/pop.lmp", &h, false, true);
1087 static_registered = 0;
1092 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1094 Con_Printf ("Playing shareware version.\n");
1096 // Sys_Error ("This dedicated server requires a full registered copy of Quake");
1098 // Con_Printf ("Playing shareware version.\n");
1099 // if (com_modified)
1100 // Sys_Error ("You must have the registered version to use modified games");
1104 Qread (h, check, sizeof(check));
1107 for (i=0 ; i<128 ; i++)
1108 if (pop[i] != (unsigned short)BigShort (check[i]))
1109 Sys_Error ("Corrupted data file.");
1111 // Cvar_Set ("cmdline", com_cmdline);
1112 Cvar_Set ("registered", "1");
1113 static_registered = 1;
1114 Con_Printf ("Playing registered version.\n");
1118 void COM_Path_f (void);
1126 void COM_InitArgv (int argc, char **argv)
1131 // reconstitute the command line for the cmdline externally visible cvar
1134 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1138 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1140 com_cmdline[n++] = argv[j][i++];
1143 if (n < (CMDLINE_LENGTH - 1))
1144 com_cmdline[n++] = ' ';
1153 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1156 largv[com_argc] = argv[com_argc];
1157 if (!strcmp ("-safe", argv[com_argc]))
1163 // force all the safe-mode switches. Note that we reserved extra space in
1164 // case we need to add these, so we don't need an overflow check
1165 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1167 largv[com_argc] = safeargvs[i];
1172 largv[com_argc] = argvdummy;
1177 standard_quake = false;
1179 if (COM_CheckParm ("-rogue"))
1182 standard_quake = false;
1185 if (COM_CheckParm ("-hipnotic"))
1188 standard_quake = false;
1191 if (COM_CheckParm ("-nehahra"))
1194 standard_quake = false;
1200 unsigned int qmalloctotal_alloc, qmalloctotal_alloccount, qmalloctotal_free, qmalloctotal_freecount;
1202 void *qmalloc(unsigned int size)
1205 qmalloctotal_alloc += size;
1206 qmalloctotal_alloccount++;
1207 mem = malloc(size+sizeof(unsigned int));
1211 return (void *)(mem + 1);
1214 void qfree(void *mem)
1220 m--; // back up to size
1221 qmalloctotal_free += *m; // size
1222 qmalloctotal_freecount++;
1226 extern void GL_TextureStats_PrintTotal(void);
1227 extern int hunk_low_used, hunk_high_used, hunk_size;
1228 void COM_Memstats_f(void)
1230 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);
1231 GL_TextureStats_PrintTotal();
1232 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);
1241 void COM_Init (char *basedir)
1244 byte swaptest[2] = {1,0};
1246 // set the byte swapping variables in a portable manner
1247 if ( *(short *)swaptest == 1)
1249 BigShort = ShortSwap;
1250 LittleShort = ShortNoSwap;
1252 LittleLong = LongNoSwap;
1253 BigFloat = FloatSwap;
1254 LittleFloat = FloatNoSwap;
1258 BigShort = ShortNoSwap;
1259 LittleShort = ShortSwap;
1260 BigLong = LongNoSwap;
1261 LittleLong = LongSwap;
1262 BigFloat = FloatNoSwap;
1263 LittleFloat = FloatSwap;
1267 Cvar_RegisterVariable (®istered);
1268 Cvar_RegisterVariable (&cmdline);
1269 Cmd_AddCommand ("path", COM_Path_f);
1270 Cmd_AddCommand ("memstats", COM_Memstats_f);
1272 COM_InitFilesystem ();
1273 COM_CheckRegistered ();
1281 does a varargs printf into a temp buffer, so I don't need to have
1282 varargs versions of all text functions.
1283 FIXME: make this buffer size safe someday
1286 char *va(char *format, ...)
1289 static char string[1024];
1291 va_start (argptr, format);
1292 vsprintf (string, format,argptr);
1299 /// just for debugging
1300 int memsearch (byte *start, int count, int search)
1304 for (i=0 ; i<count ; i++)
1305 if (start[i] == search)
1311 =============================================================================
1315 =============================================================================
1327 char name[MAX_QPATH];
1328 int filepos, filelen;
1331 typedef struct pack_s
1333 char filename[MAX_OSPATH];
1345 int filepos, filelen;
1355 // LordHavoc: was 2048, increased to 16384 and changed info[MAX_PACK_FILES] to a temporary malloc to avoid stack overflows
1356 #define MAX_FILES_IN_PACK 16384
1359 char com_cachedir[MAX_OSPATH];
1361 char com_gamedir[MAX_OSPATH];
1363 typedef struct searchpath_s
1365 char filename[MAX_OSPATH];
1366 pack_t *pack; // only one of filename / pack will be used
1367 struct searchpath_s *next;
1370 searchpath_t *com_searchpaths;
1378 void COM_Path_f (void)
1382 Con_Printf ("Current search path:\n");
1383 for (s=com_searchpaths ; s ; s=s->next)
1387 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1390 Con_Printf ("%s\n", s->filename);
1398 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1401 void COM_CreatePath (char *path)
1405 for (ofs = path+1 ; *ofs ; ofs++)
1407 if (*ofs == '/' || *ofs == '\\' || *ofs == ':')
1408 { // create the directory
1422 The filename will be prefixed by the current game directory
1425 void COM_WriteFile (char *filename, void *data, int len)
1428 char name[MAX_OSPATH];
1430 sprintf (name, "%s/%s", com_gamedir, filename);
1432 // LordHavoc: added this
1433 COM_CreatePath (name); // create directories up to the file
1435 handle = Sys_FileOpenWrite (name);
1438 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1442 Sys_Printf ("COM_WriteFile: %s\n", name);
1443 Sys_FileWrite (handle, data, len);
1444 Sys_FileClose (handle);
1452 Copies a file over from the net to the local cache, creating any directories
1453 needed. This is for the convenience of developers using ISDN from home.
1456 void COM_CopyFile (char *netpath, char *cachepath)
1459 int remaining, count;
1462 remaining = Sys_FileOpenRead (netpath, &in);
1463 COM_CreatePath (cachepath); // create directories up to the cache file
1464 out = Sys_FileOpenWrite (cachepath);
1468 if (remaining < sizeof(buf))
1471 count = sizeof(buf);
1472 Sys_FileRead (in, buf, count);
1473 Sys_FileWrite (out, buf, count);
1478 Sys_FileClose (out);
1486 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1488 int fd = open (path, O_RDONLY);
1489 unsigned char id[2];
1490 unsigned char len_bytes[4];
1494 Sys_Error ("Couldn't open %s", path);
1497 if (offs < 0 || len < 0)
1501 len = lseek (fd, 0, SEEK_END);
1502 lseek (fd, 0, SEEK_SET);
1504 lseek (fd, offs, SEEK_SET);
1508 if (id[0] == 0x1f && id[1] == 0x8b)
1510 lseek (fd, offs + len - 4, SEEK_SET);
1511 read (fd, len_bytes, 4);
1512 len = ((len_bytes[3] << 24)
1513 | (len_bytes[2] << 16)
1514 | (len_bytes[1] << 8)
1518 lseek (fd, offs, SEEK_SET);
1522 setmode (fd, O_BINARY);
1525 return Qdopen (fd, "rbz");
1527 return Qdopen (fd, "rb");
1534 Finds the file in the search path.
1535 Sets com_filesize and one of handle or file
1538 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1540 searchpath_t *search;
1541 char netpath[MAX_OSPATH];
1543 char cachepath[MAX_OSPATH];
1549 char gzfilename[MAX_OSPATH];
1552 filenamelen = strlen (filename);
1553 snprintf (gzfilename, sizeof (gzfilename), "%s.gz", filename);
1556 Sys_Error ("COM_FindFile: file not set");
1559 // search through the path, one element at a time
1561 search = com_searchpaths;
1563 { // gross hack to use quake 1 progs with quake 2 maps
1564 if (!strcmp(filename, "progs.dat"))
1565 search = search->next;
1568 for ( ; search ; search = search->next)
1570 // is the element a pak file?
1573 // look through all the pak file elements
1575 for (i=0 ; i<pak->numfiles ; i++)
1576 if (!strcmp (pak->files[i].name, filename)
1577 || !strcmp (pak->files[i].name, gzfilename))
1580 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1581 // open a new file on the pakfile
1582 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1583 return com_filesize;
1588 // check a file in the directory tree
1589 // if (!static_registered)
1590 // { // if not a registered version, don't ever go beyond base
1591 // if ( strchr (filename, '/') || strchr (filename,'\\'))
1595 sprintf (netpath, "%s/%s",search->filename, filename);
1597 findtime = Sys_FileTime (netpath);
1602 // see if the file needs to be updated in the cache
1603 if (com_cachedir[0])
1606 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1607 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1609 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1611 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1614 cachetime = Sys_FileTime (cachepath);
1616 if (cachetime < findtime)
1617 COM_CopyFile (netpath, cachepath);
1618 strcpy (netpath, cachepath);
1623 Sys_Printf ("FindFile: %s\n",netpath);
1624 *file = COM_OpenRead (netpath, -1, -1, zip);
1625 return com_filesize;
1631 Sys_Printf ("FindFile: can't find %s\n", filename);
1643 If the requested file is inside a packfile, a new QFile * will be opened
1647 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1649 return COM_FindFile (filename, file, quiet, zip);
1657 Filename are reletive to the quake directory.
1658 Always appends a 0 byte.
1661 cache_user_t *loadcache;
1664 byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
1671 buf = NULL; // quiet compiler warning
1673 // look for it in the filesystem or pack files
1674 len = COM_FOpenFile (path, &h, quiet, true);
1678 // extract the filename base name for hunk tag
1679 COM_FileBase (path, base);
1684 buf = Hunk_AllocName (len+1, va("%s (file)", path));
1686 Sys_Error ("COM_LoadFile: not enough hunk space for %s (size %i)", path, len);
1689 // buf = Z_Malloc (len+1);
1691 // Sys_Error ("COM_LoadFile: not enough zone space for %s (size %i)", path, len);
1694 buf = Cache_Alloc (loadcache, len+1, base);
1696 Sys_Error ("COM_LoadFile: not enough cache space for %s (size %i)", path, len);
1699 buf = qmalloc (len+1);
1701 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1704 Sys_Error ("COM_LoadFile: bad usehunk");
1708 ((byte *)buf)[len] = 0;
1710 Qread (h, buf, len);
1716 byte *COM_LoadHunkFile (char *path, qboolean quiet)
1718 return COM_LoadFile (path, 1, quiet);
1721 // LordHavoc: returns malloc'd memory
1722 byte *COM_LoadMallocFile (char *path, qboolean quiet)
1724 return COM_LoadFile (path, 5, quiet);
1727 void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
1730 COM_LoadFile (path, 3, quiet);
1737 Takes an explicit (not game tree related) path to a pak file.
1739 Loads the header and directory, adding the files at the beginning
1740 of the list so they override previous pack files.
1743 pack_t *COM_LoadPackFile (char *packfile)
1745 dpackheader_t header;
1747 packfile_t *newfiles;
1751 // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
1755 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1757 // Con_Printf ("Couldn't open %s\n", packfile);
1760 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1761 if (header.id[0] != 'P' || header.id[1] != 'A'
1762 || header.id[2] != 'C' || header.id[3] != 'K')
1763 Sys_Error ("%s is not a packfile", packfile);
1764 header.dirofs = LittleLong (header.dirofs);
1765 header.dirlen = LittleLong (header.dirlen);
1767 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1769 if (numpackfiles > MAX_FILES_IN_PACK)
1770 Sys_Error ("%s has %i files", packfile, numpackfiles);
1772 if (numpackfiles != PAK0_COUNT)
1773 com_modified = true; // not the original file
1775 newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "pack file-table");
1777 info = qmalloc(sizeof(*info)*MAX_FILES_IN_PACK);
1778 Sys_FileSeek (packhandle, header.dirofs);
1779 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1781 // crc the directory to check for modifications
1783 // LordHavoc: speedup
1784 CRC_ProcessBytes(&crc, (byte *)info, header.dirlen);
1785 // for (i=0 ; i<header.dirlen ; i++)
1786 // CRC_ProcessByte (&crc, ((byte *)info)[i]);
1787 if (crc != PAK0_CRC)
1788 com_modified = true;
1790 // parse the directory
1791 for (i=0 ; i<numpackfiles ; i++)
1793 strcpy (newfiles[i].name, info[i].name);
1794 newfiles[i].filepos = LittleLong(info[i].filepos);
1795 newfiles[i].filelen = LittleLong(info[i].filelen);
1799 pack = Hunk_AllocName (sizeof (pack_t), packfile);
1800 strcpy (pack->filename, packfile);
1801 pack->handle = packhandle;
1802 pack->numfiles = numpackfiles;
1803 pack->files = newfiles;
1805 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1812 COM_AddGameDirectory
1814 Sets com_gamedir, adds the directory to the head of the path,
1815 then loads and adds pak1.pak pak2.pak ...
1818 void COM_AddGameDirectory (char *dir)
1821 searchpath_t *search;
1823 char pakfile[MAX_OSPATH];
1825 strcpy (com_gamedir, dir);
1828 // add the directory to the search path
1830 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1831 strcpy (search->filename, dir);
1832 search->next = com_searchpaths;
1833 com_searchpaths = search;
1836 // add any pak files in the format pak0.pak pak1.pak, ...
1840 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1841 pak = COM_LoadPackFile (pakfile);
1844 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1846 search->next = com_searchpaths;
1847 com_searchpaths = search;
1851 // add the contents of the parms.txt file to the end of the command line
1861 void COM_InitFilesystem (void)
1864 char basedir[MAX_OSPATH];
1865 searchpath_t *search;
1869 // Overrides the system supplied base directory (under GAMENAME)
1871 i = COM_CheckParm ("-basedir");
1872 if (i && i < com_argc-1)
1873 strcpy (basedir, com_argv[i+1]);
1875 strcpy (basedir, host_parms.basedir);
1877 j = strlen (basedir);
1881 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1888 // Overrides the system supplied cache directory (NULL or /qcache)
1889 // -cachedir - will disable caching.
1891 i = COM_CheckParm ("-cachedir");
1892 if (i && i < com_argc-1)
1894 if (com_argv[i+1][0] == '-')
1895 com_cachedir[0] = 0;
1897 strcpy (com_cachedir, com_argv[i+1]);
1899 else if (host_parms.cachedir)
1900 strcpy (com_cachedir, host_parms.cachedir);
1902 com_cachedir[0] = 0;
1906 // start up with GAMENAME by default (id1)
1908 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1911 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1913 if (COM_CheckParm ("-rogue"))
1914 COM_AddGameDirectory (va("%s/rogue", basedir) );
1915 if (COM_CheckParm ("-hipnotic"))
1916 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1917 if (COM_CheckParm ("-nehahra"))
1918 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1923 // Adds basedir/gamedir as an override game
1925 i = COM_CheckParm ("-game");
1926 if (i && i < com_argc-1)
1928 com_modified = true;
1929 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1933 // -path <dir or packfile> [<dir or packfile>] ...
1934 // Fully specifies the exact search path, overriding the generated one
1936 i = COM_CheckParm ("-path");
1939 com_modified = true;
1940 com_searchpaths = NULL;
1941 while (++i < com_argc)
1943 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1946 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1947 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1949 search->pack = COM_LoadPackFile (com_argv[i]);
1951 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1954 strcpy (search->filename, com_argv[i]);
1955 search->next = com_searchpaths;
1956 com_searchpaths = search;
1960 if (COM_CheckParm ("-proghack"))
1964 int COM_FileExists(char *filename)
1966 searchpath_t *search;
1967 char netpath[MAX_OSPATH];
1972 for (search = com_searchpaths;search;search = search->next)
1977 for (i = 0;i < pak->numfiles;i++)
1978 if (!strcmp (pak->files[i].name, filename))
1983 sprintf (netpath, "%s/%s",search->filename, filename);
1984 findtime = Sys_FileTime (netpath);
1994 //======================================
1995 // LordHavoc: added these because they are useful
1997 void COM_ToLowerString(char *in, char *out)
2001 if (*in >= 'A' && *in <= 'Z')
2002 *out++ = *in++ + 'a' - 'A';
2008 void COM_ToUpperString(char *in, char *out)
2012 if (*in >= 'a' && *in <= 'z')
2013 *out++ = *in++ + 'A' - 'a';