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
24 #define NUM_SAFE_ARGVS 7
26 static char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
27 static char *argvdummy = " ";
29 static char *safeargvs[NUM_SAFE_ARGVS] =
30 {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"};
32 cvar_t registered = {"registered","0"};
33 cvar_t cmdline = {"cmdline","0", false, true};
35 qboolean com_modified; // set true if using non-id files
39 int static_registered = 1; // only for startup check, then set
41 qboolean msg_suppress_1 = 0;
43 void COM_InitFilesystem (void);
45 // if a packfile directory differs from this, it is assumed to be hacked
46 #define PAK0_COUNT 339
47 #define PAK0_CRC 32981
53 #define CMDLINE_LENGTH 256
54 char com_cmdline[CMDLINE_LENGTH];
56 qboolean standard_quake = true, rogue = false, hipnotic = false, nehahra = false;
58 // this graphic needs to be in the pak file to use registered features
59 unsigned short pop[] =
61 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
62 ,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000
63 ,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000
64 ,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600
65 ,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563
66 ,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564
67 ,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564
68 ,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563
69 ,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500
70 ,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200
71 ,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000
72 ,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000
73 ,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000
74 ,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000
75 ,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000
76 ,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000
82 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.
84 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
85 only used during filesystem initialization.
87 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.
89 The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory
90 specified, when a file is found by the normal search path, it will be mirrored
91 into the cache directory, then opened there.
96 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.
100 //============================================================================
103 // ClearLink is used for new headnodes
104 void ClearLink (link_t *l)
106 l->prev = l->next = l;
109 void RemoveLink (link_t *l)
111 l->next->prev = l->prev;
112 l->prev->next = l->next;
115 void InsertLinkBefore (link_t *l, link_t *before)
118 l->prev = before->prev;
122 void InsertLinkAfter (link_t *l, link_t *after)
124 l->next = after->next;
131 ============================================================================
133 LIBRARY REPLACEMENT FUNCTIONS
135 ============================================================================
139 void Q_memset (void *dest, int fill, int count)
143 if ( (((long)dest | count) & 3) == 0)
146 fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
147 for (i=0 ; i<count ; i++)
148 ((int *)dest)[i] = fill;
151 for (i=0 ; i<count ; i++)
152 ((byte *)dest)[i] = fill;
155 void Q_memcpy (void *dest, void *src, int count)
159 if (( ( (long)dest | (long)src | count) & 3) == 0 )
162 for (i=0 ; i<count ; i++)
163 ((int *)dest)[i] = ((int *)src)[i];
166 for (i=0 ; i<count ; i++)
167 ((byte *)dest)[i] = ((byte *)src)[i];
170 int Q_memcmp (void *m1, void *m2, int count)
175 if (((byte *)m1)[count] != ((byte *)m2)[count])
181 void Q_strcpy (char *dest, char *src)
190 void Q_strncpy (char *dest, char *src, int count)
192 while (*src && count--)
200 int Q_strlen (char *str)
211 char *Q_strrchr(char *s, char c)
213 int len = Q_strlen(s);
216 if (*--s == c) return s;
220 void Q_strcat (char *dest, char *src)
222 dest += Q_strlen(dest);
223 Q_strcpy (dest, src);
226 int Q_strcmp (char *s1, char *s2)
231 return -1; // strings not equal
233 return 0; // strings are equal
241 int Q_strncmp (char *s1, char *s2, int count)
248 return -1; // strings not equal
250 return 0; // strings are equal
258 int Q_strncasecmp (char *s1, char *s2, int n)
268 return 0; // strings are equal until end point
272 if (c1 >= 'a' && c1 <= 'z')
274 if (c2 >= 'a' && c2 <= 'z')
277 return -1; // strings not equal
280 return 0; // strings are equal
288 int Q_strcasecmp (char *s1, char *s2)
290 return Q_strncasecmp (s1, s2, 99999);
293 int Q_atoi (char *str)
312 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
318 if (c >= '0' && c <= '9')
319 val = (val<<4) + c - '0';
320 else if (c >= 'a' && c <= 'f')
321 val = (val<<4) + c - 'a' + 10;
322 else if (c >= 'A' && c <= 'F')
323 val = (val<<4) + c - 'A' + 10;
330 // check for character
334 return sign * str[1];
343 if (c <'0' || c > '9')
345 val = val*10 + c - '0';
352 float Q_atof (char *str)
372 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
378 if (c >= '0' && c <= '9')
379 val = (val*16) + c - '0';
380 else if (c >= 'a' && c <= 'f')
381 val = (val*16) + c - 'a' + 10;
382 else if (c >= 'A' && c <= 'F')
383 val = (val*16) + c - 'A' + 10;
390 // check for character
394 return sign * str[1];
410 if (c <'0' || c > '9')
412 val = val*10 + c - '0';
418 while (total > decimal)
429 ============================================================================
433 ============================================================================
437 short (*BigShort) (short l);
438 short (*LittleShort) (short l);
439 int (*BigLong) (int l);
440 int (*LittleLong) (int l);
441 float (*BigFloat) (float l);
442 float (*LittleFloat) (float l);
445 short ShortSwap (short l)
455 short ShortNoSwap (short l)
469 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
472 int LongNoSwap (int l)
477 float FloatSwap (float f)
487 dat2.b[0] = dat1.b[3];
488 dat2.b[1] = dat1.b[2];
489 dat2.b[2] = dat1.b[1];
490 dat2.b[3] = dat1.b[0];
494 float FloatNoSwap (float f)
500 ==============================================================================
504 Handles byte ordering and avoids alignment errors
505 ==============================================================================
512 void MSG_WriteChar (sizebuf_t *sb, int c)
517 // if (c < -128 || c > 127)
518 // Sys_Error ("MSG_WriteChar: range error");
521 buf = SZ_GetSpace (sb, 1);
525 void MSG_WriteByte (sizebuf_t *sb, int c)
530 // if (c < 0 || c > 255)
531 // Sys_Error ("MSG_WriteByte: range error");
534 buf = SZ_GetSpace (sb, 1);
538 void MSG_WriteShort (sizebuf_t *sb, int c)
543 // if (c < ((short)0x8000) || c > (short)0x7fff)
544 // Sys_Error ("MSG_WriteShort: range error");
547 buf = SZ_GetSpace (sb, 2);
552 void MSG_WriteLong (sizebuf_t *sb, int c)
556 buf = SZ_GetSpace (sb, 4);
558 buf[1] = (c>>8)&0xff;
559 buf[2] = (c>>16)&0xff;
563 void MSG_WriteFloat (sizebuf_t *sb, float f)
573 dat.l = LittleLong (dat.l);
575 SZ_Write (sb, &dat.l, 4);
578 void MSG_WriteString (sizebuf_t *sb, char *s)
581 SZ_Write (sb, "", 1);
583 SZ_Write (sb, s, strlen(s)+1);
586 // used by server (always dpprotocol)
587 // moved to common.h as #define
589 void MSG_WriteFloatCoord (sizebuf_t *sb, float f)
591 MSG_WriteFloat(sb, f);
596 void MSG_WriteCoord (sizebuf_t *sb, float f)
599 MSG_WriteFloat(sb, f);
601 MSG_WriteShort (sb, (int)(f*8.0f + 0.5f));
604 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
606 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
609 // LordHavoc: round to nearest value, rather than rounding down, fixes crosshair problem
610 void MSG_WriteAngle (sizebuf_t *sb, float f)
612 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
619 qboolean msg_badread;
621 void MSG_BeginReading (void)
628 // returns -1 and sets msg_badread if no more characters are available
629 int MSG_ReadChar (void)
633 // LordHavoc: minor optimization
634 if (msg_readcount >= net_message.cursize)
635 // if (msg_readcount+1 > net_message.cursize)
641 c = (signed char)net_message.data[msg_readcount];
647 int MSG_ReadByte (void)
651 // LordHavoc: minor optimization
652 if (msg_readcount >= net_message.cursize)
653 // if (msg_readcount+1 > net_message.cursize)
659 c = (unsigned char)net_message.data[msg_readcount];
666 int MSG_ReadShort (void)
670 if (msg_readcount+2 > net_message.cursize)
676 c = (short)(net_message.data[msg_readcount]
677 + (net_message.data[msg_readcount+1]<<8));
684 int MSG_ReadLong (void)
688 if (msg_readcount+4 > net_message.cursize)
694 c = net_message.data[msg_readcount]
695 + (net_message.data[msg_readcount+1]<<8)
696 + (net_message.data[msg_readcount+2]<<16)
697 + (net_message.data[msg_readcount+3]<<24);
704 float MSG_ReadFloat (void)
713 dat.b[0] = net_message.data[msg_readcount];
714 dat.b[1] = net_message.data[msg_readcount+1];
715 dat.b[2] = net_message.data[msg_readcount+2];
716 dat.b[3] = net_message.data[msg_readcount+3];
719 dat.l = LittleLong (dat.l);
724 char *MSG_ReadString (void)
726 static char string[2048];
733 if (c == -1 || c == 0)
737 } while (l < sizeof(string)-1);
744 // used by server (always dpprotocol)
745 // moved to common.h as #define
747 float MSG_ReadFloatCoord (void)
749 return MSG_ReadFloat();
754 float MSG_ReadCoord (void)
757 return MSG_ReadFloat();
759 return MSG_ReadShort() * (1.0f/8.0f);
763 float MSG_ReadCoord (void)
765 return MSG_ReadShort() * (1.0f/8.0f);
768 float MSG_ReadAngle (void)
770 return MSG_ReadChar() * (360.0f/256.0f);
773 float MSG_ReadPreciseAngle (void)
775 return MSG_ReadShort() * (360.0f/65536);
780 //===========================================================================
782 void SZ_Alloc (sizebuf_t *buf, int startsize)
786 buf->data = Hunk_AllocName (startsize, "sizebuf");
787 buf->maxsize = startsize;
792 void SZ_Free (sizebuf_t *buf)
794 // Z_Free (buf->data);
800 void SZ_Clear (sizebuf_t *buf)
805 void *SZ_GetSpace (sizebuf_t *buf, int length)
809 if (buf->cursize + length > buf->maxsize)
811 if (!buf->allowoverflow)
812 Host_Error ("SZ_GetSpace: overflow without allowoverflow set - use -zone on the commandline for more zone memory, default: 128k (quake original default was 48k)");
814 if (length > buf->maxsize)
815 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
817 buf->overflowed = true;
818 Con_Printf ("SZ_GetSpace: overflow");
822 data = buf->data + buf->cursize;
823 buf->cursize += length;
828 void SZ_Write (sizebuf_t *buf, void *data, int length)
830 memcpy (SZ_GetSpace(buf,length),data,length);
833 void SZ_Print (sizebuf_t *buf, char *data)
837 len = strlen(data)+1;
839 // byte * cast to keep VC++ happy
840 if (buf->data[buf->cursize-1])
841 memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
843 memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
847 //============================================================================
855 char *COM_SkipPath (char *pathname)
874 void COM_StripExtension (char *in, char *out)
876 while (*in && *in != '.')
886 char *COM_FileExtension (char *in)
888 static char exten[8];
891 while (*in && *in != '.')
896 for (i=0 ; i<7 && *in ; i++,in++)
907 void COM_FileBase (char *in, char *out)
926 strcpy (out,"?model?");
941 void COM_DefaultExtension (char *path, char *extension)
945 // if path doesn't have a .EXT, append extension
946 // (extension should include the .)
948 src = path + strlen(path) - 1;
950 while (*src != '/' && src != path)
953 return; // it has an extension
957 strcat (path, extension);
965 Parse a token out of a string
968 char *COM_Parse (char *data)
981 while ( (c = *data) <= ' ')
984 return NULL; // end of file;
989 if (c=='/' && data[1] == '/')
991 while (*data && *data != '\n')
997 // handle quoted strings specially
1014 // parse single characters
1015 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1023 // parse a regular word
1030 if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
1043 Returns the position (1 to argc-1) in the program's argument list
1044 where the given parameter apears, or 0 if not present
1047 int COM_CheckParm (char *parm)
1051 for (i=1 ; i<com_argc ; i++)
1054 continue; // NEXTSTEP sometimes clears appkit vars.
1055 if (!strcmp (parm,com_argv[i]))
1066 Looks for the pop.txt file and verifies it.
1067 Sets the "registered" cvar.
1068 Immediately exits out if an alternate game was attempted to be started without
1072 void COM_CheckRegistered (void)
1075 unsigned short check[128];
1078 Cvar_Set ("cmdline", com_cmdline);
1080 COM_OpenFile("gfx/pop.lmp", &h, false);
1081 static_registered = 0;
1086 Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1088 Con_Printf ("Playing shareware version.\n");
1090 // Sys_Error ("This dedicated server requires a full registered copy of Quake");
1092 // Con_Printf ("Playing shareware version.\n");
1093 // if (com_modified)
1094 // Sys_Error ("You must have the registered version to use modified games");
1098 Sys_FileRead (h, check, sizeof(check));
1101 for (i=0 ; i<128 ; i++)
1102 if (pop[i] != (unsigned short)BigShort (check[i]))
1103 Sys_Error ("Corrupted data file.");
1105 // Cvar_Set ("cmdline", com_cmdline);
1106 Cvar_Set ("registered", "1");
1107 static_registered = 1;
1108 Con_Printf ("Playing registered version.\n");
1112 void COM_Path_f (void);
1120 void COM_InitArgv (int argc, char **argv)
1125 // reconstitute the command line for the cmdline externally visible cvar
1128 for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
1132 while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
1134 com_cmdline[n++] = argv[j][i++];
1137 if (n < (CMDLINE_LENGTH - 1))
1138 com_cmdline[n++] = ' ';
1147 for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
1150 largv[com_argc] = argv[com_argc];
1151 if (!strcmp ("-safe", argv[com_argc]))
1157 // force all the safe-mode switches. Note that we reserved extra space in
1158 // case we need to add these, so we don't need an overflow check
1159 for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
1161 largv[com_argc] = safeargvs[i];
1166 largv[com_argc] = argvdummy;
1171 standard_quake = false;
1173 if (COM_CheckParm ("-rogue"))
1176 standard_quake = false;
1179 if (COM_CheckParm ("-hipnotic"))
1182 standard_quake = false;
1185 if (COM_CheckParm ("-nehahra"))
1188 standard_quake = false;
1194 unsigned int qmalloctotal_alloc, qmalloctotal_alloccount, qmalloctotal_free, qmalloctotal_freecount;
1196 void *qmalloc(unsigned int size)
1199 qmalloctotal_alloc += size;
1200 qmalloctotal_alloccount++;
1201 mem = malloc(size+sizeof(unsigned int));
1205 return (void *)(mem + 1);
1208 void qfree(void *mem)
1214 m--; // back up to size
1215 qmalloctotal_free += *m; // size
1216 qmalloctotal_freecount++;
1220 void GL_TextureStats_PrintTotal(void);
1221 extern int hunk_low_used, hunk_high_used, hunk_size;
1222 void COM_Memstats_f(void)
1224 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);
1225 GL_TextureStats_PrintTotal();
1226 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);
1235 void COM_Init (char *basedir)
1238 byte swaptest[2] = {1,0};
1240 // set the byte swapping variables in a portable manner
1241 if ( *(short *)swaptest == 1)
1243 BigShort = ShortSwap;
1244 LittleShort = ShortNoSwap;
1246 LittleLong = LongNoSwap;
1247 BigFloat = FloatSwap;
1248 LittleFloat = FloatNoSwap;
1252 BigShort = ShortNoSwap;
1253 LittleShort = ShortSwap;
1254 BigLong = LongNoSwap;
1255 LittleLong = LongSwap;
1256 BigFloat = FloatNoSwap;
1257 LittleFloat = FloatSwap;
1261 Cvar_RegisterVariable (®istered);
1262 Cvar_RegisterVariable (&cmdline);
1263 Cmd_AddCommand ("path", COM_Path_f);
1264 Cmd_AddCommand ("memstats", COM_Memstats_f);
1266 COM_InitFilesystem ();
1267 COM_CheckRegistered ();
1275 does a varargs printf into a temp buffer, so I don't need to have
1276 varargs versions of all text functions.
1277 FIXME: make this buffer size safe someday
1280 char *va(char *format, ...)
1283 static char string[1024];
1285 va_start (argptr, format);
1286 vsprintf (string, format,argptr);
1293 /// just for debugging
1294 int memsearch (byte *start, int count, int search)
1298 for (i=0 ; i<count ; i++)
1299 if (start[i] == search)
1305 =============================================================================
1309 =============================================================================
1321 char name[MAX_QPATH];
1322 int filepos, filelen;
1325 typedef struct pack_s
1327 char filename[MAX_OSPATH];
1339 int filepos, filelen;
1349 // LordHavoc: was 2048, increased to 16384 and changed info[MAX_PACK_FILES] to a temporary malloc to avoid stack overflows
1350 #define MAX_FILES_IN_PACK 16384
1352 char com_cachedir[MAX_OSPATH];
1353 char com_gamedir[MAX_OSPATH];
1355 typedef struct searchpath_s
1357 char filename[MAX_OSPATH];
1358 pack_t *pack; // only one of filename / pack will be used
1359 struct searchpath_s *next;
1362 searchpath_t *com_searchpaths;
1370 void COM_Path_f (void)
1374 Con_Printf ("Current search path:\n");
1375 for (s=com_searchpaths ; s ; s=s->next)
1379 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1382 Con_Printf ("%s\n", s->filename);
1390 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1393 void COM_CreatePath (char *path)
1397 for (ofs = path+1 ; *ofs ; ofs++)
1399 if (*ofs == '/' || *ofs == '\\' || *ofs == ':')
1400 { // create the directory
1414 The filename will be prefixed by the current game directory
1417 void COM_WriteFile (char *filename, void *data, int len)
1420 char name[MAX_OSPATH];
1422 sprintf (name, "%s/%s", com_gamedir, filename);
1424 // LordHavoc: added this
1425 COM_CreatePath (name); // create directories up to the file
1427 handle = Sys_FileOpenWrite (name);
1430 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1434 Sys_Printf ("COM_WriteFile: %s\n", name);
1435 Sys_FileWrite (handle, data, len);
1436 Sys_FileClose (handle);
1444 Copies a file over from the net to the local cache, creating any directories
1445 needed. This is for the convenience of developers using ISDN from home.
1448 void COM_CopyFile (char *netpath, char *cachepath)
1451 int remaining, count;
1454 remaining = Sys_FileOpenRead (netpath, &in);
1455 COM_CreatePath (cachepath); // create directories up to the cache file
1456 out = Sys_FileOpenWrite (cachepath);
1460 if (remaining < sizeof(buf))
1463 count = sizeof(buf);
1464 Sys_FileRead (in, buf, count);
1465 Sys_FileWrite (out, buf, count);
1470 Sys_FileClose (out);
1477 Finds the file in the search path.
1478 Sets com_filesize and one of handle or file
1481 int COM_FindFile (char *filename, int *handle, FILE **file, qboolean quiet)
1483 searchpath_t *search;
1484 char netpath[MAX_OSPATH];
1485 char cachepath[MAX_OSPATH];
1488 int findtime, cachetime;
1491 Sys_Error ("COM_FindFile: both handle and file set");
1492 if (!file && !handle)
1493 Sys_Error ("COM_FindFile: neither handle or file set");
1496 // search through the path, one element at a time
1498 search = com_searchpaths;
1500 { // gross hack to use quake 1 progs with quake 2 maps
1501 if (!strcmp(filename, "progs.dat"))
1502 search = search->next;
1505 for ( ; search ; search = search->next)
1507 // is the element a pak file?
1510 // look through all the pak file elements
1512 for (i=0 ; i<pak->numfiles ; i++)
1513 if (!strcmp (pak->files[i].name, filename))
1516 Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename);
1519 *handle = pak->handle;
1520 Sys_FileSeek (pak->handle, pak->files[i].filepos);
1523 { // open a new file on the pakfile
1524 *file = fopen (pak->filename, "rb");
1526 fseek (*file, pak->files[i].filepos, SEEK_SET);
1528 com_filesize = pak->files[i].filelen;
1529 return com_filesize;
1534 // check a file in the directory tree
1535 // if (!static_registered)
1536 // { // if not a registered version, don't ever go beyond base
1537 // if ( strchr (filename, '/') || strchr (filename,'\\'))
1541 sprintf (netpath, "%s/%s",search->filename, filename);
1543 findtime = Sys_FileTime (netpath);
1547 // see if the file needs to be updated in the cache
1548 if (!com_cachedir[0])
1549 strcpy (cachepath, netpath);
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);
1569 Sys_Printf ("FindFile: %s\n",netpath);
1570 com_filesize = Sys_FileOpenRead (netpath, &i);
1576 *file = fopen (netpath, "rb");
1578 return com_filesize;
1584 Sys_Printf ("FindFile: can't find %s\n", filename);
1599 filename never has a leading slash, but may contain directory walks
1600 returns a handle and a length
1601 it may actually be inside a pak file
1604 int COM_OpenFile (char *filename, int *handle, qboolean quiet)
1606 return COM_FindFile (filename, handle, NULL, quiet);
1613 If the requested file is inside a packfile, a new FILE * will be opened
1617 int COM_FOpenFile (char *filename, FILE **file, qboolean quiet)
1619 return COM_FindFile (filename, NULL, file, quiet);
1626 If it is a pak file handle, don't really close it
1629 void COM_CloseFile (int h)
1633 for (s = com_searchpaths ; s ; s=s->next)
1634 if (s->pack && s->pack->handle == h)
1645 Filename are reletive to the quake directory.
1646 Always appends a 0 byte.
1649 cache_user_t *loadcache;
1652 byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
1659 buf = NULL; // quiet compiler warning
1661 // look for it in the filesystem or pack files
1662 len = COM_OpenFile (path, &h, quiet);
1666 // extract the filename base name for hunk tag
1667 COM_FileBase (path, base);
1672 buf = Hunk_AllocName (len+1, va("%s (file)", path));
1674 Sys_Error ("COM_LoadFile: not enough hunk space for %s (size %i)", path, len);
1677 // buf = Z_Malloc (len+1);
1679 // Sys_Error ("COM_LoadFile: not enough zone space for %s (size %i)", path, len);
1682 buf = Cache_Alloc (loadcache, len+1, base);
1684 Sys_Error ("COM_LoadFile: not enough cache space for %s (size %i)", path, len);
1687 buf = qmalloc (len+1);
1689 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1692 Sys_Error ("COM_LoadFile: bad usehunk");
1696 ((byte *)buf)[len] = 0;
1698 Sys_FileRead (h, buf, len);
1704 byte *COM_LoadHunkFile (char *path, qboolean quiet)
1706 return COM_LoadFile (path, 1, quiet);
1709 // LordHavoc: returns malloc'd memory
1710 byte *COM_LoadMallocFile (char *path, qboolean quiet)
1712 return COM_LoadFile (path, 5, quiet);
1715 void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
1718 COM_LoadFile (path, 3, quiet);
1725 Takes an explicit (not game tree related) path to a pak file.
1727 Loads the header and directory, adding the files at the beginning
1728 of the list so they override previous pack files.
1731 pack_t *COM_LoadPackFile (char *packfile)
1733 dpackheader_t header;
1735 packfile_t *newfiles;
1739 // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
1743 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1745 // Con_Printf ("Couldn't open %s\n", packfile);
1748 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1749 if (header.id[0] != 'P' || header.id[1] != 'A'
1750 || header.id[2] != 'C' || header.id[3] != 'K')
1751 Sys_Error ("%s is not a packfile", packfile);
1752 header.dirofs = LittleLong (header.dirofs);
1753 header.dirlen = LittleLong (header.dirlen);
1755 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1757 if (numpackfiles > MAX_FILES_IN_PACK)
1758 Sys_Error ("%s has %i files", packfile, numpackfiles);
1760 if (numpackfiles != PAK0_COUNT)
1761 com_modified = true; // not the original file
1763 newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "pack file-table");
1765 info = qmalloc(sizeof(*info)*MAX_FILES_IN_PACK);
1766 Sys_FileSeek (packhandle, header.dirofs);
1767 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1769 // crc the directory to check for modifications
1771 // LordHavoc: speedup
1772 CRC_ProcessBytes(&crc, (byte *)info, header.dirlen);
1773 // for (i=0 ; i<header.dirlen ; i++)
1774 // CRC_ProcessByte (&crc, ((byte *)info)[i]);
1775 if (crc != PAK0_CRC)
1776 com_modified = true;
1778 // parse the directory
1779 for (i=0 ; i<numpackfiles ; i++)
1781 strcpy (newfiles[i].name, info[i].name);
1782 newfiles[i].filepos = LittleLong(info[i].filepos);
1783 newfiles[i].filelen = LittleLong(info[i].filelen);
1787 pack = Hunk_AllocName (sizeof (pack_t), packfile);
1788 strcpy (pack->filename, packfile);
1789 pack->handle = packhandle;
1790 pack->numfiles = numpackfiles;
1791 pack->files = newfiles;
1793 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1800 COM_AddGameDirectory
1802 Sets com_gamedir, adds the directory to the head of the path,
1803 then loads and adds pak1.pak pak2.pak ...
1806 void COM_AddGameDirectory (char *dir)
1809 searchpath_t *search;
1811 char pakfile[MAX_OSPATH];
1813 strcpy (com_gamedir, dir);
1816 // add the directory to the search path
1818 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1819 strcpy (search->filename, dir);
1820 search->next = com_searchpaths;
1821 com_searchpaths = search;
1824 // add any pak files in the format pak0.pak pak1.pak, ...
1828 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1829 pak = COM_LoadPackFile (pakfile);
1832 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1834 search->next = com_searchpaths;
1835 com_searchpaths = search;
1839 // add the contents of the parms.txt file to the end of the command line
1849 void COM_InitFilesystem (void)
1852 char basedir[MAX_OSPATH];
1853 searchpath_t *search;
1857 // Overrides the system supplied base directory (under GAMENAME)
1859 i = COM_CheckParm ("-basedir");
1860 if (i && i < com_argc-1)
1861 strcpy (basedir, com_argv[i+1]);
1863 strcpy (basedir, host_parms.basedir);
1865 j = strlen (basedir);
1869 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1875 // Overrides the system supplied cache directory (NULL or /qcache)
1876 // -cachedir - will disable caching.
1878 i = COM_CheckParm ("-cachedir");
1879 if (i && i < com_argc-1)
1881 if (com_argv[i+1][0] == '-')
1882 com_cachedir[0] = 0;
1884 strcpy (com_cachedir, com_argv[i+1]);
1886 else if (host_parms.cachedir)
1887 strcpy (com_cachedir, host_parms.cachedir);
1889 com_cachedir[0] = 0;
1892 // start up with GAMENAME by default (id1)
1894 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1897 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1899 if (COM_CheckParm ("-rogue"))
1900 COM_AddGameDirectory (va("%s/rogue", basedir) );
1901 if (COM_CheckParm ("-hipnotic"))
1902 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1903 if (COM_CheckParm ("-nehahra"))
1904 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1909 // Adds basedir/gamedir as an override game
1911 i = COM_CheckParm ("-game");
1912 if (i && i < com_argc-1)
1914 com_modified = true;
1915 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1919 // -path <dir or packfile> [<dir or packfile>] ...
1920 // Fully specifies the exact search path, overriding the generated one
1922 i = COM_CheckParm ("-path");
1925 com_modified = true;
1926 com_searchpaths = NULL;
1927 while (++i < com_argc)
1929 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1932 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1933 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1935 search->pack = COM_LoadPackFile (com_argv[i]);
1937 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1940 strcpy (search->filename, com_argv[i]);
1941 search->next = com_searchpaths;
1942 com_searchpaths = search;
1946 if (COM_CheckParm ("-proghack"))
1950 int COM_FileExists(char *filename)
1952 searchpath_t *search;
1953 char netpath[MAX_OSPATH];
1958 for (search = com_searchpaths;search;search = search->next)
1963 for (i = 0;i < pak->numfiles;i++)
1964 if (!strcmp (pak->files[i].name, filename))
1969 sprintf (netpath, "%s/%s",search->filename, filename);
1970 findtime = Sys_FileTime (netpath);
1980 //======================================
1981 // LordHavoc: added these because they are useful
1983 void COM_ToLowerString(char *in, char *out)
1987 if (*in >= 'A' && *in <= 'Z')
1988 *out++ = *in++ + 'a' - 'A';
1994 void COM_ToUpperString(char *in, char *out)
1998 if (*in >= 'a' && *in <= 'z')
1999 *out++ = *in++ + 'A' - 'a';