123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- /*
- ===========================================================================
- 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 <http://www.gnu.org/licenses/>.
- 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(); if ( writing ) { msg->WriteBool(value); } else { value = msg->ReadBool(); } return value; } // We return a value so we can support bit fields (can't pass by reference)
-
- #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
|