jedi-academy/codemp/client/cl_parse.cpp
Jonathan Gray 8550620849 CVE-2006-3325 arbitrary cvar overwrite
CVE-2006-3325
client/cl_parse.c in the id3 Quake 3 Engine 1.32c and the Icculus
Quake 3 Engine (ioquake3) revision 810 and earlier allows remote
malicious servers to overwrite arbitrary write-protected cvars
variables on the client, such as cl_allowdownload for Automatic
Downloading and fs_homepath for the quake3 path, via a string of cvar
names and values sent from the server. NOTE: this can be combined with
another vulnerability to overwrite arbitrary files.

Luigi Auriemma q3cfilevar

from Thilo Schulz in ioquake3
svn 811 git 7d51d75b05a9593508040162709043516c0f2a17

- Fix arbitrary cvar overwrite flaw: http://aluigi.altervista.org/adv.htm
2013-05-07 22:18:55 +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 );
}
*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)