Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
8.0 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Low level byte swapping routines.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #ifndef BYTESWAP_H
  8. #define BYTESWAP_H
  9. #if defined(_WIN32)
  10. #pragma once
  11. #endif
  12. #include "datamap.h" // Needed for typedescription_t. Note datamap.h is tier1 as well.
  13. class CByteswap
  14. {
  15. public:
  16. CByteswap()
  17. {
  18. // Default behavior sets the target endian to match the machine native endian (no swap).
  19. SetTargetBigEndian( IsMachineBigEndian() );
  20. }
  21. //-----------------------------------------------------------------------------
  22. // Write a single field.
  23. //-----------------------------------------------------------------------------
  24. void SwapFieldToTargetEndian( void* pOutputBuffer, void *pData, typedescription_t *pField );
  25. //-----------------------------------------------------------------------------
  26. // Write a block of fields. Works a bit like the saverestore code.
  27. //-----------------------------------------------------------------------------
  28. void SwapFieldsToTargetEndian( void *pOutputBuffer, void *pBaseData, datamap_t *pDataMap );
  29. // Swaps fields for the templated type to the output buffer.
  30. template<typename T> inline void SwapFieldsToTargetEndian( T* pOutputBuffer, void *pBaseData, unsigned int objectCount = 1 )
  31. {
  32. for ( unsigned int i = 0; i < objectCount; ++i, ++pOutputBuffer )
  33. {
  34. SwapFieldsToTargetEndian( (void*)pOutputBuffer, pBaseData, &T::m_DataMap );
  35. pBaseData = (byte*)pBaseData + sizeof(T);
  36. }
  37. }
  38. // Swaps fields for the templated type in place.
  39. template<typename T> inline void SwapFieldsToTargetEndian( T* pOutputBuffer, unsigned int objectCount = 1 )
  40. {
  41. SwapFieldsToTargetEndian<T>( pOutputBuffer, (void*)pOutputBuffer, objectCount );
  42. }
  43. //-----------------------------------------------------------------------------
  44. // True if the current machine is detected as big endian.
  45. // (Endienness is effectively detected at compile time when optimizations are
  46. // enabled)
  47. //-----------------------------------------------------------------------------
  48. static bool IsMachineBigEndian()
  49. {
  50. short nIsBigEndian = 1;
  51. // if we are big endian, the first byte will be a 0, if little endian, it will be a one.
  52. return (bool)(0 == *(char *)&nIsBigEndian );
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Sets the target byte ordering we are swapping to or from.
  56. //
  57. // Braindead Endian Reference:
  58. // x86 is LITTLE Endian
  59. // PowerPC is BIG Endian
  60. //-----------------------------------------------------------------------------
  61. inline void SetTargetBigEndian( bool bigEndian )
  62. {
  63. m_bBigEndian = bigEndian;
  64. m_bSwapBytes = IsMachineBigEndian() != bigEndian;
  65. }
  66. // Changes target endian
  67. inline void FlipTargetEndian( void )
  68. {
  69. m_bSwapBytes = !m_bSwapBytes;
  70. m_bBigEndian = !m_bBigEndian;
  71. }
  72. // Forces byte swapping state, regardless of endianess
  73. inline void ActivateByteSwapping( bool bActivate )
  74. {
  75. SetTargetBigEndian( IsMachineBigEndian() != bActivate );
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Returns true if the target machine is the same as this one in endianness.
  79. //
  80. // Used to determine when a byteswap needs to take place.
  81. //-----------------------------------------------------------------------------
  82. inline bool IsSwappingBytes( void ) // Are bytes being swapped?
  83. {
  84. return m_bSwapBytes;
  85. }
  86. inline bool IsTargetBigEndian( void ) // What is the current target endian?
  87. {
  88. return m_bBigEndian;
  89. }
  90. //-----------------------------------------------------------------------------
  91. // IsByteSwapped()
  92. //
  93. // When supplied with a chunk of input data and a constant or magic number
  94. // (in native format) determines the endienness of the current machine in
  95. // relation to the given input data.
  96. //
  97. // Returns:
  98. // 1 if input is the same as nativeConstant.
  99. // 0 if input is byteswapped relative to nativeConstant.
  100. // -1 if input is not the same as nativeConstant and not byteswapped either.
  101. //
  102. // ( This is useful for detecting byteswapping in magic numbers in structure
  103. // headers for example. )
  104. //-----------------------------------------------------------------------------
  105. template<typename T> inline int SourceIsNativeEndian( T input, T nativeConstant )
  106. {
  107. // If it's the same, it isn't byteswapped:
  108. if( input == nativeConstant )
  109. return 1;
  110. int output;
  111. LowLevelByteSwap<T>( &output, &input );
  112. if( output == nativeConstant )
  113. return 0;
  114. assert( 0 ); // if we get here, input is neither a swapped nor unswapped version of nativeConstant.
  115. return -1;
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Swaps an input buffer full of type T into the given output buffer.
  119. //
  120. // Swaps [count] items from the inputBuffer to the outputBuffer.
  121. // If inputBuffer is omitted or NULL, then it is assumed to be the same as
  122. // outputBuffer - effectively swapping the contents of the buffer in place.
  123. //-----------------------------------------------------------------------------
  124. template<typename T> inline void SwapBuffer( T* outputBuffer, T* inputBuffer = NULL, int count = 1 )
  125. {
  126. assert( count >= 0 );
  127. assert( outputBuffer );
  128. // Fail gracefully in release:
  129. if( count <=0 || !outputBuffer )
  130. return;
  131. // Optimization for the case when we are swapping in place.
  132. if( inputBuffer == NULL )
  133. {
  134. inputBuffer = outputBuffer;
  135. }
  136. // Swap everything in the buffer:
  137. for( int i = 0; i < count; i++ )
  138. {
  139. LowLevelByteSwap<T>( &outputBuffer[i], &inputBuffer[i] );
  140. }
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Swaps an input buffer full of type T into the given output buffer.
  144. //
  145. // Swaps [count] items from the inputBuffer to the outputBuffer.
  146. // If inputBuffer is omitted or NULL, then it is assumed to be the same as
  147. // outputBuffer - effectively swapping the contents of the buffer in place.
  148. //-----------------------------------------------------------------------------
  149. template<typename T> inline void SwapBufferToTargetEndian( T* outputBuffer, T* inputBuffer = NULL, int count = 1 )
  150. {
  151. assert( count >= 0 );
  152. assert( outputBuffer );
  153. // Fail gracefully in release:
  154. if( count <=0 || !outputBuffer )
  155. return;
  156. // Optimization for the case when we are swapping in place.
  157. if( inputBuffer == NULL )
  158. {
  159. inputBuffer = outputBuffer;
  160. }
  161. // Are we already the correct endienness? ( or are we swapping 1 byte items? )
  162. if( !m_bSwapBytes || ( sizeof(T) == 1 ) )
  163. {
  164. // If we were just going to swap in place then return.
  165. if( !inputBuffer )
  166. return;
  167. // Otherwise copy the inputBuffer to the outputBuffer:
  168. memcpy( outputBuffer, inputBuffer, count * sizeof( T ) );
  169. return;
  170. }
  171. // Swap everything in the buffer:
  172. for( int i = 0; i < count; i++ )
  173. {
  174. LowLevelByteSwap<T>( &outputBuffer[i], &inputBuffer[i] );
  175. }
  176. }
  177. private:
  178. //-----------------------------------------------------------------------------
  179. // The lowest level byte swapping workhorse of doom. output always contains the
  180. // swapped version of input. ( Doesn't compare machine to target endianness )
  181. //-----------------------------------------------------------------------------
  182. template<typename T> static void LowLevelByteSwap( T *output, T *input )
  183. {
  184. T temp = *output;
  185. #if defined( _X360 )
  186. // Intrinsics need the source type to be fixed-point
  187. DWORD* word = (DWORD*)input;
  188. switch( sizeof(T) )
  189. {
  190. case 8:
  191. {
  192. __storewordbytereverse( *word, 0, &temp );
  193. __storewordbytereverse( *(word+1), 4, &temp );
  194. }
  195. break;
  196. case 4:
  197. __storewordbytereverse( *word, 0, &temp );
  198. break;
  199. case 2:
  200. __storeshortbytereverse( *input, 0, &temp );
  201. break;
  202. default:
  203. Assert( "Invalid size in CByteswap::LowLevelByteSwap" && 0 );
  204. }
  205. #else
  206. for( size_t i = 0; i < sizeof(T); i++ )
  207. {
  208. ((unsigned char* )&temp)[i] = ((unsigned char*)input)[sizeof(T)-(i+1)];
  209. }
  210. #endif
  211. Q_memcpy( output, &temp, sizeof(T) );
  212. }
  213. unsigned int m_bSwapBytes : 1;
  214. unsigned int m_bBigEndian : 1;
  215. };
  216. #endif /* !BYTESWAP_H */