/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code 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 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #ifndef __SERIALIZER_H__ #define __SERIALIZER_H__ #define SERIALIZE_BOOL( ser, x ) ( ( x ) = ser.SerializeBoolNonRef( x ) ) #define SERIALIZE_ENUM( ser, x, type, max ) ( ( x ) = (type)ser.SerializeUMaxNonRef( x, max ) ) #define SERIALIZE_CVAR_FLOAT( ser, cvar ) { float a = cvar.GetFloat(); ser.Serialize( a ); cvar.SetFloat( a ); } #define SERIALIZE_CVAR_INT( ser, cvar ) { int a = cvar.GetInteger(); ser.Serialize( a ); cvar.SetInteger( a ); } #define SERIALIZE_CVAR_BOOL( ser, cvar ) { bool a = cvar.GetBool(); SERIALIZE_BOOL( ser, a ); cvar.SetBool( a ); } #define SERIALIZE_MATX( ser, var ) \ { \ int rows = var.GetNumRows(); \ int cols = var.GetNumColumns(); \ ser.Serialize( rows ); \ ser.Serialize( cols ); \ if ( ser.IsReading() ) { \ var.SetSize( rows, cols ); \ } \ for ( int y = 0; y < rows; y++ ) { \ for ( int x = 0; x < rows; x++ ) { \ ser.Serialize( var[x][y] ); \ } \ } \ } \ #define SERIALIZE_VECX( ser, var ) \ { \ int size = var.GetSize(); \ ser.Serialize( size ); \ if ( ser.IsReading() ) { \ var.SetSize( size ); \ } \ for ( int x = 0; x < size; x++ ) { \ ser.Serialize( var[x] ); \ } \ } \ #define SERIALIZE_JOINT( ser, var ) \ { \ uint16 jointIndex = ( var == NULL_JOINT_INDEX ) ? 65535 : var; \ ser.Serialize( jointIndex ); \ var = ( jointIndex == 65535 ) ? NULL_JOINT_INDEX : (jointIndex_t)jointIndex; \ } \ //#define ENABLE_SERIALIZE_CHECKPOINTS //#define SERIALIZE_SANITYCHECK //#define SERIALIZE_NO_QUANT #define SERIALIZE_CHECKPOINT( ser ) \ ser.SerializeCheckpoint( __FILE__, __LINE__ ); /* ======================== idSerializer ======================== */ class idSerializer { public: idSerializer( idBitMsg& msg_, bool writing_ ) : msg( &msg_ ), writing( writing_ ) #ifdef SERIALIZE_SANITYCHECK , magic( 0 ) #endif { } bool IsReading() { return !writing; } bool IsWriting() { return writing; } // SerializeRange - minSize through maxSize inclusive of all possible values void SerializeRange( int& value, int minSize, int maxSize ) // Supports signed types { SanityCheck(); if( writing ) { msg->WriteBits( value - minSize, idMath::BitsForInteger( maxSize - minSize ) ); } else { value = minSize + msg->ReadBits( idMath::BitsForInteger( maxSize - minSize ) ); } assert( value >= minSize && value <= maxSize ); } // SerializeUMax - maxSize inclusive, unsigned void SerializeUMax( int& value, int maxSize ) // Unsigned only { SanityCheck(); if( writing ) { msg->WriteBits( value, idMath::BitsForInteger( maxSize ) ); } else { value = msg->ReadBits( idMath::BitsForInteger( maxSize ) ); } assert( value <= maxSize ); } // SerializeUMaxNonRef - maxSize inclusive, unsigned, no reference int SerializeUMaxNonRef( int value, int maxSize ) // Unsigned only { SanityCheck(); if( writing ) { msg->WriteBits( value, idMath::BitsForInteger( maxSize ) ); } else { value = msg->ReadBits( idMath::BitsForInteger( maxSize ) ); } assert( value <= maxSize ); return value; } //void SerializeBitMsg( idBitMsg & inOutMsg, int numBytes ) { SanityCheck(); if ( writing ) { msg->WriteBitMsg( inOutMsg, numBytes ); } else { msg->ReadBitMsg( inOutMsg, numBytes ); } } // this is still needed to compile Rage code void SerializeBytes( void* bytes, int numBytes ) { SanityCheck(); for( int i = 0 ; i < numBytes ; i++ ) { Serialize( ( ( uint8* )bytes )[i] ); } }; bool SerializeBoolNonRef( bool value ) { SanityCheck(); // We return a value so we can support bit fields (can't pass by reference) if( writing ) { msg->WriteBool( value ); } else { value = msg->ReadBool(); } return value; } #ifdef SERIALIZE_NO_QUANT template< int _max_, int _numBits_ > void SerializeQ( idVec3& value ) { Serialize( value ); } template< int _max_, int _numBits_ > void SerializeQ( float& value ) { Serialize( value ); } template< int _max_, int _numBits_ > void SerializeUQ( float& value ) { Serialize( value ); } void SerializeQ( idMat3& axis, int bits = 15 ) { Serialize( axis ); } #else // SerializeQ - Quantizes a float to a variable number of bits (assumes signed, uses simple quantization) template< int _max_, int _numBits_ > void SerializeQ( idVec3& value ) { SanityCheck(); if( writing ) { msg->WriteQuantizedVector< idVec3, _max_, _numBits_ >( value ); } else { msg->ReadQuantizedVector< idVec3, _max_, _numBits_ >( value ); } } template< int _max_, int _numBits_ > void SerializeQ( float& value ) { SanityCheck(); if( writing ) { msg->WriteQuantizedFloat< _max_, _numBits_ >( value ); } else { value = msg->ReadQuantizedFloat< _max_, _numBits_ >(); } } template< int _max_, int _numBits_ > void SerializeUQ( float& value ) { SanityCheck(); if( writing ) { msg->WriteQuantizedUFloat< _max_, _numBits_ >( value ); } else { value = msg->ReadQuantizedUFloat< _max_, _numBits_ >(); } } void SerializeQ( idMat3& axis, int bits = 15 ); // Default to 15 bits per component, which has almost unnoticeable quantization #endif void Serialize( idMat3& axis ); // Raw 3x3 matrix serialize void SerializeC( idMat3& axis ); // Uses compressed quaternion template< typename _type_ > void SerializeListElement( const idList<_type_* >& list, const _type_*& element ); void SerializePacked( int& original ); void SerializeSPacked( int& original ); void SerializeString( char* s, int bufferSize ) { SanityCheck(); if( writing ) { msg->WriteString( s ); } else { msg->ReadString( s, bufferSize ); } } //void SerializeString( idAtomicString & s ) { SanityCheck(); if ( writing ) { msg->WriteString(s); } else { idStr temp; msg->ReadString( temp ); s.Set( temp ); } } void SerializeString( idStr& s ) { SanityCheck(); if( writing ) { msg->WriteString( s ); } else { msg->ReadString( s ); } } //void SerializeString( idStrId & s ) { SanityCheck(); if ( writing ) { msg->WriteString(s.GetKey()); } else { idStr key; msg->ReadString( key ); s.Set( key );} } void SerializeDelta( int32& value, const int32& base ) { SanityCheck(); if( writing ) { msg->WriteDeltaLong( base, value ); } else { value = msg->ReadDeltaLong( base ); } } void SerializeDelta( int16& value, const int16& base ) { SanityCheck(); if( writing ) { msg->WriteDeltaShort( base, value ); } else { value = msg->ReadDeltaShort( base ); } } void SerializeDelta( int8& value, const int8& base ) { SanityCheck(); if( writing ) { msg->WriteDeltaChar( base, value ); } else { value = msg->ReadDeltaChar( base ); } } void SerializeDelta( uint16& value, const uint16& base ) { SanityCheck(); if( writing ) { msg->WriteDeltaUShort( base, value ); } else { value = msg->ReadDeltaUShort( base ); } } void SerializeDelta( uint8& value, const uint8& base ) { SanityCheck(); if( writing ) { msg->WriteDeltaByte( base, value ); } else { value = msg->ReadDeltaByte( base ); } } void SerializeDelta( float& value, const float& base ) { SanityCheck(); if( writing ) { msg->WriteDeltaFloat( base, value ); } else { value = msg->ReadDeltaFloat( base ); } } // Common types, no compression void Serialize( int64& value ) { SanityCheck(); if( writing ) { msg->WriteLongLong( value ); } else { value = msg->ReadLongLong(); } } void Serialize( uint64& value ) { SanityCheck(); if( writing ) { msg->WriteLongLong( value ); } else { value = msg->ReadLongLong(); } } void Serialize( int32& value ) { SanityCheck(); if( writing ) { msg->WriteLong( value ); } else { value = msg->ReadLong(); } } void Serialize( uint32& value ) { SanityCheck(); if( writing ) { msg->WriteLong( value ); } else { value = msg->ReadLong(); } } void Serialize( int16& value ) { SanityCheck(); if( writing ) { msg->WriteShort( value ); } else { value = msg->ReadShort(); } } void Serialize( uint16& value ) { SanityCheck(); if( writing ) { msg->WriteUShort( value ); } else { value = msg->ReadUShort(); } } void Serialize( uint8& value ) { SanityCheck(); if( writing ) { msg->WriteByte( value ); } else { value = msg->ReadByte(); } } void Serialize( int8& value ) { SanityCheck(); if( writing ) { msg->WriteChar( value ); } else { value = msg->ReadChar(); } } void Serialize( bool& value ) { SanityCheck(); if( writing ) { msg->WriteByte( value ? 1 : 0 ); } else { value = msg->ReadByte() != 0; } } void Serialize( float& value ) { SanityCheck(); if( writing ) { msg->WriteFloat( value ); } else { value = msg->ReadFloat(); } } void Serialize( idRandom2& value ) { SanityCheck(); if( writing ) { msg->WriteLong( value.GetSeed() ); } else { value.SetSeed( msg->ReadLong() ); } } void Serialize( idVec3& value ) { SanityCheck(); if( writing ) { msg->WriteVectorFloat( value ); } else { msg->ReadVectorFloat( value ); } } void Serialize( idVec2& value ) { SanityCheck(); if( writing ) { msg->WriteVectorFloat( value ); } else { msg->ReadVectorFloat( value ); } } void Serialize( idVec6& value ) { SanityCheck(); if( writing ) { msg->WriteVectorFloat( value ); } else { msg->ReadVectorFloat( value ); } } void Serialize( idVec4& value ) { SanityCheck(); if( writing ) { msg->WriteVectorFloat( value ); } else { msg->ReadVectorFloat( value ); } } // serialize an angle, normalized to between 0 to 360 and quantized to 16 bits void SerializeAngle( float& value ) { SanityCheck(); if( writing ) { float nAngle = idMath::AngleNormalize360( value ); assert( nAngle >= 0.0f ); // should never get a negative angle uint16 sAngle = nAngle * ( 65536.0f / 360.0f ); msg->WriteUShort( sAngle ); } else { uint16 sAngle = msg->ReadUShort(); value = sAngle * ( 360.0f / 65536.0f ); } } //void Serialize( degrees_t & value ) { // SanityCheck(); // float angle = value.Get(); // Serialize( angle ); // value.Set( angle ); // } //void SerializeAngle( degrees_t & value ) { // SanityCheck(); // float angle = value.Get(); // SerializeAngle( angle ); // value.Set( angle ); // } //void Serialize( radians_t & value ) { // SanityCheck(); // // convert to degrees // degrees_t d( value.Get() * idMath::M_RAD2DEG ); // Serialize( d ); // if ( !writing ) { // // if reading, get the value we read in degrees and convert back to radians // value.Set( d.Get() * idMath::M_DEG2RAD ); // } // } //void SerializeAngle( radians_t & value ) { // SanityCheck(); // // convert to degrees // degrees_t d( value.Get() * idMath::M_RAD2DEG ); // // serialize as normalized degrees between 0 - 360 // SerializeAngle( d ); // if ( !writing ) { // // if reading, get the value we read in degrees and convert back to radians // value.Set( d.Get() * idMath::M_DEG2RAD ); // } // } // //void Serialize( idColor & value ) { // Serialize( value.r ); // Serialize( value.g ); // Serialize( value.b ); // Serialize( value.a ); //} void SanityCheck() { #ifdef SERIALIZE_SANITYCHECK if( writing ) { msg->WriteUShort( 0xCCCC ); msg->WriteUShort( magic ); } else { int cccc = msg->ReadUShort(); int m = msg->ReadUShort(); assert( cccc == 0xCCCC ); assert( m == magic ); // For release builds if( cccc != 0xCCCC ) { idLib::Error( "idSerializer::SanityCheck - cccc != 0xCCCC" ); } if( m != magic ) { idLib::Error( "idSerializer::SanityCheck - m != magic" ); } } magic++; #endif } void SerializeCheckpoint( const char* file, int line ) { #ifdef ENABLE_SERIALIZE_CHECKPOINTS const uint32 tagValue = 0xABADF00D; uint32 tag = tagValue; Serialize( tag ); if( tag != tagValue ) { idLib::Error( "SERIALIZE_CHECKPOINT: tag != tagValue (file: %s - line: %i)", file, line ); } #endif } idBitMsg& GetMsg() { return *msg; } private: bool writing; idBitMsg* msg; #ifdef SERIALIZE_SANITYCHECK int magic; #endif }; class idSerializerScopedBlock { public: idSerializerScopedBlock( idSerializer& ser_, int maxSizeBytes_ ) { ser = &ser_; maxSizeBytes = maxSizeBytes_; startByte = ser->IsReading() ? ser->GetMsg().GetReadCount() : ser->GetMsg().GetSize(); startWriteBits = ser->GetMsg().GetWriteBit(); } ~idSerializerScopedBlock() { // Serialize remaining bits while( ser->GetMsg().GetWriteBit() != startWriteBits ) { ser->SerializeBoolNonRef( false ); } // Verify we didn't go over int endByte = ser->IsReading() ? ser->GetMsg().GetReadCount() : ser->GetMsg().GetSize(); int sizeBytes = endByte - startByte; if( !verify( sizeBytes <= maxSizeBytes ) ) { idLib::Warning( "idSerializerScopedBlock went over maxSize (%d > %d)", sizeBytes, maxSizeBytes ); return; } // Serialize remaining bytes uint8 b = 0; while( sizeBytes < maxSizeBytes ) { ser->Serialize( b ); sizeBytes++; } int finalSize = ( ( ser->IsReading() ? ser->GetMsg().GetReadCount() : ser->GetMsg().GetSize() ) - startByte ); verify( maxSizeBytes == finalSize ); } private: idSerializer* ser; int maxSizeBytes; int startByte; int startWriteBits; }; /* ======================== idSerializer::SerializeQ ======================== */ #ifndef SERIALIZE_NO_QUANT ID_INLINE void idSerializer::SerializeQ( idMat3& axis, int bits ) { SanityCheck(); const float scale = ( ( 1 << ( bits - 1 ) ) - 1 ); if( IsWriting() ) { idQuat quat = axis.ToQuat(); int maxIndex = 0; for( unsigned int i = 1; i < 4; i++ ) { if( idMath::Fabs( quat[i] ) > idMath::Fabs( quat[maxIndex] ) ) { maxIndex = i; } } msg->WriteBits( maxIndex, 2 ); idVec3 out; if( quat[maxIndex] < 0.0f ) { out.x = -quat[( maxIndex + 1 ) & 3]; out.y = -quat[( maxIndex + 2 ) & 3]; out.z = -quat[( maxIndex + 3 ) & 3]; } else { out.x = quat[( maxIndex + 1 ) & 3]; out.y = quat[( maxIndex + 2 ) & 3]; out.z = quat[( maxIndex + 3 ) & 3]; } msg->WriteBits( idMath::Ftoi( out.x * scale ), -bits ); msg->WriteBits( idMath::Ftoi( out.y * scale ), -bits ); msg->WriteBits( idMath::Ftoi( out.z * scale ), -bits ); } else if( IsReading() ) { idQuat quat; idVec3 in; int maxIndex = msg->ReadBits( 2 ); in.x = ( float )msg->ReadBits( -bits ) / scale; in.y = ( float )msg->ReadBits( -bits ) / scale; in.z = ( float )msg->ReadBits( -bits ) / scale; quat[( maxIndex + 1 ) & 3] = in.x; quat[( maxIndex + 2 ) & 3] = in.y; quat[( maxIndex + 3 ) & 3] = in.z; quat[maxIndex] = idMath::Sqrt( idMath::Fabs( 1.0f - in.x * in.x - in.y * in.y - in.z * in.z ) ); axis = quat.ToMat3(); } } #endif /* ======================== idSerializer::Serialize ======================== */ ID_INLINE void idSerializer::Serialize( idMat3& axis ) { SanityCheck(); Serialize( axis[0] ); Serialize( axis[1] ); Serialize( axis[2] ); } /* ======================== idSerializer::SerializeC ======================== */ ID_INLINE void idSerializer::SerializeC( idMat3& axis ) { SanityCheck(); if( IsWriting() ) { idCQuat cquat = axis.ToCQuat(); Serialize( cquat.x ); Serialize( cquat.y ); Serialize( cquat.z ); } else if( IsReading() ) { idCQuat cquat; Serialize( cquat.x ); Serialize( cquat.y ); Serialize( cquat.z ); axis = cquat.ToMat3(); } } /* ======================== idSerializer::SerializeListElement ======================== */ template< typename _type_ > ID_INLINE void idSerializer::SerializeListElement( const idList<_type_* >& list, const _type_*& element ) { SanityCheck(); if( IsWriting() ) { int index = list.FindIndex( const_cast<_type_*>( element ) ); assert( index >= 0 ); SerializePacked( index ); } else if( IsReading() ) { int index = 0; SerializePacked( index ); element = list[index]; } } /* ======================== idSerializer::SerializePacked Writes out 7 bits at a time, using every 8th bit to signify more bits exist NOTE - Signed values work with this function, but take up more bytes Use SerializeSPacked if you anticipate lots of negative values ======================== */ ID_INLINE void idSerializer::SerializePacked( int& original ) { SanityCheck(); if( IsWriting() ) { uint32 value = original; while( true ) { uint8 byte = value & 0x7F; value >>= 7; byte |= value ? 0x80 : 0; msg->WriteByte( byte ); // Emit byte if( value == 0 ) { break; } } } else { uint8 byte = 0x80; uint32 value = 0; int32 shift = 0; while( byte & 0x80 && shift < 32 ) { byte = msg->ReadByte(); value |= ( byte & 0x7F ) << shift; shift += 7; } original = value; } } /* ======================== idSerializer::SerializeSPacked Writes out 7 bits at a time, using every 8th bit to signify more bits exist NOTE - An extra bit of the first byte is used to store the sign (this function supports negative values, but will use 2 bytes for values greater than 63) ======================== */ ID_INLINE void idSerializer::SerializeSPacked( int& value ) { SanityCheck(); if( IsWriting() ) { uint32 uvalue = idMath::Abs( value ); // Write the first byte specifically to handle the sign bit uint8 byte = uvalue & 0x3f; byte |= value < 0 ? 0x40 : 0; uvalue >>= 6; byte |= uvalue > 0 ? 0x80 : 0; msg->WriteByte( byte ); while( uvalue > 0 ) { uint8 byte2 = uvalue & 0x7F; uvalue >>= 7; byte2 |= uvalue ? 0x80 : 0; msg->WriteByte( byte2 ); // Emit byte } } else { // Load the first byte specifically to handle the sign bit uint8 byte = msg->ReadByte(); uint32 uvalue = byte & 0x3f; bool sgn = ( byte & 0x40 ) ? true : false; int32 shift = 6; while( byte & 0x80 && shift < 32 ) { byte = msg->ReadByte(); // Read byte uvalue |= ( byte & 0x7F ) << shift; shift += 7; } value = sgn ? -( ( int )uvalue ) : uvalue; } } #endif