jedi-academy/codemp/client/cl_parse.cpp
Jonathan Gray 61687fff0c CVE-2011-2764/CVE-2011-3012 check for dangerous file extensions
CVE-2011-2764
The FS_CheckFilenameIsNotExecutable function in qcommon/files.c in the
ioQuake3 engine 1.36 and earlier, as used in World of Padman, Smokin'
Guns, OpenArena, Tremulous, and ioUrbanTerror, does not properly
determine dangerous file extensions, which allows remote attackers to
execute arbitrary code via a crafted third-party addon that creates a
Trojan horse DLL file.

CVE-2011-3012
The ioQuake3 engine, as used in World of Padman 1.2 and earlier,
Tremulous 1.1.0, and ioUrbanTerror 2007-12-20, does not check for
dangerous file extensions before writing to the quake3 directory, which
allows remote attackers to execute arbitrary code via a crafted
third-party addon that creates a Trojan horse DLL file, a different
vulnerability than CVE-2011-2764.

bugzilla #3695

from Tim Angus in ioquake3
svn 1405 git 2c0861c1cea44861c5ceba2dc39e601d6bc3f0af

* (bug 3695) Not allowing to write file with lib extention (.dll/.so/...)
  (TsT <tst2006@gmail.com>)

from Tim Angus in ioquake3
svn 1499 git 48d8c8876b6ec035b0bb85f4d3c47c9210c3ca30

* s/FS_FilenameIsExecutable/FS_CheckFilenameIsNotExecutable/g
* Fix potential buffer under run in FS_CheckFilenameIsNotExecutable

from Thilo Schulz in ioquake3
svn 2098 git c4f739b8d03ca203435744c4a96e3561863ccdfe

Fix extension name comparison for DLL files

from Zack Middleton in ioquake3
git 6c88bf8aeee3c1e5449682f874f91e86cb393ef4

Rename FS_CheckFilenameIsNotExecutable to ..NotImmutable

from Harley Laue in ioquake3
git 1b2a6abed996b43eb108486abbda449b3d16e019

Rename FS_CheckFilenameIsNotImmutable to ..IsMutable
2013-05-07 22:20:02 +10:00

1065 lines
26 KiB
C++

