511 lines
11 KiB
C
511 lines
11 KiB
C
/*
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
// common.c -- misc functions used in client and server
|
|
|
|
#include "engine.h"
|
|
|
|
/*
|
|
=======================================================================
|
|
|
|
INFOSTRING STUFF
|
|
=======================================================================
|
|
*/
|
|
|
|
static char sv_info[MAX_INFO_STRING];
|
|
|
|
/*
|
|
===============
|
|
Info_Print
|
|
|
|
printing current key-value pair
|
|
===============
|
|
*/
|
|
void Info_Print (char *s)
|
|
{
|
|
char key[512];
|
|
char value[512];
|
|
char *o;
|
|
int l;
|
|
|
|
if (*s == '\\') s++;
|
|
|
|
while (*s)
|
|
{
|
|
o = key;
|
|
while (*s && *s != '\\') *o++ = *s++;
|
|
|
|
l = o - key;
|
|
if (l < 20)
|
|
{
|
|
memset (o, ' ', 20-l);
|
|
key[20] = 0;
|
|
}
|
|
else *o = 0;
|
|
Msg ("%s", key);
|
|
|
|
if (!*s)
|
|
{
|
|
Msg ("MISSING VALUE\n");
|
|
return;
|
|
}
|
|
|
|
o = value;
|
|
s++;
|
|
while (*s && *s != '\\') *o++ = *s++;
|
|
*o = 0;
|
|
|
|
if (*s) s++;
|
|
Msg ("%s\n", value);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Info_ValueForKey
|
|
|
|
Searches the string for the given
|
|
key and returns the associated value, or an empty string.
|
|
===============
|
|
*/
|
|
char *Info_ValueForKey (char *s, char *key)
|
|
{
|
|
char pkey[512];
|
|
static char value[2][512]; // use two buffers so compares work without stomping on each other
|
|
static int valueindex;
|
|
char *o;
|
|
|
|
valueindex ^= 1;
|
|
if (*s == '\\') s++;
|
|
while (1)
|
|
{
|
|
o = pkey;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s) return "";
|
|
*o++ = *s++;
|
|
}
|
|
*o = 0;
|
|
s++;
|
|
|
|
o = value[valueindex];
|
|
|
|
while (*s != '\\' && *s)
|
|
{
|
|
if (!*s) return "";
|
|
*o++ = *s++;
|
|
}
|
|
*o = 0;
|
|
|
|
if (!strcmp (key, pkey) ) return value[valueindex];
|
|
if (!*s) return "";
|
|
s++;
|
|
}
|
|
}
|
|
|
|
void Info_RemoveKey (char *s, char *key)
|
|
{
|
|
char *start;
|
|
char pkey[512];
|
|
char value[512];
|
|
char *o;
|
|
|
|
if (strstr (key, "\\")) return;
|
|
|
|
while (1)
|
|
{
|
|
start = s;
|
|
if (*s == '\\') s++;
|
|
o = pkey;
|
|
while (*s != '\\')
|
|
{
|
|
if (!*s) return;
|
|
*o++ = *s++;
|
|
}
|
|
*o = 0;
|
|
s++;
|
|
|
|
o = value;
|
|
while (*s != '\\' && *s)
|
|
{
|
|
if (!*s) return;
|
|
*o++ = *s++;
|
|
}
|
|
*o = 0;
|
|
|
|
if (!strcmp (key, pkey) )
|
|
{
|
|
strcpy (start, s); // remove this part
|
|
return;
|
|
}
|
|
if (!*s) return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Info_Validate
|
|
|
|
Some characters are illegal in info strings because they
|
|
can mess up the server's parsing
|
|
==================
|
|
*/
|
|
bool Info_Validate (char *s)
|
|
{
|
|
if (strstr (s, "\"")) return false;
|
|
if (strstr (s, ";")) return false;
|
|
return true;
|
|
}
|
|
|
|
void Info_SetValueForKey (char *s, char *key, char *value)
|
|
{
|
|
char newi[MAX_INFO_STRING], *v;
|
|
int c, maxsize = MAX_INFO_STRING;
|
|
|
|
if (strstr (key, "\\") || strstr (value, "\\") )
|
|
{
|
|
Msg ("Can't use keys or values with a \\\n");
|
|
return;
|
|
}
|
|
|
|
if (strstr (key, ";") )
|
|
{
|
|
Msg ("Can't use keys or values with a semicolon\n");
|
|
return;
|
|
}
|
|
if (strstr (key, "\"") || strstr (value, "\"") )
|
|
{
|
|
Msg ("Can't use keys or values with a \"\n");
|
|
return;
|
|
}
|
|
if (strlen(key) > MAX_INFO_KEY - 1 || strlen(value) > MAX_INFO_KEY-1)
|
|
{
|
|
Msg ("Keys and values must be < 64 characters.\n");
|
|
return;
|
|
}
|
|
|
|
Info_RemoveKey (s, key);
|
|
if (!value || !strlen(value)) return;
|
|
sprintf (newi, "\\%s\\%s", key, value);
|
|
|
|
if (strlen(newi) + strlen(s) > maxsize)
|
|
{
|
|
Msg ("Info string length exceeded\n");
|
|
return;
|
|
}
|
|
|
|
// only copy ascii values
|
|
s += strlen(s);
|
|
v = newi;
|
|
while (*v)
|
|
{
|
|
c = *v++;
|
|
c &= 127; // strip high bits
|
|
if (c >= 32 && c < 127) *s++ = c;
|
|
}
|
|
*s = 0;
|
|
}
|
|
|
|
static void Cvar_LookupBitInfo(const char *name, const char *string, const char *info, void *unused)
|
|
{
|
|
Info_SetValueForKey((char *)info, (char *)name, (char *)string);
|
|
}
|
|
|
|
char *Cvar_Userinfo (void)
|
|
{
|
|
sv_info[0] = 0; // clear previous calls
|
|
Cvar_LookupVars( CVAR_USERINFO, sv_info, NULL, Cvar_LookupBitInfo );
|
|
return sv_info;
|
|
}
|
|
|
|
char *Cvar_Serverinfo (void)
|
|
{
|
|
sv_info[0] = 0; // clear previous calls
|
|
Cvar_LookupVars( CVAR_SERVERINFO, sv_info, NULL, Cvar_LookupBitInfo );
|
|
return sv_info;
|
|
}
|
|
|
|
/*
|
|
=======================================================================
|
|
|
|
FILENAME AUTOCOMPLETION
|
|
=======================================================================
|
|
*/
|
|
/*
|
|
=====================================
|
|
Cmd_GetMapList
|
|
|
|
Prints or complete map filename
|
|
=====================================
|
|
*/
|
|
bool Cmd_GetMapList (const char *s, char *completedname, int length )
|
|
{
|
|
search_t *t;
|
|
file_t *f;
|
|
char message[MAX_QPATH];
|
|
char matchbuf[MAX_QPATH];
|
|
byte buf[MAX_SYSPATH]; // 1 kb
|
|
int i, nummaps;
|
|
|
|
t = FS_Search(va("maps/%s*.bsp", s), true );
|
|
if( !t ) return false;
|
|
|
|
FS_FileBase(t->filenames[0], matchbuf );
|
|
strncpy( completedname, matchbuf, length );
|
|
if(t->numfilenames == 1) return true;
|
|
|
|
for(i = 0, nummaps = 0; i < t->numfilenames; i++)
|
|
{
|
|
const char *data = NULL;
|
|
char *entities = NULL;
|
|
char entfilename[MAX_QPATH];
|
|
int ver = -1, lumpofs = 0, lumplen = 0;
|
|
const char *ext = FS_FileExtension( t->filenames[i] );
|
|
|
|
if( std.stricmp(ext, "bsp" )) continue;
|
|
|
|
strncpy(message, "^1error^7", sizeof(message));
|
|
f = FS_Open(t->filenames[i], "rb" );
|
|
|
|
if( f )
|
|
{
|
|
memset(buf, 0, 1024);
|
|
FS_Read(f, buf, 1024);
|
|
if(!memcmp(buf, "IBSP", 4))
|
|
{
|
|
dheader_t *header = (dheader_t *)buf;
|
|
ver = LittleLong(((int *)buf)[1]);
|
|
|
|
switch(ver)
|
|
{
|
|
case 38: // quake2 (xash)
|
|
case 46: // quake3
|
|
case 47: // return to castle wolfenstein
|
|
lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
|
|
lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lump_t ents; // quake1 entity lump
|
|
memcpy(&ents, buf + 4, sizeof(lump_t)); // skip first four bytes (version)
|
|
ver = LittleLong(((int *)buf)[0]);
|
|
|
|
switch( ver )
|
|
{
|
|
case 28: // quake 1 beta
|
|
case 29: // quake 1 regular
|
|
case 30: // Half-Life regular
|
|
lumpofs = LittleLong(ents.fileofs);
|
|
lumplen = LittleLong(ents.filelen);
|
|
break;
|
|
default:
|
|
ver = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
std.strncpy(entfilename, t->filenames[i], sizeof(entfilename));
|
|
FS_StripExtension( entfilename );
|
|
FS_DefaultExtension( entfilename, ".ent" );
|
|
entities = (char *)FS_LoadFile(entfilename, NULL);
|
|
|
|
if( !entities && lumplen >= 10 )
|
|
{
|
|
FS_Seek(f, lumpofs, SEEK_SET);
|
|
entities = (char *)Z_Malloc(lumplen + 1);
|
|
FS_Read(f, entities, lumplen);
|
|
}
|
|
|
|
if( entities )
|
|
{
|
|
// if there are entities to parse, a missing message key just
|
|
// means there is no title, so clear the message string now
|
|
message[0] = 0;
|
|
data = entities;
|
|
while(Com_ParseToken(&data))
|
|
{
|
|
if(!strcmp(com_token, "{" )) continue;
|
|
else if(!strcmp(com_token, "}" )) break;
|
|
else if(!strcmp(com_token, "message" ))
|
|
{
|
|
// get the message contents
|
|
Com_ParseToken(&data);
|
|
strncpy(message, com_token, sizeof(message));
|
|
}
|
|
else if(!strcmp(com_token, "mapversion" ))
|
|
{
|
|
// get map version
|
|
Com_ParseToken(&data);
|
|
// old xash maps are Half-Life, so don't overwrite version
|
|
if(ver > 30) ver = atoi(com_token);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if( entities )Z_Free(entities);
|
|
if( f )FS_Close(f);
|
|
FS_FileBase(t->filenames[i], matchbuf );
|
|
|
|
switch(ver)
|
|
{
|
|
case 28: strncpy((char *)buf, "Quake1 beta", sizeof(buf)); break;
|
|
case 29: strncpy((char *)buf, "Quake1", sizeof(buf)); break;
|
|
case 30: strncpy((char *)buf, "Half-Life", sizeof(buf)); break;
|
|
case 38: strncpy((char *)buf, "Quake 2", sizeof(buf)); break;
|
|
case 46: strncpy((char *)buf, "Quake 3", sizeof(buf)); break;
|
|
case 47: strncpy((char *)buf, "RTCW", sizeof(buf)); break;
|
|
case 220: strncpy((char *)buf, "Xash 3D", sizeof(buf)); break;
|
|
default: strncpy((char *)buf, "??", sizeof(buf)); break;
|
|
}
|
|
Msg("%16s (%s) ^3%s^7\n", matchbuf, buf, message);
|
|
nummaps++;
|
|
}
|
|
Msg("\n^3 %i maps found.\n", nummaps );
|
|
Z_Free( t );
|
|
|
|
// cut shortestMatch to the amount common with s
|
|
for( i = 0; matchbuf[i]; i++ )
|
|
{
|
|
if(tolower(completedname[i]) != tolower(matchbuf[i]))
|
|
completedname[i] = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=====================================
|
|
Cmd_GetDemoList
|
|
|
|
Prints or complete demo filename
|
|
=====================================
|
|
*/
|
|
bool Cmd_GetDemoList (const char *s, char *completedname, int length )
|
|
{
|
|
search_t *t;
|
|
char matchbuf[MAX_QPATH];
|
|
int i, numdems;
|
|
|
|
t = FS_Search(va("demos/%s*.dem", s ), true);
|
|
if(!t) return false;
|
|
|
|
FS_FileBase(t->filenames[0], matchbuf );
|
|
if(completedname && length) strncpy( completedname, matchbuf, length );
|
|
if(t->numfilenames == 1) return true;
|
|
|
|
for(i = 0, numdems = 0; i < t->numfilenames; i++)
|
|
{
|
|
const char *ext = FS_FileExtension( t->filenames[i] );
|
|
|
|
if( std.stricmp(ext, "dem" )) continue;
|
|
FS_FileBase(t->filenames[i], matchbuf );
|
|
Msg("%16s\n", matchbuf );
|
|
numdems++;
|
|
}
|
|
Msg("\n^3 %i demos found.\n", numdems );
|
|
Z_Free(t);
|
|
|
|
// cut shortestMatch to the amount common with s
|
|
if(completedname && length)
|
|
{
|
|
for( i = 0; matchbuf[i]; i++ )
|
|
{
|
|
if(tolower(completedname[i]) != tolower(matchbuf[i]))
|
|
completedname[i] = 0;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=====================================
|
|
Cmd_GetMovieList
|
|
|
|
Prints or complete movie filename
|
|
=====================================
|
|
*/
|
|
bool Cmd_GetMovieList (const char *s, char *completedname, int length )
|
|
{
|
|
search_t *t;
|
|
char matchbuf[MAX_QPATH];
|
|
int i, nummovies;
|
|
|
|
t = FS_Search(va("video/%s*.roq", s ), true);
|
|
if(!t) return false;
|
|
|
|
FS_FileBase(t->filenames[0], matchbuf );
|
|
if(completedname && length) strncpy( completedname, matchbuf, length );
|
|
if(t->numfilenames == 1) return true;
|
|
|
|
for(i = 0, nummovies = 0; i < t->numfilenames; i++)
|
|
{
|
|
const char *ext = FS_FileExtension( t->filenames[i] );
|
|
|
|
if( std.stricmp(ext, "roq" )) continue;
|
|
FS_FileBase(t->filenames[i], matchbuf );
|
|
Msg("%16s\n", matchbuf );
|
|
nummovies++;
|
|
}
|
|
Msg("\n^3 %i movies found.\n", nummovies );
|
|
Z_Free(t);
|
|
|
|
// cut shortestMatch to the amount common with s
|
|
if(completedname && length)
|
|
{
|
|
for( i = 0; matchbuf[i]; i++ )
|
|
{
|
|
if(tolower(completedname[i]) != tolower(matchbuf[i]))
|
|
completedname[i] = 0;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cmd_WriteVariables
|
|
|
|
Appends lines containing "set variable value" for all variables
|
|
with the archive flag set to true.
|
|
============
|
|
*/
|
|
|
|
static void Cmd_WriteCvar(const char *name, const char *string, const char *unused, void *f)
|
|
{
|
|
FS_Printf(f, "seta %s \"%s\"\n", name, string );
|
|
}
|
|
|
|
void Cmd_WriteVariables( file_t *f )
|
|
{
|
|
FS_Printf (f, "unsetall\n" );
|
|
Cvar_LookupVars( CVAR_ARCHIVE, NULL, f, Cmd_WriteCvar );
|
|
}
|
|
|
|
float frand(void)
|
|
{
|
|
return (rand()&32767)* (1.0/32767);
|
|
}
|
|
|
|
float crand(void)
|
|
{
|
|
return (rand()&32767)* (2.0/32767) - 1;
|
|
} |