/* 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. */ // msg.c -- Message IO functions #include "qcommon.h" /* ============================================================================== MESSAGE IO FUNCTIONS Handles byte ordering and avoids alignment errors ============================================================================== */ void MSG_Init( msg_t *buf, qbyte *data, int length ) { memset( buf, 0, sizeof(*buf) ); buf->data = data; buf->maxsize = length; buf->compressed = qfalse; } void MSG_Clear( msg_t *buf ) { buf->cursize = 0; buf->overflowed = qfalse; buf->compressed = qfalse; } void *MSG_GetSpace( msg_t *buf, int length ) { void *data; if( buf->cursize + length > buf->maxsize ) { if( !buf->allowoverflow ) Com_Error( ERR_FATAL, "MSG_GetSpace: overflow without allowoverflow set" ); if( length > buf->maxsize ) Com_Error( ERR_FATAL, "MSG_GetSpace: %i is > full buffer size", length ); Com_Printf( "MSG_GetSpace: overflow\n" ); MSG_Clear( buf ); buf->overflowed = qtrue; } data = buf->data + buf->cursize; buf->cursize += length; return data; } //=========================================================================== void MSG_Print( msg_t *buf, char *data ) { size_t len; len = strlen(data)+1; if( buf->cursize ) { if( buf->data[buf->cursize-1] ) memcpy( (qbyte *)MSG_GetSpace(buf, (int)len), data, len ); // no trailing 0 else { qbyte *dest = (qbyte *)MSG_GetSpace( buf, (int)len-1 ); if( !buf->overflowed ) memcpy( dest-1, data, len ); // write over trailing 0 } } else memcpy( (qbyte *)MSG_GetSpace(buf, (int)len), data, len ); } // // writing functions // void MSG_Write( msg_t *buf, void *data, int length ) { memcpy( MSG_GetSpace(buf, length), data, length ); } void MSG_WriteChar( msg_t *sb, int c ) { qbyte *buf; #ifdef PARANOID if( c < -128 || c > 127 ) Com_Error( ERR_FATAL, "MSG_WriteChar: range error" ); #endif buf = MSG_GetSpace( sb, 1 ); buf[0] = c; } void MSG_WriteByte( msg_t *sb, int c ) { qbyte *buf; #ifdef PARANOID if( c < 0 || c > 255 ) Com_Error( ERR_FATAL, "MSG_WriteByte: range error" ); #endif buf = MSG_GetSpace( sb, 1 ); buf[0] = c; } void MSG_WriteShort( msg_t *sb, int c ) { qbyte *buf; #ifdef PARANOID if( c < ((short)0x8000) || c > (short)0x7fff ) Com_Error( ERR_FATAL, "MSG_WriteShort: range error" ); #endif buf = MSG_GetSpace( sb, 2 ); buf[0] = c&0xff; buf[1] = (c>>8)&0xff; } void MSG_WriteLong( msg_t *sb, int c ) { qbyte *buf; buf = MSG_GetSpace( sb, 4 ); buf[0] = c&0xff; buf[1] = (c>>8)&0xff; buf[2] = (c>>16)&0xff; buf[3] = c>>24; } void MSG_WriteFloat( msg_t *sb, float f ) { union { float f; int l; } dat; dat.f = f; dat.l = LittleLong( dat.l ); MSG_Write( sb, &dat.l, 4 ); } void MSG_WriteString( msg_t *sb, char *s ) { if( !s ) MSG_Write( sb, "", 1 ); else MSG_Write( sb, s, (int)strlen(s)+1 ); } void MSG_WriteInt3( msg_t *sb, int c ) { qbyte *buf; buf = MSG_GetSpace( sb, 3 ); buf[0] = c&0xff; buf[1] = (c>>8)&0xff; buf[2] = (c>>16)&0xff; } void MSG_WriteDeltaUsercmd( msg_t *buf, usercmd_t *from, usercmd_t *cmd ) { int bits; // send the movement message bits = 0; if( cmd->angles[0] != from->angles[0] ) bits |= CM_ANGLE1; if( cmd->angles[1] != from->angles[1] ) bits |= CM_ANGLE2; if( cmd->angles[2] != from->angles[2] ) bits |= CM_ANGLE3; if( cmd->forwardmove != from->forwardmove ) bits |= CM_FORWARD; if( cmd->sidemove != from->sidemove ) bits |= CM_SIDE; if( cmd->upmove != from->upmove ) bits |= CM_UP; if( cmd->buttons != from->buttons ) bits |= CM_BUTTONS; MSG_WriteByte( buf, bits ); if( bits & CM_ANGLE1 ) MSG_WriteShort( buf, cmd->angles[0] ); if( bits & CM_ANGLE2 ) MSG_WriteShort( buf, cmd->angles[1] ); if( bits & CM_ANGLE3 ) MSG_WriteShort( buf, cmd->angles[2] ); if( bits & CM_FORWARD ) MSG_WriteShort( buf, cmd->forwardmove ); if( bits & CM_SIDE ) MSG_WriteShort( buf, cmd->sidemove ); if( bits & CM_UP ) MSG_WriteShort( buf, cmd->upmove ); if( bits & CM_BUTTONS ) MSG_WriteByte( buf, cmd->buttons ); MSG_WriteByte( buf, cmd->msec ); MSG_WriteLong( buf, cmd->serverTimeStamp ); } void MSG_WriteDir( msg_t *sb, vec3_t dir ) { if( !dir ) { MSG_WriteByte( sb, 0 ); return; } MSG_WriteByte( sb, DirToByte(dir) ); } //================== //MSG_WriteDeltaEntity // //Writes part of a packetentities message. //Can delta from either a baseline or a previous packet_entity //================== void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, msg_t *msg, qboolean force, qboolean newentity ) { int bits; if( !to->number ) Com_Error( ERR_FATAL, "Unset entity number" ); if( to->number >= MAX_EDICTS ) Com_Error( ERR_FATAL, "Entity number >= MAX_EDICTS" ); // send an update bits = 0; if( to->number >= 256 ) bits |= U_NUMBER16; // number8 is implicit otherwise if( to->origin[0] != from->origin[0] ) bits |= U_ORIGIN1; if( to->origin[1] != from->origin[1] ) bits |= U_ORIGIN2; if( to->origin[2] != from->origin[2] ) bits |= U_ORIGIN3; if( to->angles[0] != from->angles[0] ) bits |= U_ANGLE1; if( to->angles[1] != from->angles[1] ) bits |= U_ANGLE2; if( to->angles[2] != from->angles[2] ) bits |= U_ANGLE3; if( to->skinnum != from->skinnum ) { if( (unsigned)to->skinnum < 256 ) bits |= U_SKIN8; else if( (unsigned)to->skinnum < 0x10000 ) bits |= U_SKIN16; else bits |= (U_SKIN8|U_SKIN16); } if( to->frame != from->frame ) { if( to->frame < 256 ) bits |= U_FRAME8; else bits |= U_FRAME16; } if( to->effects != from->effects ) { if( to->effects < 256 ) bits |= U_EFFECTS8; else if( to->effects < 0x8000 ) bits |= U_EFFECTS16; else bits |= U_EFFECTS8|U_EFFECTS16; } if( to->renderfx != from->renderfx ) { if( to->renderfx < 256 ) bits |= U_RENDERFX8; else if( to->renderfx < 0x8000 ) bits |= U_RENDERFX16; else bits |= U_RENDERFX8|U_RENDERFX16; } if( to->solid != from->solid ) bits |= U_SOLID; // events are not delta compressed, just 0 compressed if( to->events[0] ) bits |= U_EVENT; if( to->events[1] ) bits |= U_EVENT2; if( to->modelindex != from->modelindex ) bits |= U_MODEL; if( to->modelindex2 != from->modelindex2 ) bits |= U_MODEL2; if( (to->type != from->type) || (to->takedamage != from->takedamage) ) bits |= U_TYPE; if( to->sound != from->sound ) bits |= U_SOUND; if( newentity ) bits |= U_OLDORIGIN; if( to->weapon != from->weapon ) bits |= U_WEAPON; if( to->light != from->light ) bits |= U_LIGHT; if( to->team != from->team ) bits |= U_TEAM; // // write the message // if( !bits && !force ) return; // nothing to send! //---------- if( bits & 0xff000000 ) bits |= U_MOREBITS3 | U_MOREBITS2 | U_MOREBITS1; else if( bits & 0x00ff0000 ) bits |= U_MOREBITS2 | U_MOREBITS1; else if( bits & 0x0000ff00 ) bits |= U_MOREBITS1; MSG_WriteByte( msg, bits&255 ); if( bits & 0xff000000 ) { MSG_WriteByte( msg, (bits>>8 )&255 ); MSG_WriteByte( msg, (bits>>16)&255 ); MSG_WriteByte( msg, (bits>>24)&255 ); } else if( bits & 0x00ff0000 ) { MSG_WriteByte( msg, (bits>>8 )&255 ); MSG_WriteByte( msg, (bits>>16)&255 ); } else if( bits & 0x0000ff00 ) { MSG_WriteByte( msg, (bits>>8 )&255 ); } //---------- if( bits & U_NUMBER16 ) MSG_WriteShort( msg, to->number ); else MSG_WriteByte( msg, to->number ); if( bits & U_SOLID ) MSG_WriteShort( msg, to->solid ); if( bits & U_MODEL ) MSG_WriteByte( msg, to->modelindex ); if( bits & U_MODEL2 ) MSG_WriteByte( msg, to->modelindex2 ); if( bits & U_FRAME8 ) MSG_WriteByte( msg, to->frame ); else if( bits & U_FRAME16 ) MSG_WriteShort( msg, to->frame ); if( (bits & U_SKIN8) && (bits & U_SKIN16) ) //used for laser colors MSG_WriteLong( msg, to->skinnum ); else if( bits & U_SKIN8 ) MSG_WriteByte( msg, to->skinnum ); else if( bits & U_SKIN16 ) MSG_WriteShort( msg, to->skinnum ); if( (bits & (U_EFFECTS8|U_EFFECTS16)) == (U_EFFECTS8|U_EFFECTS16) ) MSG_WriteLong( msg, to->effects ); else if( bits & U_EFFECTS8 ) MSG_WriteByte( msg, to->effects ); else if( bits & U_EFFECTS16 ) MSG_WriteShort( msg, to->effects ); if( (bits & (U_RENDERFX8|U_RENDERFX16)) == (U_RENDERFX8|U_RENDERFX16) ) MSG_WriteLong( msg, to->renderfx ); else if( bits & U_RENDERFX8 ) MSG_WriteByte( msg, to->renderfx ); else if( bits & U_RENDERFX16 ) MSG_WriteShort( msg, to->renderfx ); if( bits & U_ORIGIN1 ) MSG_WriteCoord( msg, to->origin[0] ); if( bits & U_ORIGIN2 ) MSG_WriteCoord( msg, to->origin[1] ); if( bits & U_ORIGIN3 ) MSG_WriteCoord( msg, to->origin[2] ); if( bits & U_ANGLE1 && (to->solid == SOLID_BMODEL) ) MSG_WriteAngle16( msg, to->angles[0] ); else if( bits & U_ANGLE1 ) MSG_WriteAngle( msg, to->angles[0] ); if( bits & U_ANGLE2 && (to->solid == SOLID_BMODEL) ) MSG_WriteAngle16( msg, to->angles[1] ); else if( bits & U_ANGLE2 ) MSG_WriteAngle( msg, to->angles[1] ); if( bits & U_ANGLE3 && (to->solid == SOLID_BMODEL) ) MSG_WriteAngle16( msg, to->angles[2] ); else if( bits & U_ANGLE3 ) MSG_WriteAngle( msg, to->angles[2] ); if( bits & U_OLDORIGIN ) MSG_WritePos( msg, to->old_origin ); if( bits & U_TYPE ) { MSG_WriteByte( msg, to->type|(ET_INVERSE * to->takedamage) ); } if( bits & U_SOUND ) MSG_WriteByte( msg, to->sound ); if( bits & U_EVENT ) { if ( !to->eventParms[0] ) { MSG_WriteByte( msg, to->events[0] ); } else { MSG_WriteByte( msg, to->events[0] | EV_INVERSE ); MSG_WriteByte( msg, to->eventParms[0] ); } } if( bits & U_EVENT2 ) { if( !to->eventParms[1] ) { MSG_WriteByte( msg, to->events[1] ); } else { MSG_WriteByte( msg, to->events[1] | EV_INVERSE ); MSG_WriteByte( msg, to->eventParms[1] ); } } if( bits & U_WEAPON ) MSG_WriteByte( msg, to->weapon ); if( bits & U_LIGHT ) MSG_WriteLong( msg, to->light ); if( bits & U_TEAM ) MSG_WriteByte( msg, to->team ); } //============================================================ // // reading functions // void MSG_BeginReading( msg_t *msg ) { msg->readcount = 0; } int MSG_ReadShort( msg_t *msg_read ) { int c; if( msg_read->readcount+2 > msg_read->cursize ) c = -1; else c = (short)( msg_read->data[msg_read->readcount] + (msg_read->data[msg_read->readcount+1]<<8) ); msg_read->readcount += 2; return c; } int MSG_ReadInt3( msg_t *msg_read ) { int c; if( msg_read->readcount+3 > msg_read->cursize ) c = -1; else c = msg_read->data[msg_read->readcount] | (msg_read->data[msg_read->readcount+1]<<8) | (msg_read->data[msg_read->readcount+2]<<16) | ((msg_read->data[msg_read->readcount+2] & 0x80) ? ~0xFFFFFF : 0); msg_read->readcount += 3; return c; } int MSG_ReadLong( msg_t *msg_read ) { int c; if (msg_read->readcount+4 > msg_read->cursize) c = -1; else c = msg_read->data[msg_read->readcount] + (msg_read->data[msg_read->readcount+1]<<8 ) + (msg_read->data[msg_read->readcount+2]<<16) + (msg_read->data[msg_read->readcount+3]<<24); msg_read->readcount += 4; return c; } float MSG_ReadFloat( msg_t *msg_read ) { union { qbyte b[4]; float f; int l; } dat; if( msg_read->readcount+4 > msg_read->cursize ) dat.f = -1; else { dat.b[0] = msg_read->data[msg_read->readcount]; dat.b[1] = msg_read->data[msg_read->readcount+1]; dat.b[2] = msg_read->data[msg_read->readcount+2]; dat.b[3] = msg_read->data[msg_read->readcount+3]; } msg_read->readcount += 4; dat.l = LittleLong( dat.l ); return dat.f; } char *MSG_ReadString( msg_t *msg_read ) { int l, c; static char string[2048]; l = 0; do { c = MSG_ReadByte( msg_read ); if( c == 255 ) continue; if( c == -1 || c == 0 ) break; string[l] = c; l++; } while( l < sizeof(string)-1 ); string[l] = 0; return string; } char *MSG_ReadStringLine( msg_t *msg_read ) { int l, c; static char string[2048]; l = 0; do { c = MSG_ReadByte( msg_read ); if( c == 255 ) continue; if( c == -1 || c == 0 || c == '\n' ) break; string[l] = c; l++; } while( l < sizeof(string)-1 ); string[l] = 0; return string; } void MSG_ReadDir( msg_t *sb, vec3_t dir ) { ByteToDir( MSG_ReadByte (sb), dir ); } void MSG_ReadDeltaUsercmd( msg_t *msg_read, usercmd_t *from, usercmd_t *move ) { int bits; memcpy( move, from, sizeof(*move) ); bits = MSG_ReadByte (msg_read); // read current angles if( bits & CM_ANGLE1 ) move->angles[0] = MSG_ReadShort( msg_read ); if( bits & CM_ANGLE2 ) move->angles[1] = MSG_ReadShort( msg_read ); if( bits & CM_ANGLE3 ) move->angles[2] = MSG_ReadShort( msg_read ); // read movement if( bits & CM_FORWARD ) move->forwardmove = MSG_ReadShort( msg_read ); if( bits & CM_SIDE ) move->sidemove = MSG_ReadShort( msg_read ); if( bits & CM_UP ) move->upmove = MSG_ReadShort( msg_read ); // read buttons if( bits & CM_BUTTONS ) move->buttons = MSG_ReadByte( msg_read ); // read time to run command move->msec = MSG_ReadByte( msg_read ); move->serverTimeStamp = MSG_ReadLong( msg_read ); } void MSG_ReadData( msg_t *msg_read, void *data, int len ) { int i; for( i = 0; i < len; i++ ) ((qbyte *)data)[i] = MSG_ReadByte( msg_read ); } int MSG_SkipData( msg_t *msg_read, int len ) { if ( msg_read->readcount + len < msg_read->cursize ) { msg_read->readcount += len; return 1; } else { return 0; } }