//Anything above this #include will be ignored by the compiler
#include "../qcommon/exe_headers.h"
// cl_parse.c -- parse a message received from the server
#include "client.h"
#include "../qcommon/stringed_ingame.h"
#include "../ghoul2/G2_local.h"
#ifdef _DONETPROFILE_
#include "../qcommon/INetProfile.h"
#endif
#include "../zlib32/zip.h"
#ifdef _XBOX
#include "../xbox/XBLive.h"
#include "../xbox/XBoxCommon.h"
#include "../xbox/XBVoice.h"
#endif
static char hiddenCvarVal[128];
char *svc_strings[256] = {
"svc_bad",
"svc_nop",
"svc_gamestate",
"svc_configstring",
"svc_baseline",
"svc_serverCommand",
"svc_download",
"svc_snapshot",
"svc_setgame",
"svc_mapchange",
#ifdef _XBOX
"svc_newpeer",
"svc_removepeer",
"svc_xbInfo",
#endif
};
void SHOWNET( msg_t *msg, char *s) {
if ( cl_shownet->integer >= 2) {
Com_Printf ("%3i:%s\n", msg->readcount-1, s);
}
}
//void CL_SP_Print(const word ID, byte *Data); //, char* color)
/*
=========================================================================
MESSAGE PARSING
=========================================================================
*/
/*
==================
CL_DeltaEntity
Parses deltas from the given base and adds the resulting entity
to the current frame
==================
*/
void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old,
qboolean unchanged) {
entityState_t *state;
// save the parsed entity state into the big circular buffer so
// it can be used as the source for a later delta
state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)];
if ( unchanged )
{
*state = *old;
}
else
{
MSG_ReadDeltaEntity( msg, old, state, newnum );
}
if ( state->number == (MAX_GENTITIES-1) ) {
return; // entity was delta removed
}
cl.parseEntitiesNum++;
frame->numEntities++;
}
/*
==================
CL_ParsePacketEntities
==================
*/
void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) {
int newnum;
entityState_t *oldstate;
int oldindex, oldnum;
newframe->parseEntitiesNum = cl.parseEntitiesNum;
newframe->numEntities = 0;
// delta from the entities present in oldframe
oldindex = 0;
oldstate = NULL;
if (!oldframe) {
oldnum = 99999;
} else {
if ( oldindex >= oldframe->numEntities ) {
oldnum = 99999;
} else {
oldstate = &cl.parseEntities[
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
oldnum = oldstate->number;
}
}
while ( 1 ) {
// read the entity index number
newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
if ( newnum == (MAX_GENTITIES-1) ) {
break;
}
if ( msg->readcount > msg->cursize ) {
Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");
}
while ( oldnum < newnum ) {
// one or more entities from the old packet are unchanged
if ( cl_shownet->integer == 3 ) {
Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum);
}
CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
oldindex++;
if ( oldindex >= oldframe->numEntities ) {
oldnum = 99999;
} else {
oldstate = &cl.parseEntities[
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
oldnum = oldstate->number;
}
}
if (oldnum == newnum) {
// delta from previous state
if ( cl_shownet->integer == 3 ) {
Com_Printf ("%3i: delta: %i\n", msg->readcount, newnum);
}
CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse );
oldindex++;
if ( oldindex >= oldframe->numEntities ) {
oldnum = 99999;
} else {
oldstate = &cl.parseEntities[
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
oldnum = oldstate->number;
}
continue;
}
if ( oldnum > newnum ) {
// delta from baseline
if ( cl_shownet->integer == 3 ) {
Com_Printf ("%3i: baseline: %i\n", msg->readcount, newnum);
}
CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse );
continue;
}
}
// any remaining entities in the old frame are copied over
while ( oldnum != 99999 ) {
// one or more entities from the old packet are unchanged
if ( cl_shownet->integer == 3 ) {
Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum);
}
CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
oldindex++;
if ( oldindex >= oldframe->numEntities ) {
oldnum = 99999;
} else {
oldstate = &cl.parseEntities[
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
oldnum = oldstate->number;
}
}
}
/*
================
CL_ParseSnapshot
If the snapshot is parsed properly, it will be copied to
cl.snap and saved in cl.snapshots[]. If the snapshot is invalid
for any reason, no changes to the state will be made at all.
================
*/
void CL_ParseSnapshot( msg_t *msg ) {
int len;
clSnapshot_t *old;
clSnapshot_t newSnap;
int deltaNum;
int oldMessageNum;
int i, packetNum;
// get the reliable sequence acknowledge number
// NOTE: now sent with all server to client messages
//clc.reliableAcknowledge = MSG_ReadLong( msg );
// read in the new snapshot to a temporary buffer
// we will only copy to cl.snap if it is valid
Com_Memset (&newSnap, 0, sizeof(newSnap));
// we will have read any new server commands in this
// message before we got to svc_snapshot
newSnap.serverCommandNum = clc.serverCommandSequence;
newSnap.serverTime = MSG_ReadLong( msg );
newSnap.messageNum = clc.serverMessageSequence;
deltaNum = MSG_ReadByte( msg );
if ( !deltaNum ) {
newSnap.deltaNum = -1;
} else {
newSnap.deltaNum = newSnap.messageNum - deltaNum;
}
newSnap.snapFlags = MSG_ReadByte( msg );
// If the frame is delta compressed from data that we
// no longer have available, we must suck up the rest of
// the frame, but not use it, then ask for a non-compressed
// message
if ( newSnap.deltaNum <= 0 ) {
newSnap.valid = qtrue; // uncompressed frame
old = NULL;
#ifndef _XBOX // No demos on Xbox
clc.demowaiting = qfalse; // we can start recording now
#endif
} else {
old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
if ( !old->valid ) {
// should never happen
Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
} else if ( old->messageNum != newSnap.deltaNum ) {
// The frame that the server did the delta from
// is too old, so we can't reconstruct it properly.
Com_Printf ("Delta frame too old.\n");
} else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) {
Com_DPrintf ("Delta parseEntitiesNum too old.\n");
} else {
newSnap.valid = qtrue; // valid delta parse
}
}
// read areamask
len = MSG_ReadByte( msg );
if(len > sizeof(newSnap.areamask))
{
Com_Error (ERR_DROP,"CL_ParseSnapshot: Invalid size %d for areamask", len);
return;
}
MSG_ReadData( msg, &newSnap.areamask, len);
// read playerinfo
SHOWNET( msg, "playerstate" );
if ( old ) {
MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps );
if (newSnap.ps.m_iVehicleNum)
{ //this means we must have written our vehicle's ps too
MSG_ReadDeltaPlayerstate( msg, &old->vps, &newSnap.vps, qtrue );
}
} else {
MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps );
if (newSnap.ps.m_iVehicleNum)
{ //this means we must have written our vehicle's ps too
MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.vps, qtrue );
}
}
// read packet entities
SHOWNET( msg, "packet entities" );
CL_ParsePacketEntities( msg, old, &newSnap );
// if not valid, dump the entire thing now that it has
// been properly read
if ( !newSnap.valid ) {
return;
}
// clear the valid flags of any snapshots between the last
// received and this one, so if there was a dropped packet
// it won't look like something valid to delta from next
// time we wrap around in the buffer
oldMessageNum = cl.snap.messageNum + 1;
if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) {
oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 );
}
for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) {
cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse;
}
// copy to the current good spot
cl.snap = newSnap;
cl.snap.ping = 999;
// calculate ping time
for ( i = 0 ; i < PACKET_BACKUP ; i++ ) {
packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK;
if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) {
cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime;
break;
}
}
// save the frame off in the backup array for later delta comparisons
cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap;
if (cl_shownet->integer == 3) {
Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum,
cl.snap.deltaNum, cl.snap.ping );
}
cl.newSnapshots = qtrue;
}
/*
================
CL_ParseSetGame
rww - Update fs_game, this message is so we can use the ext_data
*_overrides.txt files for mods.
================
*/
void MSG_CheckNETFPSFOverrides(qboolean psfOverrides);
void FS_UpdateGamedir(void);
void CL_ParseSetGame( msg_t *msg )
{
char newGameDir[MAX_QPATH];
int i = 0;
char next;
while (i < MAX_QPATH)
{
next = MSG_ReadByte( msg );
if (next)
{ //if next is 0 then we have finished reading to the end of the message
newGameDir[i] = next;
}
else
{
break;
}
i++;
}
newGameDir[i] = 0;
Cvar_Set("fs_game", newGameDir);
//Update the search path for the mod dir
FS_UpdateGamedir();
//Now update the overrides manually
#ifndef _XBOX // No mods on Xbox
MSG_CheckNETFPSFOverrides(qfalse);
MSG_CheckNETFPSFOverrides(qtrue);
#endif
}
//=====================================================================
int cl_connectedToPureServer;
int cl_connectedGAME;
int cl_connectedCGAME;
int cl_connectedUI;
/*
==================
CL_SystemInfoChanged
The systeminfo configstring has been changed, so parse
new information out of it. This will happen at every
gamestate, and possibly during gameplay.
==================
*/
void CL_SystemInfoChanged( void ) {
char *systemInfo;
const char *s, *t;
char key[BIG_INFO_KEY];
char value[BIG_INFO_VALUE];
qboolean gameSet;
systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ];
cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
// don't set any vars when playing a demo
#ifndef _XBOX // No demos on Xbox
if ( clc.demoplaying ) {
return;
}
#endif
s = Info_ValueForKey( systemInfo, "sv_cheats" );
if ( atoi(s) == 0 )
{
Cvar_SetCheatState();
}
// check pure server string
s = Info_ValueForKey( systemInfo, "sv_paks" );
t = Info_ValueForKey( systemInfo, "sv_pakNames" );
FS_PureServerSetLoadedPaks( s, t );
s = Info_ValueForKey( systemInfo, "sv_referencedPaks" );
t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" );
FS_PureServerSetReferencedPaks( s, t );
gameSet = qfalse;
// scan through all the variables in the systeminfo and locally set cvars to match
s = systemInfo;
while ( s ) {
int cvar_flags;
Info_NextPair( &s, key, value );
if ( !key[0] ) {
break;
}
// ehw!
if ( !Q_stricmp( key, "fs_game" ) )
{
if(FS_CheckDirTraversal(value))
{
Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", value);
continue;
}
gameSet = qtrue;
}
if((cvar_flags = Cvar_Flags(key)) == CVAR_NONEXISTENT)
Cvar_Get(key, value, CVAR_SERVER_CREATED | CVAR_ROM);
else
{
// If this cvar may not be modified by a server discard the value.
if(!(cvar_flags & (CVAR_SYSTEMINFO | CVAR_SERVER_CREATED)))
continue;
Cvar_Set(key, value);
}
Cvar_Set( key, value );
}
// if game folder should not be set and it is set at the client side
if (!gameSet && *Cvar_VariableString("fs_game") ) {
Cvar_Set( "fs_game", "" );
}
cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" );
cl_connectedGAME = atoi(Info_ValueForKey( systemInfo, "vm_game" ));
cl_connectedCGAME = atoi(Info_ValueForKey( systemInfo, "vm_cgame" ));
cl_connectedUI = atoi(Info_ValueForKey( systemInfo, "vm_ui" ));
}
void CL_ParseAutomapSymbols ( msg_t* msg )
{
int i;
clc.rmgAutomapSymbolCount = (unsigned short) MSG_ReadShort ( msg );
for ( i = 0; i < clc.rmgAutomapSymbolCount; i ++ )
{
clc.rmgAutomapSymbols[i].mType = (int)MSG_ReadByte ( msg );
clc.rmgAutomapSymbols[i].mSide = (int)MSG_ReadByte ( msg );
clc.rmgAutomapSymbols[i].mOrigin[0] = (float)MSG_ReadLong ( msg );
clc.rmgAutomapSymbols[i].mOrigin[1] = (float)MSG_ReadLong ( msg );
}
}
void CL_ParseRMG ( msg_t* msg )
{
clc.rmgHeightMapSize = (unsigned short)MSG_ReadShort ( msg );
if ( !clc.rmgHeightMapSize )
{
return;
}
z_stream zdata;
int size;
unsigned char heightmap1[15000];
if ( MSG_ReadBits ( msg, 1 ) )
{
// Read the heightmap
memset(&zdata, 0, sizeof(z_stream));
inflateInit ( &zdata, Z_SYNC_FLUSH );
MSG_ReadData ( msg, heightmap1, clc.rmgHeightMapSize );
zdata.next_in = heightmap1;
zdata.avail_in = clc.rmgHeightMapSize;
zdata.next_out = (unsigned char*)clc.rmgHeightMap;
zdata.avail_out = MAX_HEIGHTMAP_SIZE;
inflate (&zdata);
clc.rmgHeightMapSize = zdata.total_out;
inflateEnd(&zdata);
}
else
{
MSG_ReadData ( msg, (unsigned char*)clc.rmgHeightMap, clc.rmgHeightMapSize );
}
size = (unsigned short)MSG_ReadShort ( msg );
if ( MSG_ReadBits ( msg, 1 ) )
{
// Read the flatten map
memset(&zdata, 0, sizeof(z_stream));
inflateInit ( &zdata, Z_SYNC_FLUSH );
MSG_ReadData ( msg, heightmap1, size );
zdata.next_in = heightmap1;
zdata.avail_in = clc.rmgHeightMapSize;
zdata.next_out = (unsigned char*)clc.rmgFlattenMap;
zdata.avail_out = MAX_HEIGHTMAP_SIZE;
inflate (&zdata );
inflateEnd(&zdata);
}
else
{
MSG_ReadData ( msg, (unsigned char*)clc.rmgFlattenMap, size );
}
// Read the seed
clc.rmgSeed = MSG_ReadLong ( msg );
CL_ParseAutomapSymbols ( msg );
}
/*
==================
CL_ParseGamestate
==================
*/
void CL_ParseGamestate( msg_t *msg ) {
int i;
entityState_t *es;
int newnum;
entityState_t nullstate;
int cmd;
char *s;
Con_Close();
clc.connectPacketCount = 0;
// wipe local client state
CL_ClearState();
#ifdef _DONETPROFILE_
int startBytes,endBytes;
startBytes=msg->readcount;
#endif
// a gamestate always marks a server command sequence
clc.serverCommandSequence = MSG_ReadLong( msg );
// parse all the configstrings and baselines
cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings
while ( 1 ) {
cmd = MSG_ReadByte( msg );
if ( cmd == svc_EOF ) {
break;
}
if ( cmd == svc_configstring ) {
int len, start;
start = msg->readcount;
i = MSG_ReadShort( msg );
if ( i < 0 || i >= MAX_CONFIGSTRINGS ) {
Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
}
s = MSG_ReadBigString( msg );
if (cl_shownet->integer >= 2)
{
Com_Printf("%3i: %d: %s\n", start, i, s);
}
/*
if (i == CS_SERVERINFO)
{ //get the special value here
char *f = strstr(s, "g_debugMelee");
if (f)
{
while (*f && *f != '\\')
{ //find the \ after it
f++;
}
if (*f == '\\')
{ //got it
int i = 0;
f++;
while (*f && *f != '\\' && i < 128)
{
hiddenCvarVal[i] = *f;
i++;
f++;
}
hiddenCvarVal[i] = 0;
//resume here
s = f;
}
}
}
*/
len = strlen( s );
if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
}
// append it to the gameState string buffer
cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 );
cl.gameState.dataCount += len + 1;
} else if ( cmd == svc_baseline ) {
newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
if ( newnum < 0 || newnum >= MAX_GENTITIES ) {
Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum );
}
Com_Memset (&nullstate, 0, sizeof(nullstate));
es = &cl.entityBaselines[ newnum ];
MSG_ReadDeltaEntity( msg, &nullstate, es, newnum );
} else {
Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" );
}
}
clc.clientNum = MSG_ReadLong(msg);
// read the checksum feed
clc.checksumFeed = MSG_ReadLong( msg );
CL_ParseRMG ( msg ); //rwwRMG - get info for it from the server
#ifdef _DONETPROFILE_
endBytes=msg->readcount;
// ClReadProf().AddField("svc_gamestate",endBytes-startBytes);
#endif
// parse serverId and other cvars
CL_SystemInfoChanged();
// reinitialize the filesystem if the game directory has changed
if( FS_ConditionalRestart( clc.checksumFeed ) ) {
// don't set to true because we yet have to start downloading
// enabling this can cause double loading of a map when connecting to
// a server which has a different game directory set
//clc.downloadRestart = qtrue;
}
// This used to call CL_StartHunkUsers, but now we enter the download state before loading the
// cgame
CL_InitDownloads();
// make sure the game starts
Cvar_Set( "cl_paused", "0" );
}
//=====================================================================
/*
=====================
CL_ParseDownload
A download message has been received from the server
=====================
*/
void CL_ParseDownload ( msg_t *msg ) {
#ifdef _XBOX
assert(!"Xbox received a download message. Unsupported!");
#else
int size;
unsigned char data[MAX_MSGLEN];
int block;
if (!*clc.downloadTempName) {
Com_Printf("Server sending download, but no download was requested\n");
CL_AddReliableCommand("stopdl");
return;
}
// read the data
block = (unsigned short)MSG_ReadShort ( msg );
if ( !block )
{
// block zero is special, contains file size
clc.downloadSize = MSG_ReadLong ( msg );
Cvar_SetValue( "cl_downloadSize", clc.downloadSize );
if (clc.downloadSize < 0)
{
Com_Error(ERR_DROP, MSG_ReadString( msg ) );
return;
}
}
size = (unsigned short)MSG_ReadShort ( msg );
if (size < 0 || size > sizeof(data))
{
Com_Error(ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk", size);
return;
}
MSG_ReadData( msg, data, size );
if (clc.downloadBlock != block) {
Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block);
return;
}
// open the file if not opened yet
if (!clc.download)
{
clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName );
if (!clc.download) {
Com_Printf( "Could not create %s\n", clc.downloadTempName );
CL_AddReliableCommand( "stopdl" );
CL_NextDownload();
return;
}
}
if (size)
FS_Write( data, size, clc.download );
CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock) );
clc.downloadBlock++;
clc.downloadCount += size;
// So UI gets access to it
Cvar_SetValue( "cl_downloadCount", clc.downloadCount );
if (!size) { // A zero length block means EOF
if (clc.download) {
FS_FCloseFile( clc.download );
clc.download = 0;
// rename the file
FS_SV_Rename ( clc.downloadTempName, clc.downloadName, qfalse );
}
*clc.downloadTempName = *clc.downloadName = 0;
Cvar_Set( "cl_downloadName", "" );
// send intentions now
// We need this because without it, we would hold the last nextdl and then start
// loading right away. If we take a while to load, the server is happily trying
// to send us that last block over and over.
// Write it twice to help make sure we acknowledge the download
CL_WritePacket();
CL_WritePacket();
// get another file if needed
CL_NextDownload ();
}
#endif
}
int CL_GetValueForHidden(const char *s)
{ //string arg here just in case I want to add more sometime and make a lookup table
return atoi(hiddenCvarVal);
}
/*
=====================
CL_ParseCommandString
Command strings are just saved off until cgame asks for them
when it transitions a snapshot
=====================
*/
void CL_ParseCommandString( msg_t *msg ) {
char *s;
int seq;
int index;
#ifdef _DONETPROFILE_
int startBytes,endBytes;
startBytes=msg->readcount;
#endif
seq = MSG_ReadLong( msg );
s = MSG_ReadString( msg );
#ifdef _DONETPROFILE_
endBytes=msg->readcount;
ClReadProf().AddField("svc_serverCommand",endBytes-startBytes);
#endif
// see if we have already executed stored it off
if ( clc.serverCommandSequence >= seq ) {
return;
}
clc.serverCommandSequence = seq;
index = seq & (MAX_RELIABLE_COMMANDS-1);
/*
if (s[0] == 'c' && s[1] == 's' && s[2] == ' ' && s[3] == '0' && s[4] == ' ')
{ //yes.. we seem to have an incoming server info.
char *f = strstr(s, "g_debugMelee");
if (f)
{
while (*f && *f != '\\')
{ //find the \ after it
f++;
}
if (*f == '\\')
{ //got it
int i = 0;
f++;
while (*f && *f != '\\' && i < 128)
{
hiddenCvarVal[i] = *f;
i++;
f++;
}
hiddenCvarVal[i] = 0;
//don't worry about backing over beginning of string I guess,
//we already know we successfully strstr'd the initial string
//which exceeds this length.
//MSG_ReadString appears to just return a static buffer so I
//can stomp over its contents safely.
f--;
*f = '\"';
f--;
*f = ' ';
f--;
*f = '0';
f--;
*f = ' ';
f--;
*f = 's';
f--;
*f = 'c';
//the normal configstring gets to start here...
s = f;
}
}
}
*/
Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) );
}
/*
=====================
CL_ParseServerMessage
=====================
*/
void CL_ParseServerMessage( msg_t *msg ) {
int cmd;
if ( cl_shownet->integer == 1 ) {
Com_Printf ("%i ",msg->cursize);
} else if ( cl_shownet->integer >= 2 ) {
Com_Printf ("------------------\n");
}
MSG_Bitstream(msg);
// get the reliable sequence acknowledge number
clc.reliableAcknowledge = MSG_ReadLong( msg );
//
if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) {
clc.reliableAcknowledge = clc.reliableSequence;
}
//
// parse the message
//
while ( 1 ) {
if ( msg->readcount > msg->cursize ) {
Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message");
break;
}
cmd = MSG_ReadByte( msg );
if ( cmd == svc_EOF) {
SHOWNET( msg, "END OF MESSAGE" );
break;
}
if ( cl_shownet->integer >= 2 ) {
if ( !svc_strings[cmd] ) {
Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd );
} else {
SHOWNET( msg, svc_strings[cmd] );
}
}
// other commands
switch ( cmd ) {
default:
Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
break;
case svc_nop:
break;
case svc_serverCommand:
CL_ParseCommandString( msg );
break;
case svc_gamestate:
CL_ParseGamestate( msg );
break;
case svc_snapshot:
CL_ParseSnapshot( msg );
break;
case svc_setgame:
CL_ParseSetGame( msg );
break;
case svc_download:
CL_ParseDownload( msg );
break;
case svc_mapchange:
if (cgvm)
{
VM_Call( cgvm, CG_MAP_CHANGE );
}
break;
#ifdef _XBOX
case svc_newpeer:
{ //jsw// new client to add to our XBonlineInfo
// We now get the index that we should use to store this from the server
// That ensures that our playerlist and clientinfo stay in sync!
int index = MSG_ReadLong(msg);
// Sanity check - server shouldn't have us overwriting an active player
// Unless, we're the server, in which case it will be active. Doh.
assert( com_sv_running->integer ||
!xbOnlineInfo.xbPlayerList[index].isActive );
// OK. Read directly into the right place
MSG_ReadData(msg, &xbOnlineInfo.xbPlayerList[index], sizeof(XBPlayerInfo));
// Need to do address conversion here, voice needs it
XNetXnAddrToInAddr( &xbOnlineInfo.xbPlayerList[index].xbAddr,
Net_GetXNKID(),
&xbOnlineInfo.xbPlayerList[index].inAddr );
// VVFIXME - Search for g_Voice here - lots of stuff we're not doing
// g_Voice.OnPlayerJoined( index );
//if(g_Voice.IsPlayerMuted(&xbOnlineInfo.xbPlayerList[index].xuid))
//{
// g_Voice.MutePlayer(&xbOnlineInfo.xbPlayerList[index].xbAddr, &xbOnlineInfo.xbPlayerList[index].xuid, false);
// xbOnlineInfo.xbPlayerList[index].flags |= MUTED_PLAYER;
//}
//else if(Cvar_Get(CVAR_XBL_WT_ACTIVE)->integer)
//{ //reset w/t in case he's on the channel
// g_Voice.EnableWalkieTalkie();
//}
XBL_PL_CheckHistoryList(&xbOnlineInfo.xbPlayerList[index]);
break;
}
case svc_removepeer:
{
// Remove a client from our xbOnlineInfo. Our ordering is the same
// as the server, so we just get an index.
int index = MSG_ReadLong(msg);
// Sanity check
assert( xbOnlineInfo.xbPlayerList[index].isActive &&
index != xbOnlineInfo.localIndex );
// Clear out important information
xbOnlineInfo.xbPlayerList[index].isActive = false;
g_Voice.OnPlayerDisconnect( &xbOnlineInfo.xbPlayerList[index] );
XBL_PL_RemoveActivePeer(&xbOnlineInfo.xbPlayerList[index].xuid, index);
break;
}
case svc_xbInfo:
{ //jsw//get XNADDR list from server
MSG_ReadData(msg, &xbOnlineInfo, sizeof(XBOnlineInfo));
// Immediately convert everyone's XNADDR to an IN_ADDR
for( int i = 0; i < MAX_ONLINE_PLAYERS; ++i )
{
if( xbOnlineInfo.xbPlayerList[i].isActive )
XNetXnAddrToInAddr( &xbOnlineInfo.xbPlayerList[i].xbAddr,
Net_GetXNKID(),
&xbOnlineInfo.xbPlayerList[i].inAddr );
}
//now find which entry we are
if( logged_on )
{
PXONLINE_USER pToUser = XOnlineGetLogonUsers();
BOOL found = false;
for( int i = 0; i < MAX_ONLINE_PLAYERS && !found; i++ )
{
if( xbOnlineInfo.xbPlayerList[i].isActive && XOnlineAreUsersIdentical(&pToUser->xuid, &xbOnlineInfo.xbPlayerList[i].xuid))
{
xbOnlineInfo.localIndex = i;
found = true;
}
}
// OK. Time to join the chat! - VVFIXME - need to check mute lists
g_Voice.JoinSession();
}
else
{
DWORD dwStatus;
IN_ADDR localAddr;
XNetXnAddrToInAddr(Net_GetXNADDR( &dwStatus ), Net_GetXNKID(), &localAddr);
if( dwStatus != XNET_GET_XNADDR_NONE )
{
int j;
for( j = 0; j < MAX_ONLINE_PLAYERS; ++j )
{
if( xbOnlineInfo.xbPlayerList[j].isActive &&
localAddr.s_addr == xbOnlineInfo.xbPlayerList[i].inAddr.s_addr )
break;
}
xbOnlineInfo.localIndex = (j == MAX_ONLINE_PLAYERS) ? -1 : j;
}
}
break;
}
#endif
}
}
}
extern int scr_center_y;
void SCR_CenterPrint (char *str);//, PalIdx_t colour)