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);
53 // if a packfile directory differs from this, it is assumed to be hacked
54 #define PAK0_COUNT 339
55 #define PAK0_CRC 32981
61 #define CMDLINE_LENGTH 256
62 char com_cmdline[CMDLINE_LENGTH];
64 qboolean standard_quake = true, rogue = false, hipnotic = false, nehahra = false;
66 // this graphic needs to be in the pak file to use registered features
67 unsigned short pop[] =
69 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
70 ,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000
71 ,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000
72 ,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600
73 ,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563
74 ,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564
75 ,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564
76 ,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563
77 ,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500
78 ,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200
79 ,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000
80 ,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000
81 ,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000
82 ,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000
83 ,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000
84 ,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000
90 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.
92 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
93 only used during filesystem initialization.
95 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.
97 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
98 specified, when a file is found by the normal search path, it will be mirrored
99 into the cache directory, then opened there.
104 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.
108 //============================================================================
111 // ClearLink is used for new headnodes
112 void ClearLink (link_t *l)
114 l->prev = l->next = l;
117 void RemoveLink (link_t *l)
119 l->next->prev = l->prev;
120 l->prev->next = l->next;
123 void InsertLinkBefore (link_t *l, link_t *before)
126 l->prev = before->prev;
130 void InsertLinkAfter (link_t *l, link_t *after)
132 l->next = after->next;
139 ============================================================================
141 LIBRARY REPLACEMENT FUNCTIONS
143 ============================================================================
147 void Q_memset (void *dest, int fill, int count)
151 if ( (((long)dest | count) & 3) == 0)
154 fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
155 for (i=0 ; i<count ; i++)
156 ((int *)dest)[i] = fill;
159 for (i=0 ; i<count ; i++)
160 ((byte *)dest)[i] = fill;
163 void Q_memcpy (void *dest, void *src, int count)
167 if (( ( (long)dest | (long)src | count) & 3) == 0 )
170 for (i=0 ; i<count ; i++)
171 ((int *)dest)[i] = ((int *)src)[i];
174 for (i=0 ; i<count ; i++)
175 ((byte *)dest)[i] = ((byte *)src)[i];
178 int Q_memcmp (void *m1, void *m2, int count)
183 if (((byte *)m1)[count] != ((byte *)m2)[count])
189 void Q_strcpy (char *dest, char *src)
198 void Q_strncpy (char *dest, char *src, int count)
200 while (*src && count--)
208 int Q_strlen (char *str)
219 char *Q_strrchr(char *s, char c)
221 int len = Q_strlen(s);
224 if (*--s == c) return s;
228 void Q_strcat (char *dest, char *src)
230 dest += Q_strlen(dest);
231 Q_strcpy (dest, src);
234 int Q_strcmp (char *s1, char *s2)
239 return -1; // strings not equal
241 return 0; // strings are equal
249 int Q_strncmp (char *s1, char *s2, int count)
256 return -1; // strings not equal
258 return 0; // strings are equal
266 int Q_strncasecmp (char *s1, char *s2, int n)
276 return 0; // strings are equal until end point
280 if (c1 >= 'a' && c1 <= 'z')
282 if (c2 >= 'a' && c2 <= 'z')
285 return -1; // strings not equal
288 return 0; // strings are equal
296 int Q_strcasecmp (char *s1, char *s2)
298 return Q_strncasecmp (s1, s2, 99999);
301 int Q_atoi (char *str)
320 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
326 if (c >= '0' && c <= '9')
327 val = (val<<4) + c - '0';
328 else if (c >= 'a' && c <= 'f')
329 val = (val<<4) + c - 'a' + 10;
330 else if (c >= 'A' && c <= 'F')
331 val = (val<<4) + c - 'A' + 10;
338 // check for character
342 return sign * str[1];
351 if (c <'0' || c > '9')
353 val = val*10 + c - '0';
360 float Q_atof (char *str)
380 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
386 if (c >= '0' && c <= '9')
387 val = (val*16) + c - '0';
388 else if (c >= 'a' && c <= 'f')
389 val = (val*16) + c - 'a' + 10;
390 else if (c >= 'A' && c <= 'F')
391 val = (val*16) + c - 'A' + 10;
398 // check for character
402 return sign * str[1];
418 if (c <'0' || c > '9')
420 val = val*10 + c - '0';
426 while (total > decimal)
437 ============================================================================
441 ============================================================================
445 short (*BigShort) (short l);
446 short (*LittleShort) (short l);
447 int (*BigLong) (int l);
448 int (*LittleLong) (int l);
449 float (*BigFloat) (float l);
450 float (*LittleFloat) (float l);
453 short ShortSwap (short l)
463 short ShortNoSwap (short l)
477 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
480 int LongNoSwap (int l)
485 float FloatSwap (float f)
495 dat2.b[0] = dat1.b[3];
496 dat2.b[1] = dat1.b[2];
497 dat2.b[2] = dat1.b[1];
498 dat2.b[3] = dat1.b[0];
502 float FloatNoSwap (float f)
508 ==============================================================================
512 Handles byte ordering and avoids alignment errors
513 ==============================================================================
520 void MSG_WriteChar (sizebuf_t *sb, int c)
525 // if (c < -128 || c > 127)
526 // Sys_Error ("MSG_WriteChar: range error");
529 buf = SZ_GetSpace (sb, 1);
533 void MSG_WriteByte (sizebuf_t *sb, int c)
538 // if (c < 0 || c > 255)
539 // Sys_Error ("MSG_WriteByte: range error");
542 buf = SZ_GetSpace (sb, 1);
546 void MSG_WriteShort (sizebuf_t *sb, int c)
551 // if (c < ((short)0x8000) || c > (short)0x7fff)
552 // Sys_Error ("MSG_WriteShort: range error");
555 buf = SZ_GetSpace (sb, 2);
560 void MSG_WriteLong (sizebuf_t *sb, int c)
564 buf = SZ_GetSpace (sb, 4);
566 buf[1] = (c>>8)&0xff;
567 buf[2] = (c>>16)&0xff;
571 void MSG_WriteFloat (sizebuf_t *sb, float f)
581 dat.l = LittleLong (dat.l);
583 SZ_Write (sb, &dat.l, 4);
586 void MSG_WriteString (sizebuf_t *sb, char *s)
589 SZ_Write (sb, "", 1);
591 SZ_Write (sb, s, strlen(s)+1);
594 // used by server (always dpprotocol)
595 // moved to common.h as #define
597 void MSG_WriteFloatCoord (sizebuf_t *sb, float f)
599 MSG_WriteFloat(sb, f);
604 void MSG_WriteCoord (sizebuf_t *sb, float f)
607 MSG_WriteFloat(sb, f);
609 MSG_WriteShort (sb, (int)(f*8.0f + 0.5f));
612 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
614 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
617 // LordHavoc: round to nearest value, rather than rounding down, fixes crosshair problem
618 void MSG_WriteAngle (sizebuf_t *sb, float f)
620 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
627 qboolean msg_badread;
629 void MSG_BeginReading (void)
636 // returns -1 and sets msg_badread if no more characters are available
637 int MSG_ReadChar (void)
641 // LordHavoc: minor optimization
642 if (msg_readcount >= net_message.cursize)
643 // if (msg_readcount+1 > net_message.cursize)
649 c = (signed char)net_message.data[msg_readcount];
655 int MSG_ReadByte (void)
659 // LordHavoc: minor optimization
660 if (msg_readcount >= net_message.cursize)
661 // if (msg_readcount+1 > net_message.cursize)
667 c = (unsigned char)net_message.data[msg_readcount];
674 int MSG_ReadShort (void)
678 if (msg_readcount+2 > net_message.cursize)
684 c = (short)(net_message.data[msg_readcount]
685 + (net_message.data[msg_readcount+1]<<8));
692 int MSG_ReadLong (void)
696 if (msg_readcount+4 > net_message.cursize)
702 c = net_message.data[msg_readcount]
703 + (net_message.data[msg_readcount+1]<<8)
704 + (net_message.data[msg_readcount+2]<<16)
705 + (net_message.data[msg_readcount+3]<<24);
712 float MSG_ReadFloat (void)
721 dat.b[0] = net_message.data[msg_readcount];
722 dat.b[1] = net_message.data[msg_readcount+1];
723 dat.b[2] = net_message.data[msg_readcount+2];
724 dat.b[3] = net_message.data[msg_readcount+3];
727 dat.l = LittleLong (dat.l);
732 char *MSG_ReadString (void)
734 static char string[2048];
741 if (c == -1 || c == 0)
745 } while (l < sizeof(string)-1);
752 // used by server (always dpprotocol)
753 // moved to common.h as #define
755 float MSG_ReadFloatCoord (void)
757 return MSG_ReadFloat();
762 float MSG_ReadCoord (void)
765 return MSG_ReadFloat();
767 return MSG_ReadShort() * (1.0f/8.0f);
771 float MSG_ReadCoord (void)
773 return MSG_ReadShort() * (1.0f/8.0f);
776 float MSG_ReadAngle (void)
778 return MSG_ReadChar() * (360.0f/256.0f);
781 float MSG_ReadPreciseAngle (void)
783 return MSG_ReadShort() * (360.0f/65536);
788 //===========================================================================
790 void SZ_Alloc (sizebuf_t *buf, int startsize)
794 buf->data = Hunk_AllocName (startsize, "sizebuf");
795 buf->maxsize = startsize;
800 void SZ_Free (sizebuf_t *buf)
802 // Z_Free (buf->data);
808 void SZ_Clear (sizebuf_t *buf)
813 void *SZ_GetSpace (sizebuf_t *buf, int length)
817 if (buf->cursize + length > buf->maxsize)
819 if (!buf->allowoverflow)
820 Host_Error ("SZ_GetSpace: overflow without allowoverflow set - use -zone on the commandline for more zone memory, default: 128k (quake original default was 48k)");
822 if (length > buf->maxsize)
823 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
825 buf->overflowed = true;
826 Con_Printf ("SZ_GetSpace: overflow");
830 data = buf->data + buf->cursize;
831 buf->cursize += length;
836 void SZ_Write (sizebuf_t *buf, void *data, int length)
838 memcpy (SZ_GetSpace(buf,length),data,length);
841 void SZ_Print (sizebuf_t *buf, char *data)
845 len = strlen(data)+1;
847 // byte * cast to keep VC++ happy
848 if (buf->data[buf->cursize-1])
849 memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
851 memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
855 //============================================================================
863 char *COM_SkipPath (char *pathname)
882 void COM_StripExtension (char *in, char *out)
884 while (*in && *in != '.')
894 char *COM_FileExtension (char *in)
896 static char exten[8];
899 while (*in && *in != '.')
904 for (i=0 ; i<7 && *in ; i++,in++)
915 void COM_FileBase (char *in, char *out)
934 strcpy (out,"?model?");
949 void COM_DefaultExtension (char *path, char *extension)
953 // if path doesn't have a .EXT, append extension
954 // (extension should include the .)
956 src = path + strlen(path) - 1;
958 while (*src != '/' && src != path)
961 return; // it has an extension
965 strcat (path, extension);
973 Parse a token out of a string
976 char *COM_Parse (char *data)
989 while ( (c = *data) <= ' ')
992 return NULL; // end of file;
997 if (c=='/' && data[1] == '/')
999 while (*data && *data != '\n')
1005 // handle quoted strings specially
1022 // parse single characters
1023 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1031 // parse a regular word
1038 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1051 Returns the position (1 to argc-1) in the program's argument list
1052 where the given parameter apears, or 0 if not present
1055 int COM_CheckParm (char *parm)
1059 for (i=1 ; i<com_argc ; i++)
1062 continue; // NEXTSTEP sometimes clears appkit vars.
1063 if (!strcmp (parm,com_argv[i]))
1074 Looks for the pop.txt file and verifies it.
1075 Sets the "registered" cvar.
1076 Immediately exits out if an alternate game was attempted to be started without
1080 void COM_CheckRegistered (void)
1083 unsigned short check[128];
1086 Cvar_Set ("cmdline", com_cmdline);
1088 COM_FOpenFile("gfx/pop.lmp", &h, false, true);
1089 static_registered = 0;
1094 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1096 Con_Printf ("Playing shareware version.\n");
1098 // Sys_Error ("This dedicated server requires a full registered copy of Quake");
1100 // Con_Printf ("Playing shareware version.\n");
1101 // if (com_modified)
1102 // Sys_Error ("You must have the registered version to use modified games");
1106 Qread (h, check, sizeof(check));
1109 for (i=0 ; i<128 ; i++)
1110 if (pop[i] != (unsigned short)BigShort (check[i]))
1111 Sys_Error ("Corrupted data file.");
1113 // Cvar_Set ("cmdline", com_cmdline);
1114 Cvar_Set ("registered", "1");
1115 static_registered = 1;
1116 Con_Printf ("Playing registered version.\n");
1120 void COM_Path_f (void);
1128 void COM_InitArgv (int argc, char **argv)
1133 // reconstitute the command line for the cmdline externally visible cvar
1136 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1140 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1142 com_cmdline[n++] = argv[j][i++];
1145 if (n < (CMDLINE_LENGTH - 1))
1146 com_cmdline[n++] = ' ';
1155 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1158 largv[com_argc] = argv[com_argc];
1159 if (!strcmp ("-safe", argv[com_argc]))
1165 // force all the safe-mode switches. Note that we reserved extra space in
1166 // case we need to add these, so we don't need an overflow check
1167 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1169 largv[com_argc] = safeargvs[i];
1174 largv[com_argc] = argvdummy;
1179 standard_quake = false;
1181 if (COM_CheckParm ("-rogue"))
1184 standard_quake = false;
1187 if (COM_CheckParm ("-hipnotic"))
1190 standard_quake = false;
1193 if (COM_CheckParm ("-nehahra"))
1196 standard_quake = false;
1202 unsigned int qmalloctotal_alloc, qmalloctotal_alloccount, qmalloctotal_free, qmalloctotal_freecount;
1204 void *qmalloc(unsigned int size)
1207 qmalloctotal_alloc += size;
1208 qmalloctotal_alloccount++;
1209 mem = malloc(size+sizeof(unsigned int));
1213 return (void *)(mem + 1);
1216 void qfree(void *mem)
1222 m--; // back up to size
1223 qmalloctotal_free += *m; // size
1224 qmalloctotal_freecount++;
1228 extern void GL_TextureStats_PrintTotal(void);
1229 extern int hunk_low_used, hunk_high_used, hunk_size;
1230 void COM_Memstats_f(void)
1232 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);
1233 GL_TextureStats_PrintTotal();
1234 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);
1243 void COM_Init (char *basedir)
1246 byte swaptest[2] = {1,0};
1248 // set the byte swapping variables in a portable manner
1249 if ( *(short *)swaptest == 1)
1251 BigShort = ShortSwap;
1252 LittleShort = ShortNoSwap;
1254 LittleLong = LongNoSwap;
1255 BigFloat = FloatSwap;
1256 LittleFloat = FloatNoSwap;
1260 BigShort = ShortNoSwap;
1261 LittleShort = ShortSwap;
1262 BigLong = LongNoSwap;
1263 LittleLong = LongSwap;
1264 BigFloat = FloatNoSwap;
1265 LittleFloat = FloatSwap;
1269 Cvar_RegisterVariable (®istered);
1270 Cvar_RegisterVariable (&cmdline);
1271 Cmd_AddCommand ("path", COM_Path_f);
1272 Cmd_AddCommand ("memstats", COM_Memstats_f);
1274 COM_InitFilesystem ();
1275 COM_CheckRegistered ();
1283 does a varargs printf into a temp buffer, so I don't need to have
1284 varargs versions of all text functions.
1285 FIXME: make this buffer size safe someday
1288 char *va(char *format, ...)
1291 static char string[1024];
1293 va_start (argptr, format);
1294 vsprintf (string, format,argptr);
1301 /// just for debugging
1302 int memsearch (byte *start, int count, int search)
1306 for (i=0 ; i<count ; i++)
1307 if (start[i] == search)
1313 =============================================================================
1317 =============================================================================
1329 char name[MAX_QPATH];
1330 int filepos, filelen;
1333 typedef struct pack_s
1335 char filename[MAX_OSPATH];
1347 int filepos, filelen;
1357 // LordHavoc: was 2048, increased to 16384 and changed info[MAX_PACK_FILES] to a temporary malloc to avoid stack overflows
1358 #define MAX_FILES_IN_PACK 16384
1361 char com_cachedir[MAX_OSPATH];
1363 char com_gamedir[MAX_OSPATH];
1365 typedef struct searchpath_s
1367 char filename[MAX_OSPATH];
1368 pack_t *pack; // only one of filename / pack will be used
1369 struct searchpath_s *next;
1372 searchpath_t *com_searchpaths;
1380 void COM_Path_f (void)
1384 Con_Printf ("Current search path:\n");
1385 for (s=com_searchpaths ; s ; s=s->next)
1389 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1392 Con_Printf ("%s\n", s->filename);
1400 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1403 void COM_CreatePath (char *path)
1407 for (ofs = path+1 ; *ofs ; ofs++)
1409 if (*ofs == '/' || *ofs == '\\' || *ofs == ':')
1410 { // create the directory
1424 The filename will be prefixed by the current game directory
1427 void COM_WriteFile (char *filename, void *data, int len)
1430 char name[MAX_OSPATH];
1432 sprintf (name, "%s/%s", com_gamedir, filename);
1434 // LordHavoc: added this
1435 COM_CreatePath (name); // create directories up to the file
1437 handle = Sys_FileOpenWrite (name);
1440 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1444 Sys_Printf ("COM_WriteFile: %s\n", name);
1445 Sys_FileWrite (handle, data, len);
1446 Sys_FileClose (handle);
1454 Copies a file over from the net to the local cache, creating any directories
1455 needed. This is for the convenience of developers using ISDN from home.
1458 void COM_CopyFile (char *netpath, char *cachepath)
1461 int remaining, count;
1464 remaining = Sys_FileOpenRead (netpath, &in);
1465 COM_CreatePath (cachepath); // create directories up to the cache file
1466 out = Sys_FileOpenWrite (cachepath);
1470 if (remaining < sizeof(buf))
1473 count = sizeof(buf);
1474 Sys_FileRead (in, buf, count);
1475 Sys_FileWrite (out, buf, count);
1480 Sys_FileClose (out);
1488 QFile * COM_OpenRead (const char *path, int offs, int len, qboolean zip)
1490 int fd = open (path, O_RDONLY);
1491 unsigned char id[2];
1492 unsigned char len_bytes[4];
1496 Sys_Error ("Couldn't open %s", path);
1499 if (offs < 0 || len < 0)
1503 len = lseek (fd, 0, SEEK_END);
1504 lseek (fd, 0, SEEK_SET);
1506 lseek (fd, offs, SEEK_SET);
1510 if (id[0] == 0x1f && id[1] == 0x8b)
1512 lseek (fd, offs + len - 4, SEEK_SET);
1513 read (fd, len_bytes, 4);
1514 len = ((len_bytes[3] << 24)
1515 | (len_bytes[2] << 16)
1516 | (len_bytes[1] << 8)
1520 lseek (fd, offs, SEEK_SET);
1524 setmode (fd, O_BINARY);
1527 return Qdopen (fd, "rbz");
1529 return Qdopen (fd, "rb");
1536 Finds the file in the search path.
1537 Sets com_filesize and one of handle or file
1540 int COM_FindFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1542 searchpath_t *search;
1543 char netpath[MAX_OSPATH];
1545 char cachepath[MAX_OSPATH];
1551 char gzfilename[MAX_OSPATH];
1554 filenamelen = strlen (filename);
1555 sprintf (gzfilename, "%s.gz", filename);
1558 Sys_Error ("COM_FindFile: file not set");
1561 // search through the path, one element at a time
1563 search = com_searchpaths;
1565 { // gross hack to use quake 1 progs with quake 2 maps
1566 if (!strcmp(filename, "progs.dat"))
1567 search = search->next;
1570 for ( ; search ; search = search->next)
1572 // is the element a pak file?
1575 // look through all the pak file elements
1577 for (i=0 ; i<pak->numfiles ; i++)
1578 if (!strcmp (pak->files[i].name, filename)
1579 || !strcmp (pak->files[i].name, gzfilename))
1582 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1583 // open a new file on the pakfile
1584 *file = COM_OpenRead (pak->filename, pak->files[i].filepos, pak->files[i].filelen, zip);
1585 return com_filesize;
1590 // check a file in the directory tree
1591 // if (!static_registered)
1592 // { // if not a registered version, don't ever go beyond base
1593 // if ( strchr (filename, '/') || strchr (filename,'\\'))
1597 sprintf (netpath, "%s/%s",search->filename, filename);
1599 findtime = Sys_FileTime (netpath);
1604 // see if the file needs to be updated in the cache
1605 if (com_cachedir[0])
1608 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1609 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1611 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1613 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1616 cachetime = Sys_FileTime (cachepath);
1618 if (cachetime < findtime)
1619 COM_CopyFile (netpath, cachepath);
1620 strcpy (netpath, cachepath);
1625 Sys_Printf ("FindFile: %s\n",netpath);
1626 *file = COM_OpenRead (netpath, -1, -1, zip);
1627 return com_filesize;
1633 Sys_Printf ("FindFile: can't find %s\n", filename);
1645 If the requested file is inside a packfile, a new QFile * will be opened
1649 int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip)
1651 return COM_FindFile (filename, file, quiet, zip);
1659 Filename are reletive to the quake directory.
1660 Always appends a 0 byte.
1663 cache_user_t *loadcache;
1666 byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
1673 buf = NULL; // quiet compiler warning
1675 // look for it in the filesystem or pack files
1676 len = COM_FOpenFile (path, &h, quiet, true);
1680 // extract the filename base name for hunk tag
1681 COM_FileBase (path, base);
1686 buf = Hunk_AllocName (len+1, va("%s (file)", path));
1688 Sys_Error ("COM_LoadFile: not enough hunk space for %s (size %i)", path, len);
1691 // buf = Z_Malloc (len+1);
1693 // Sys_Error ("COM_LoadFile: not enough zone space for %s (size %i)", path, len);
1696 // buf = Cache_Alloc (loadcache, len+1, base);
1698 // Sys_Error ("COM_LoadFile: not enough cache space for %s (size %i)", path, len);
1701 buf = qmalloc (len+1);
1703 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1706 Sys_Error ("COM_LoadFile: bad usehunk");
1710 ((byte *)buf)[len] = 0;
1712 Qread (h, buf, len);
1718 byte *COM_LoadHunkFile (char *path, qboolean quiet)
1720 return COM_LoadFile (path, 1, quiet);
1723 // LordHavoc: returns malloc'd memory
1724 byte *COM_LoadMallocFile (char *path, qboolean quiet)
1726 return COM_LoadFile (path, 5, quiet);
1730 void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
1733 COM_LoadFile (path, 3, quiet);
1741 Takes an explicit (not game tree related) path to a pak file.
1743 Loads the header and directory, adding the files at the beginning
1744 of the list so they override previous pack files.
1747 pack_t *COM_LoadPackFile (char *packfile)
1749 dpackheader_t header;
1751 packfile_t *newfiles;
1755 // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
1759 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1761 // Con_Printf ("Couldn't open %s\n", packfile);
1764 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1765 if (header.id[0] != 'P' || header.id[1] != 'A'
1766 || header.id[2] != 'C' || header.id[3] != 'K')
1767 Sys_Error ("%s is not a packfile", packfile);
1768 header.dirofs = LittleLong (header.dirofs);
1769 header.dirlen = LittleLong (header.dirlen);
1771 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1773 if (numpackfiles > MAX_FILES_IN_PACK)
1774 Sys_Error ("%s has %i files", packfile, numpackfiles);
1776 if (numpackfiles != PAK0_COUNT)
1777 com_modified = true; // not the original file
1779 newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "pack file-table");
1781 info = qmalloc(sizeof(*info)*MAX_FILES_IN_PACK);
1782 Sys_FileSeek (packhandle, header.dirofs);
1783 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1785 // crc the directory to check for modifications
1787 // LordHavoc: speedup
1788 CRC_ProcessBytes(&crc, (byte *)info, header.dirlen);
1789 // for (i=0 ; i<header.dirlen ; i++)
1790 // CRC_ProcessByte (&crc, ((byte *)info)[i]);
1791 if (crc != PAK0_CRC)
1792 com_modified = true;
1794 // parse the directory
1795 for (i=0 ; i<numpackfiles ; i++)
1797 strcpy (newfiles[i].name, info[i].name);
1798 newfiles[i].filepos = LittleLong(info[i].filepos);
1799 newfiles[i].filelen = LittleLong(info[i].filelen);
1803 pack = Hunk_AllocName (sizeof (pack_t), packfile);
1804 strcpy (pack->filename, packfile);
1805 pack->handle = packhandle;
1806 pack->numfiles = numpackfiles;
1807 pack->files = newfiles;
1809 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1816 COM_AddGameDirectory
1818 Sets com_gamedir, adds the directory to the head of the path,
1819 then loads and adds pak1.pak pak2.pak ...
1822 void COM_AddGameDirectory (char *dir)
1825 searchpath_t *search;
1827 char pakfile[MAX_OSPATH];
1829 strcpy (com_gamedir, dir);
1832 // add the directory to the search path
1834 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1835 strcpy (search->filename, dir);
1836 search->next = com_searchpaths;
1837 com_searchpaths = search;
1840 // add any pak files in the format pak0.pak pak1.pak, ...
1844 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1845 pak = COM_LoadPackFile (pakfile);
1848 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1850 search->next = com_searchpaths;
1851 com_searchpaths = search;
1855 // add the contents of the parms.txt file to the end of the command line
1865 void COM_InitFilesystem (void)
1868 char basedir[MAX_OSPATH];
1869 searchpath_t *search;
1873 // Overrides the system supplied base directory (under GAMENAME)
1875 i = COM_CheckParm ("-basedir");
1876 if (i && i < com_argc-1)
1877 strcpy (basedir, com_argv[i+1]);
1879 strcpy (basedir, host_parms.basedir);
1881 j = strlen (basedir);
1885 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1892 // Overrides the system supplied cache directory (NULL or /qcache)
1893 // -cachedir - will disable caching.
1895 i = COM_CheckParm ("-cachedir");
1896 if (i && i < com_argc-1)
1898 if (com_argv[i+1][0] == '-')
1899 com_cachedir[0] = 0;
1901 strcpy (com_cachedir, com_argv[i+1]);
1903 else if (host_parms.cachedir)
1904 strcpy (com_cachedir, host_parms.cachedir);
1906 com_cachedir[0] = 0;
1910 // start up with GAMENAME by default (id1)
1912 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1915 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1917 if (COM_CheckParm ("-rogue"))
1918 COM_AddGameDirectory (va("%s/rogue", basedir) );
1919 if (COM_CheckParm ("-hipnotic"))
1920 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1921 if (COM_CheckParm ("-nehahra"))
1922 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1927 // Adds basedir/gamedir as an override game
1929 i = COM_CheckParm ("-game");
1930 if (i && i < com_argc-1)
1932 com_modified = true;
1933 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1937 // -path <dir or packfile> [<dir or packfile>] ...
1938 // Fully specifies the exact search path, overriding the generated one
1940 i = COM_CheckParm ("-path");
1943 com_modified = true;
1944 com_searchpaths = NULL;
1945 while (++i < com_argc)
1947 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1950 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1951 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1953 search->pack = COM_LoadPackFile (com_argv[i]);
1955 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1958 strcpy (search->filename, com_argv[i]);
1959 search->next = com_searchpaths;
1960 com_searchpaths = search;
1964 if (COM_CheckParm ("-proghack"))
1968 int COM_FileExists(char *filename)
1970 searchpath_t *search;
1971 char netpath[MAX_OSPATH];
1976 for (search = com_searchpaths;search;search = search->next)
1981 for (i = 0;i < pak->numfiles;i++)
1982 if (!strcmp (pak->files[i].name, filename))
1987 sprintf (netpath, "%s/%s",search->filename, filename);
1988 findtime = Sys_FileTime (netpath);
1998 //======================================
1999 // LordHavoc: added these because they are useful
2001 void COM_ToLowerString(char *in, char *out)
2005 if (*in >= 'A' && *in <= 'Z')
2006 *out++ = *in++ + 'a' - 'A';
2012 void COM_ToUpperString(char *in, char *out)
2016 if (*in >= 'a' && *in <= 'z')
2017 *out++ = *in++ + 'A' - 'a';