This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/common.c

521 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
=======================================================================
*/
/*
===============
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;
}
char *Cvar_BitInfo (int bit)
{
static char info[MAX_INFO_STRING];
cvar_t *var;
info[0] = 0;
for (var = cvar_vars; var; var = var->next)
{
if (var->flags & bit) Info_SetValueForKey (info, var->name, var->string);
}
return info;
}
char *Cvar_Userinfo (void)
{
return Cvar_BitInfo (CVAR_USERINFO);
}
char *Cvar_Serverinfo (void)
{
return Cvar_BitInfo (CVAR_SERVERINFO);
}
/*
=======================================================================
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.
============
*/
void Cmd_WriteVariables( file_t *f )
{
cvar_t *var;
char buffer[1024];
FS_Printf (f, "unsetall\n" );
for(var = cvar_vars; var; var = var->next)
{
if( var->flags & CVAR_ARCHIVE )
{
// write the latched value, even if it hasn't taken effect yet
if(!var->latched_string ) sprintf(buffer, "seta %s \"%s\"\n", var->name, var->string);
else sprintf(buffer, "seta %s \"%s\"\n", var->name, var->latched_string);
FS_Printf (f, "%s", buffer);
}
}
}
float frand(void)
{
return (rand()&32767)* (1.0/32767);
}
float crand(void)
{
return (rand()&32767)* (2.0/32767) - 1;
}