Serializer.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. 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.
  17. 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.
  18. ===========================================================================
  19. */
  20. #ifndef __SERIALIZER_H__
  21. #define __SERIALIZER_H__
  22. #define SERIALIZE_BOOL( ser, x ) ( ( x ) = ser.SerializeBoolNonRef( x ) )
  23. #define SERIALIZE_ENUM( ser, x, type, max ) ( ( x ) = (type)ser.SerializeUMaxNonRef( x, max ) )
  24. #define SERIALIZE_CVAR_FLOAT( ser, cvar ) { float a = cvar.GetFloat(); ser.Serialize( a ); cvar.SetFloat( a ); }
  25. #define SERIALIZE_CVAR_INT( ser, cvar ) { int a = cvar.GetInteger(); ser.Serialize( a ); cvar.SetInteger( a ); }
  26. #define SERIALIZE_CVAR_BOOL( ser, cvar ) { bool a = cvar.GetBool(); SERIALIZE_BOOL( ser, a ); cvar.SetBool( a ); }
  27. #define SERIALIZE_MATX( ser, var ) \
  28. { \
  29. int rows = var.GetNumRows(); \
  30. int cols = var.GetNumColumns(); \
  31. ser.Serialize( rows ); \
  32. ser.Serialize( cols ); \
  33. if ( ser.IsReading() ) { \
  34. var.SetSize( rows, cols ); \
  35. } \
  36. for ( int y = 0; y < rows; y++ ) { \
  37. for ( int x = 0; x < rows; x++ ) { \
  38. ser.Serialize( var[x][y] ); \
  39. } \
  40. } \
  41. } \
  42. #define SERIALIZE_VECX( ser, var ) \
  43. { \
  44. int size = var.GetSize(); \
  45. ser.Serialize( size ); \
  46. if ( ser.IsReading() ) { \
  47. var.SetSize( size ); \
  48. } \
  49. for ( int x = 0; x < size; x++ ) { \
  50. ser.Serialize( var[x] ); \
  51. } \
  52. } \
  53. #define SERIALIZE_JOINT( ser, var ) \
  54. { \
  55. uint16 jointIndex = ( var == NULL_JOINT_INDEX ) ? 65535 : var; \
  56. ser.Serialize( jointIndex ); \
  57. var = ( jointIndex == 65535 ) ? NULL_JOINT_INDEX : (jointIndex_t)jointIndex; \
  58. } \
  59. //#define ENABLE_SERIALIZE_CHECKPOINTS
  60. //#define SERIALIZE_SANITYCHECK
  61. //#define SERIALIZE_NO_QUANT
  62. #define SERIALIZE_CHECKPOINT( ser ) \
  63. ser.SerializeCheckpoint( __FILE__, __LINE__ );
  64. /*
  65. ========================
  66. idSerializer
  67. ========================
  68. */
  69. class idSerializer {
  70. public:
  71. idSerializer( idBitMsg & msg_, bool writing_) : msg( &msg_ ), writing( writing_ )
  72. #ifdef SERIALIZE_SANITYCHECK
  73. ,magic( 0 )
  74. #endif
  75. { }
  76. bool IsReading() { return !writing; }
  77. bool IsWriting() { return writing; }
  78. // SerializeRange - minSize through maxSize inclusive of all possible values
  79. void SerializeRange( int & value, int minSize, int maxSize ) { // Supports signed types
  80. SanityCheck();
  81. if ( writing ) {
  82. msg->WriteBits( value - minSize, idMath::BitsForInteger( maxSize-minSize ) );
  83. } else {
  84. value = minSize + msg->ReadBits( idMath::BitsForInteger( maxSize-minSize ) );
  85. }
  86. assert( value >= minSize && value <= maxSize );
  87. }
  88. // SerializeUMax - maxSize inclusive, unsigned
  89. void SerializeUMax( int & value, int maxSize ) { // Unsigned only
  90. SanityCheck();
  91. if ( writing ) {
  92. msg->WriteBits( value, idMath::BitsForInteger( maxSize ) );
  93. } else {
  94. value = msg->ReadBits( idMath::BitsForInteger( maxSize ) );
  95. }
  96. assert( value <= maxSize );
  97. }
  98. // SerializeUMaxNonRef - maxSize inclusive, unsigned, no reference
  99. int SerializeUMaxNonRef( int value, int maxSize ) { // Unsigned only
  100. SanityCheck();
  101. if ( writing ) {
  102. msg->WriteBits(value, idMath::BitsForInteger( maxSize ) );
  103. } else {
  104. value = msg->ReadBits( idMath::BitsForInteger( maxSize ) );
  105. }
  106. assert( value <= maxSize );
  107. return value;
  108. }
  109. //void SerializeBitMsg( idBitMsg & inOutMsg, int numBytes ) { SanityCheck(); if ( writing ) { msg->WriteBitMsg( inOutMsg, numBytes ); } else { msg->ReadBitMsg( inOutMsg, numBytes ); } }
  110. // this is still needed to compile Rage code
  111. void SerializeBytes( void * bytes, int numBytes ) { SanityCheck(); for ( int i = 0 ; i < numBytes ; i++ ) { Serialize( ((uint8 *)bytes)[i] ); } };
  112. 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)
  113. #ifdef SERIALIZE_NO_QUANT
  114. template< int _max_, int _numBits_ >
  115. void SerializeQ( idVec3 & value ) { Serialize( value ); }
  116. template< int _max_, int _numBits_ >
  117. void SerializeQ( float & value ) { Serialize( value ); }
  118. template< int _max_, int _numBits_ >
  119. void SerializeUQ( float & value ) { Serialize( value ); }
  120. void SerializeQ( idMat3 & axis, int bits = 15 ) { Serialize( axis ); }
  121. #else
  122. // SerializeQ - Quantizes a float to a variable number of bits (assumes signed, uses simple quantization)
  123. template< int _max_, int _numBits_ >
  124. void SerializeQ( idVec3 & value ) { SanityCheck(); if ( writing ) { msg->WriteQuantizedVector< idVec3, _max_, _numBits_ >( value ); } else { msg->ReadQuantizedVector< idVec3, _max_, _numBits_ >( value ); } }
  125. template< int _max_, int _numBits_ >
  126. void SerializeQ( float & value ) { SanityCheck(); if ( writing ) { msg->WriteQuantizedFloat< _max_, _numBits_ >( value ); } else { value = msg->ReadQuantizedFloat< _max_, _numBits_ >(); } }
  127. template< int _max_, int _numBits_ >
  128. void SerializeUQ( float & value ) { SanityCheck(); if ( writing ) { msg->WriteQuantizedUFloat< _max_, _numBits_ >( value ); } else { value = msg->ReadQuantizedUFloat< _max_, _numBits_ >(); } }
  129. void SerializeQ( idMat3 & axis, int bits = 15 ); // Default to 15 bits per component, which has almost unnoticeable quantization
  130. #endif
  131. void Serialize( idMat3 & axis); // Raw 3x3 matrix serialize
  132. void SerializeC( idMat3 & axis); // Uses compressed quaternion
  133. template< typename _type_ >
  134. void SerializeListElement( const idList<_type_* > & list, const _type_ *&element );
  135. void SerializePacked(int & original);
  136. void SerializeSPacked(int & original);
  137. void SerializeString( char * s, int bufferSize ) { SanityCheck(); if ( writing ) { msg->WriteString(s); } else { msg->ReadString( s, bufferSize ); } }
  138. //void SerializeString( idAtomicString & s ) { SanityCheck(); if ( writing ) { msg->WriteString(s); } else { idStr temp; msg->ReadString( temp ); s.Set( temp ); } }
  139. void SerializeString( idStr & s ) { SanityCheck(); if ( writing ) { msg->WriteString(s); } else { msg->ReadString( s ); } }
  140. //void SerializeString( idStrId & s ) { SanityCheck(); if ( writing ) { msg->WriteString(s.GetKey()); } else { idStr key; msg->ReadString( key ); s.Set( key );} }
  141. void SerializeDelta( int32 & value, const int32 & base ) { SanityCheck(); if ( writing ) { msg->WriteDeltaLong( base, value ); } else { value = msg->ReadDeltaLong( base ); } }
  142. void SerializeDelta( int16 & value, const int16 & base ) { SanityCheck(); if ( writing ) { msg->WriteDeltaShort( base, value ); } else { value = msg->ReadDeltaShort( base ); } }
  143. void SerializeDelta( int8 & value, const int8 & base ) { SanityCheck(); if ( writing ) { msg->WriteDeltaChar( base, value ); } else { value = msg->ReadDeltaChar( base ); } }
  144. void SerializeDelta( uint16 & value, const uint16 & base ) { SanityCheck(); if ( writing ) { msg->WriteDeltaUShort( base, value ); } else { value = msg->ReadDeltaUShort( base ); } }
  145. void SerializeDelta( uint8 & value, const uint8 & base ) { SanityCheck(); if ( writing ) { msg->WriteDeltaByte( base, value ); } else { value = msg->ReadDeltaByte( base ); } }
  146. void SerializeDelta( float & value, const float & base ) { SanityCheck(); if ( writing ) { msg->WriteDeltaFloat( base, value ); } else { value = msg->ReadDeltaFloat( base ); } }
  147. // Common types, no compression
  148. void Serialize( int64 & value ) { SanityCheck(); if ( writing ) { msg->WriteLongLong(value); } else { value = msg->ReadLongLong(); } }
  149. void Serialize( uint64 & value ) { SanityCheck(); if ( writing ) { msg->WriteLongLong(value); } else { value = msg->ReadLongLong(); } }
  150. void Serialize( int32 & value ) { SanityCheck(); if ( writing ) { msg->WriteLong(value); } else { value = msg->ReadLong(); } }
  151. void Serialize( uint32 & value ) { SanityCheck(); if ( writing ) { msg->WriteLong(value); } else { value = msg->ReadLong(); } }
  152. void Serialize( int16 & value ) { SanityCheck(); if ( writing ) { msg->WriteShort(value); } else { value = msg->ReadShort(); } }
  153. void Serialize( uint16 & value ) { SanityCheck(); if ( writing ) { msg->WriteUShort(value); } else { value = msg->ReadUShort(); } }
  154. void Serialize( uint8 & value ) { SanityCheck(); if ( writing ) { msg->WriteByte(value); } else { value = msg->ReadByte(); } }
  155. void Serialize( int8 & value ) { SanityCheck(); if ( writing ) { msg->WriteChar(value); } else { value = msg->ReadChar(); } }
  156. void Serialize( bool & value ) { SanityCheck(); if ( writing ) { msg->WriteByte(value?1:0); } else { value = msg->ReadByte() != 0; } }
  157. void Serialize( float & value ) { SanityCheck(); if ( writing ) { msg->WriteFloat(value); } else { value = msg->ReadFloat(); } }
  158. void Serialize( idRandom2 & value ) { SanityCheck(); if ( writing ) { msg->WriteLong(value.GetSeed()); } else { value.SetSeed( msg->ReadLong() ); } }
  159. void Serialize( idVec3 & value ) { SanityCheck(); if ( writing ) { msg->WriteVectorFloat(value); } else { msg->ReadVectorFloat(value); } }
  160. void Serialize( idVec2 & value ) { SanityCheck(); if ( writing ) { msg->WriteVectorFloat(value); } else { msg->ReadVectorFloat(value); } }
  161. void Serialize( idVec6 & value ) { SanityCheck(); if ( writing ) { msg->WriteVectorFloat(value); } else { msg->ReadVectorFloat(value); } }
  162. void Serialize( idVec4 & value ) { SanityCheck(); if ( writing ) { msg->WriteVectorFloat(value); } else { msg->ReadVectorFloat(value); } }
  163. // serialize an angle, normalized to between 0 to 360 and quantized to 16 bits
  164. void SerializeAngle( float & value ) {
  165. SanityCheck();
  166. if ( writing ) {
  167. float nAngle = idMath::AngleNormalize360( value );
  168. assert( nAngle >= 0.0f ); // should never get a negative angle
  169. uint16 sAngle = nAngle * ( 65536.0f / 360.0f );
  170. msg->WriteUShort( sAngle );
  171. } else {
  172. uint16 sAngle = msg->ReadUShort();
  173. value = sAngle * ( 360.0f / 65536.0f );
  174. }
  175. }
  176. //void Serialize( degrees_t & value ) {
  177. // SanityCheck();
  178. // float angle = value.Get();
  179. // Serialize( angle );
  180. // value.Set( angle );
  181. // }
  182. //void SerializeAngle( degrees_t & value ) {
  183. // SanityCheck();
  184. // float angle = value.Get();
  185. // SerializeAngle( angle );
  186. // value.Set( angle );
  187. // }
  188. //void Serialize( radians_t & value ) {
  189. // SanityCheck();
  190. // // convert to degrees
  191. // degrees_t d( value.Get() * idMath::M_RAD2DEG );
  192. // Serialize( d );
  193. // if ( !writing ) {
  194. // // if reading, get the value we read in degrees and convert back to radians
  195. // value.Set( d.Get() * idMath::M_DEG2RAD );
  196. // }
  197. // }
  198. //void SerializeAngle( radians_t & value ) {
  199. // SanityCheck();
  200. // // convert to degrees
  201. // degrees_t d( value.Get() * idMath::M_RAD2DEG );
  202. // // serialize as normalized degrees between 0 - 360
  203. // SerializeAngle( d );
  204. // if ( !writing ) {
  205. // // if reading, get the value we read in degrees and convert back to radians
  206. // value.Set( d.Get() * idMath::M_DEG2RAD );
  207. // }
  208. // }
  209. //
  210. //void Serialize( idColor & value ) {
  211. // Serialize( value.r );
  212. // Serialize( value.g );
  213. // Serialize( value.b );
  214. // Serialize( value.a );
  215. //}
  216. void SanityCheck() {
  217. #ifdef SERIALIZE_SANITYCHECK
  218. if ( writing ) {
  219. msg->WriteUShort( 0xCCCC );
  220. msg->WriteUShort( magic );
  221. } else {
  222. int cccc = msg->ReadUShort();
  223. int m = msg->ReadUShort();
  224. assert( cccc == 0xCCCC );
  225. assert( m == magic );
  226. // For release builds
  227. if ( cccc != 0xCCCC ) {
  228. idLib::Error( "idSerializer::SanityCheck - cccc != 0xCCCC" );
  229. }
  230. if ( m != magic ) {
  231. idLib::Error( "idSerializer::SanityCheck - m != magic" );
  232. }
  233. }
  234. magic++;
  235. #endif
  236. }
  237. void SerializeCheckpoint( const char * file, int line ) {
  238. #ifdef ENABLE_SERIALIZE_CHECKPOINTS
  239. const uint32 tagValue = 0xABADF00D;
  240. uint32 tag = tagValue;
  241. Serialize( tag );
  242. if ( tag != tagValue ) {
  243. idLib::Error( "SERIALIZE_CHECKPOINT: tag != tagValue (file: %s - line: %i)", file, line );
  244. }
  245. #endif
  246. }
  247. idBitMsg & GetMsg() { return *msg; }
  248. private:
  249. bool writing;
  250. idBitMsg * msg;
  251. #ifdef SERIALIZE_SANITYCHECK
  252. int magic;
  253. #endif
  254. };
  255. class idSerializerScopedBlock {
  256. public:
  257. idSerializerScopedBlock( idSerializer &ser_, int maxSizeBytes_ ) {
  258. ser = &ser_;
  259. maxSizeBytes = maxSizeBytes_;
  260. startByte = ser->IsReading() ? ser->GetMsg().GetReadCount() : ser->GetMsg().GetSize();
  261. startWriteBits = ser->GetMsg().GetWriteBit();
  262. }
  263. ~idSerializerScopedBlock() {
  264. // Serialize remaining bits
  265. while ( ser->GetMsg().GetWriteBit() != startWriteBits ) {
  266. ser->SerializeBoolNonRef( false );
  267. }
  268. // Verify we didn't go over
  269. int endByte = ser->IsReading() ? ser->GetMsg().GetReadCount() : ser->GetMsg().GetSize();
  270. int sizeBytes = endByte - startByte;
  271. if ( !verify( sizeBytes <= maxSizeBytes ) ) {
  272. idLib::Warning( "idSerializerScopedBlock went over maxSize (%d > %d)", sizeBytes, maxSizeBytes );
  273. return;
  274. }
  275. // Serialize remaining bytes
  276. uint8 b=0;
  277. while ( sizeBytes < maxSizeBytes ) {
  278. ser->Serialize( b );
  279. sizeBytes++;
  280. }
  281. int finalSize = ( ( ser->IsReading() ? ser->GetMsg().GetReadCount() : ser->GetMsg().GetSize() ) - startByte );
  282. verify( maxSizeBytes == finalSize );
  283. }
  284. private:
  285. idSerializer * ser;
  286. int maxSizeBytes;
  287. int startByte;
  288. int startWriteBits;
  289. };
  290. /*
  291. ========================
  292. idSerializer::SerializeQ
  293. ========================
  294. */
  295. #ifndef SERIALIZE_NO_QUANT
  296. ID_INLINE void idSerializer::SerializeQ( idMat3 &axis, int bits ) {
  297. SanityCheck();
  298. const float scale = ( ( 1 << ( bits - 1 ) ) - 1 );
  299. if ( IsWriting() ) {
  300. idQuat quat = axis.ToQuat();
  301. int maxIndex = 0;
  302. for ( unsigned int i = 1; i < 4; i++ ) {
  303. if ( idMath::Fabs( quat[i] ) > idMath::Fabs( quat[maxIndex] ) ) {
  304. maxIndex = i;
  305. }
  306. }
  307. msg->WriteBits( maxIndex, 2 );
  308. idVec3 out;
  309. if ( quat[maxIndex] < 0.0f ) {
  310. out.x = -quat[( maxIndex + 1 ) & 3];
  311. out.y = -quat[( maxIndex + 2 ) & 3];
  312. out.z = -quat[( maxIndex + 3 ) & 3];
  313. } else {
  314. out.x = quat[( maxIndex + 1 ) & 3];
  315. out.y = quat[( maxIndex + 2 ) & 3];
  316. out.z = quat[( maxIndex + 3 ) & 3];
  317. }
  318. msg->WriteBits( idMath::Ftoi( out.x * scale ), -bits);
  319. msg->WriteBits( idMath::Ftoi( out.y * scale ), -bits);
  320. msg->WriteBits( idMath::Ftoi( out.z * scale ), -bits);
  321. } else if ( IsReading() ) {
  322. idQuat quat;
  323. idVec3 in;
  324. int maxIndex = msg->ReadBits(2);
  325. in.x = (float)msg->ReadBits(-bits) / scale;
  326. in.y = (float)msg->ReadBits(-bits) / scale;
  327. in.z = (float)msg->ReadBits(-bits) / scale;
  328. quat[( maxIndex + 1 ) & 3] = in.x;
  329. quat[( maxIndex + 2 ) & 3] = in.y;
  330. quat[( maxIndex + 3 ) & 3] = in.z;
  331. quat[maxIndex] = idMath::Sqrt( idMath::Fabs( 1.0f - in.x * in.x - in.y * in.y - in.z * in.z ) );
  332. axis = quat.ToMat3();
  333. }
  334. }
  335. #endif
  336. /*
  337. ========================
  338. idSerializer::Serialize
  339. ========================
  340. */
  341. ID_INLINE void idSerializer::Serialize( idMat3 & axis ) {
  342. SanityCheck();
  343. Serialize( axis[0] );
  344. Serialize( axis[1] );
  345. Serialize( axis[2] );
  346. }
  347. /*
  348. ========================
  349. idSerializer::SerializeC
  350. ========================
  351. */
  352. ID_INLINE void idSerializer::SerializeC( idMat3 & axis ) {
  353. SanityCheck();
  354. if ( IsWriting() ) {
  355. idCQuat cquat = axis.ToCQuat();
  356. Serialize( cquat.x );
  357. Serialize( cquat.y );
  358. Serialize( cquat.z );
  359. } else if ( IsReading() ) {
  360. idCQuat cquat;
  361. Serialize( cquat.x );
  362. Serialize( cquat.y );
  363. Serialize( cquat.z );
  364. axis = cquat.ToMat3();
  365. }
  366. }
  367. /*
  368. ========================
  369. idSerializer::SerializeListElement
  370. ========================
  371. */
  372. template< typename _type_ >
  373. ID_INLINE void idSerializer::SerializeListElement( const idList<_type_* > & list, const _type_ *&element ) {
  374. SanityCheck();
  375. if ( IsWriting() ) {
  376. int index = list.FindIndex( const_cast<_type_ *>(element) );
  377. assert( index >= 0 );
  378. SerializePacked( index );
  379. } else if ( IsReading() ) {
  380. int index = 0;
  381. SerializePacked( index );
  382. element = list[index];
  383. }
  384. }
  385. /*
  386. ========================
  387. idSerializer::SerializePacked
  388. Writes out 7 bits at a time, using every 8th bit to signify more bits exist
  389. NOTE - Signed values work with this function, but take up more bytes
  390. Use SerializeSPacked if you anticipate lots of negative values
  391. ========================
  392. */
  393. ID_INLINE void idSerializer::SerializePacked(int & original) {
  394. SanityCheck();
  395. if ( IsWriting() ) {
  396. uint32 value = original;
  397. while ( true ) {
  398. uint8 byte = value & 0x7F;
  399. value >>= 7;
  400. byte |= value ? 0x80 : 0;
  401. msg->WriteByte( byte ); // Emit byte
  402. if ( value == 0 ) {
  403. break;
  404. }
  405. }
  406. } else {
  407. uint8 byte = 0x80;
  408. uint32 value = 0;
  409. int32 shift = 0;
  410. while ( byte & 0x80 && shift < 32 ) {
  411. byte = msg->ReadByte();
  412. value |= (byte & 0x7F) << shift;
  413. shift += 7;
  414. }
  415. original = value;
  416. }
  417. }
  418. /*
  419. ========================
  420. idSerializer::SerializeSPacked
  421. Writes out 7 bits at a time, using every 8th bit to signify more bits exist
  422. NOTE - An extra bit of the first byte is used to store the sign
  423. (this function supports negative values, but will use 2 bytes for values greater than 63)
  424. ========================
  425. */
  426. ID_INLINE void idSerializer::SerializeSPacked(int & value) {
  427. SanityCheck();
  428. if ( IsWriting() ) {
  429. uint32 uvalue = idMath::Abs( value );
  430. // Write the first byte specifically to handle the sign bit
  431. uint8 byte = uvalue & 0x3f;
  432. byte |= value < 0 ? 0x40 : 0;
  433. uvalue >>= 6;
  434. byte |= uvalue > 0 ? 0x80 : 0;
  435. msg->WriteByte( byte );
  436. while ( uvalue > 0 ) {
  437. uint8 byte2 = uvalue & 0x7F;
  438. uvalue >>= 7;
  439. byte2 |= uvalue ? 0x80 : 0;
  440. msg->WriteByte( byte2 ); // Emit byte
  441. }
  442. } else {
  443. // Load the first byte specifically to handle the sign bit
  444. uint8 byte = msg->ReadByte();
  445. uint32 uvalue = byte & 0x3f;
  446. bool sgn = (byte & 0x40) ? true : false;
  447. int32 shift = 6;
  448. while ( byte & 0x80 && shift < 32 ) {
  449. byte = msg->ReadByte(); // Read byte
  450. uvalue |= (byte & 0x7F) << shift;
  451. shift += 7;
  452. }
  453. value = sgn ? -((int)uvalue) : uvalue;
  454. }
  455. }
  456. #endif