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 extern 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
1353 char com_cachedir[MAX_OSPATH];
1355 char com_gamedir[MAX_OSPATH];
1357 typedef struct searchpath_s
1359 char filename[MAX_OSPATH];
1360 pack_t *pack; // only one of filename / pack will be used
1361 struct searchpath_s *next;
1364 searchpath_t *com_searchpaths;
1372 void COM_Path_f (void)
1376 Con_Printf ("Current search path:\n");
1377 for (s=com_searchpaths ; s ; s=s->next)
1381 Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
1384 Con_Printf ("%s\n", s->filename);
1392 LordHavoc: Previously only used for CopyFile, now also used for COM_WriteFile.
1395 void COM_CreatePath (char *path)
1399 for (ofs = path+1 ; *ofs ; ofs++)
1401 if (*ofs == '/' || *ofs == '\\' || *ofs == ':')
1402 { // create the directory
1416 The filename will be prefixed by the current game directory
1419 void COM_WriteFile (char *filename, void *data, int len)
1422 char name[MAX_OSPATH];
1424 sprintf (name, "%s/%s", com_gamedir, filename);
1426 // LordHavoc: added this
1427 COM_CreatePath (name); // create directories up to the file
1429 handle = Sys_FileOpenWrite (name);
1432 Sys_Printf ("COM_WriteFile: failed on %s\n", name);
1436 Sys_Printf ("COM_WriteFile: %s\n", name);
1437 Sys_FileWrite (handle, data, len);
1438 Sys_FileClose (handle);
1446 Copies a file over from the net to the local cache, creating any directories
1447 needed. This is for the convenience of developers using ISDN from home.
1450 void COM_CopyFile (char *netpath, char *cachepath)
1453 int remaining, count;
1456 remaining = Sys_FileOpenRead (netpath, &in);
1457 COM_CreatePath (cachepath); // create directories up to the cache file
1458 out = Sys_FileOpenWrite (cachepath);
1462 if (remaining < sizeof(buf))
1465 count = sizeof(buf);
1466 Sys_FileRead (in, buf, count);
1467 Sys_FileWrite (out, buf, count);
1472 Sys_FileClose (out);
1479 Finds the file in the search path.
1480 Sets com_filesize and one of handle or file
1483 int COM_FindFile (char *filename, int *handle, FILE **file, qboolean quiet)
1485 searchpath_t *search;
1486 char netpath[MAX_OSPATH];
1488 char cachepath[MAX_OSPATH];
1496 Sys_Error ("COM_FindFile: both handle and file set");
1497 if (!file && !handle)
1498 Sys_Error ("COM_FindFile: neither handle or file set");
1501 // search through the path, one element at a time
1503 search = com_searchpaths;
1505 { // gross hack to use quake 1 progs with quake 2 maps
1506 if (!strcmp(filename, "progs.dat"))
1507 search = search->next;
1510 for ( ; search ; search = search->next)
1512 // is the element a pak file?
1515 // look through all the pak file elements
1517 for (i=0 ; i<pak->numfiles ; i++)
1518 if (!strcmp (pak->files[i].name, filename))
1521 Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename);
1524 *handle = pak->handle;
1525 Sys_FileSeek (pak->handle, pak->files[i].filepos);
1528 { // open a new file on the pakfile
1529 *file = fopen (pak->filename, "rb");
1531 fseek (*file, pak->files[i].filepos, SEEK_SET);
1533 com_filesize = pak->files[i].filelen;
1534 return com_filesize;
1539 // check a file in the directory tree
1540 // if (!static_registered)
1541 // { // if not a registered version, don't ever go beyond base
1542 // if ( strchr (filename, '/') || strchr (filename,'\\'))
1546 sprintf (netpath, "%s/%s",search->filename, filename);
1548 findtime = Sys_FileTime (netpath);
1553 // see if the file needs to be updated in the cache
1554 if (com_cachedir[0])
1557 if ((strlen(netpath) < 2) || (netpath[1] != ':'))
1558 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1560 sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
1562 sprintf (cachepath,"%s%s", com_cachedir, netpath);
1565 cachetime = Sys_FileTime (cachepath);
1567 if (cachetime < findtime)
1568 COM_CopyFile (netpath, cachepath);
1569 strcpy (netpath, cachepath);
1574 Sys_Printf ("FindFile: %s\n",netpath);
1575 com_filesize = Sys_FileOpenRead (netpath, &i);
1581 *file = fopen (netpath, "rb");
1583 return com_filesize;
1589 Sys_Printf ("FindFile: can't find %s\n", filename);
1604 filename never has a leading slash, but may contain directory walks
1605 returns a handle and a length
1606 it may actually be inside a pak file
1609 int COM_OpenFile (char *filename, int *handle, qboolean quiet)
1611 return COM_FindFile (filename, handle, NULL, quiet);
1618 If the requested file is inside a packfile, a new FILE * will be opened
1622 int COM_FOpenFile (char *filename, FILE **file, qboolean quiet)
1624 return COM_FindFile (filename, NULL, file, quiet);
1631 If it is a pak file handle, don't really close it
1634 void COM_CloseFile (int h)
1638 for (s = com_searchpaths ; s ; s=s->next)
1639 if (s->pack && s->pack->handle == h)
1650 Filename are reletive to the quake directory.
1651 Always appends a 0 byte.
1654 cache_user_t *loadcache;
1657 byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
1664 buf = NULL; // quiet compiler warning
1666 // look for it in the filesystem or pack files
1667 len = COM_OpenFile (path, &h, quiet);
1671 // extract the filename base name for hunk tag
1672 COM_FileBase (path, base);
1677 buf = Hunk_AllocName (len+1, va("%s (file)", path));
1679 Sys_Error ("COM_LoadFile: not enough hunk space for %s (size %i)", path, len);
1682 // buf = Z_Malloc (len+1);
1684 // Sys_Error ("COM_LoadFile: not enough zone space for %s (size %i)", path, len);
1687 buf = Cache_Alloc (loadcache, len+1, base);
1689 Sys_Error ("COM_LoadFile: not enough cache space for %s (size %i)", path, len);
1692 buf = qmalloc (len+1);
1694 Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
1697 Sys_Error ("COM_LoadFile: bad usehunk");
1701 ((byte *)buf)[len] = 0;
1703 Sys_FileRead (h, buf, len);
1709 byte *COM_LoadHunkFile (char *path, qboolean quiet)
1711 return COM_LoadFile (path, 1, quiet);
1714 // LordHavoc: returns malloc'd memory
1715 byte *COM_LoadMallocFile (char *path, qboolean quiet)
1717 return COM_LoadFile (path, 5, quiet);
1720 void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
1723 COM_LoadFile (path, 3, quiet);
1730 Takes an explicit (not game tree related) path to a pak file.
1732 Loads the header and directory, adding the files at the beginning
1733 of the list so they override previous pack files.
1736 pack_t *COM_LoadPackFile (char *packfile)
1738 dpackheader_t header;
1740 packfile_t *newfiles;
1744 // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
1748 if (Sys_FileOpenRead (packfile, &packhandle) == -1)
1750 // Con_Printf ("Couldn't open %s\n", packfile);
1753 Sys_FileRead (packhandle, (void *)&header, sizeof(header));
1754 if (header.id[0] != 'P' || header.id[1] != 'A'
1755 || header.id[2] != 'C' || header.id[3] != 'K')
1756 Sys_Error ("%s is not a packfile", packfile);
1757 header.dirofs = LittleLong (header.dirofs);
1758 header.dirlen = LittleLong (header.dirlen);
1760 numpackfiles = header.dirlen / sizeof(dpackfile_t);
1762 if (numpackfiles > MAX_FILES_IN_PACK)
1763 Sys_Error ("%s has %i files", packfile, numpackfiles);
1765 if (numpackfiles != PAK0_COUNT)
1766 com_modified = true; // not the original file
1768 newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "pack file-table");
1770 info = qmalloc(sizeof(*info)*MAX_FILES_IN_PACK);
1771 Sys_FileSeek (packhandle, header.dirofs);
1772 Sys_FileRead (packhandle, (void *)info, header.dirlen);
1774 // crc the directory to check for modifications
1776 // LordHavoc: speedup
1777 CRC_ProcessBytes(&crc, (byte *)info, header.dirlen);
1778 // for (i=0 ; i<header.dirlen ; i++)
1779 // CRC_ProcessByte (&crc, ((byte *)info)[i]);
1780 if (crc != PAK0_CRC)
1781 com_modified = true;
1783 // parse the directory
1784 for (i=0 ; i<numpackfiles ; i++)
1786 strcpy (newfiles[i].name, info[i].name);
1787 newfiles[i].filepos = LittleLong(info[i].filepos);
1788 newfiles[i].filelen = LittleLong(info[i].filelen);
1792 pack = Hunk_AllocName (sizeof (pack_t), packfile);
1793 strcpy (pack->filename, packfile);
1794 pack->handle = packhandle;
1795 pack->numfiles = numpackfiles;
1796 pack->files = newfiles;
1798 Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
1805 COM_AddGameDirectory
1807 Sets com_gamedir, adds the directory to the head of the path,
1808 then loads and adds pak1.pak pak2.pak ...
1811 void COM_AddGameDirectory (char *dir)
1814 searchpath_t *search;
1816 char pakfile[MAX_OSPATH];
1818 strcpy (com_gamedir, dir);
1821 // add the directory to the search path
1823 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1824 strcpy (search->filename, dir);
1825 search->next = com_searchpaths;
1826 com_searchpaths = search;
1829 // add any pak files in the format pak0.pak pak1.pak, ...
1833 sprintf (pakfile, "%s/pak%i.pak", dir, i);
1834 pak = COM_LoadPackFile (pakfile);
1837 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1839 search->next = com_searchpaths;
1840 com_searchpaths = search;
1844 // add the contents of the parms.txt file to the end of the command line
1854 void COM_InitFilesystem (void)
1857 char basedir[MAX_OSPATH];
1858 searchpath_t *search;
1862 // Overrides the system supplied base directory (under GAMENAME)
1864 i = COM_CheckParm ("-basedir");
1865 if (i && i < com_argc-1)
1866 strcpy (basedir, com_argv[i+1]);
1868 strcpy (basedir, host_parms.basedir);
1870 j = strlen (basedir);
1874 if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
1881 // Overrides the system supplied cache directory (NULL or /qcache)
1882 // -cachedir - will disable caching.
1884 i = COM_CheckParm ("-cachedir");
1885 if (i && i < com_argc-1)
1887 if (com_argv[i+1][0] == '-')
1888 com_cachedir[0] = 0;
1890 strcpy (com_cachedir, com_argv[i+1]);
1892 else if (host_parms.cachedir)
1893 strcpy (com_cachedir, host_parms.cachedir);
1895 com_cachedir[0] = 0;
1899 // start up with GAMENAME by default (id1)
1901 COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
1904 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1906 if (COM_CheckParm ("-rogue"))
1907 COM_AddGameDirectory (va("%s/rogue", basedir) );
1908 if (COM_CheckParm ("-hipnotic"))
1909 COM_AddGameDirectory (va("%s/hipnotic", basedir) );
1910 if (COM_CheckParm ("-nehahra"))
1911 COM_AddGameDirectory (va("%s/nehahra", basedir) );
1916 // Adds basedir/gamedir as an override game
1918 i = COM_CheckParm ("-game");
1919 if (i && i < com_argc-1)
1921 com_modified = true;
1922 COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
1926 // -path <dir or packfile> [<dir or packfile>] ...
1927 // Fully specifies the exact search path, overriding the generated one
1929 i = COM_CheckParm ("-path");
1932 com_modified = true;
1933 com_searchpaths = NULL;
1934 while (++i < com_argc)
1936 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1939 search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
1940 if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
1942 search->pack = COM_LoadPackFile (com_argv[i]);
1944 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
1947 strcpy (search->filename, com_argv[i]);
1948 search->next = com_searchpaths;
1949 com_searchpaths = search;
1953 if (COM_CheckParm ("-proghack"))
1957 int COM_FileExists(char *filename)
1959 searchpath_t *search;
1960 char netpath[MAX_OSPATH];
1965 for (search = com_searchpaths;search;search = search->next)
1970 for (i = 0;i < pak->numfiles;i++)
1971 if (!strcmp (pak->files[i].name, filename))
1976 sprintf (netpath, "%s/%s",search->filename, filename);
1977 findtime = Sys_FileTime (netpath);
1987 //======================================
1988 // LordHavoc: added these because they are useful
1990 void COM_ToLowerString(char *in, char *out)
1994 if (*in >= 'A' && *in <= 'Z')
1995 *out++ = *in++ + 'a' - 'A';
2001 void COM_ToUpperString(char *in, char *out)
2005 if (*in >= 'a' && *in <= 'z')
2006 *out++ = *in++ + 'A' - 'a';