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
249 lines
8.0 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Low level byte swapping routines. |
|
// |
|
// $NoKeywords: $ |
|
//============================================================================= |
|
#ifndef BYTESWAP_H |
|
#define BYTESWAP_H |
|
#if defined(_WIN32) |
|
#pragma once |
|
#endif |
|
|
|
#include "datamap.h" // Needed for typedescription_t. Note datamap.h is tier1 as well. |
|
|
|
class CByteswap |
|
{ |
|
public: |
|
CByteswap() |
|
{ |
|
// Default behavior sets the target endian to match the machine native endian (no swap). |
|
SetTargetBigEndian( IsMachineBigEndian() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Write a single field. |
|
//----------------------------------------------------------------------------- |
|
void SwapFieldToTargetEndian( void* pOutputBuffer, void *pData, typedescription_t *pField ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Write a block of fields. Works a bit like the saverestore code. |
|
//----------------------------------------------------------------------------- |
|
void SwapFieldsToTargetEndian( void *pOutputBuffer, void *pBaseData, datamap_t *pDataMap ); |
|
|
|
// Swaps fields for the templated type to the output buffer. |
|
template<typename T> inline void SwapFieldsToTargetEndian( T* pOutputBuffer, void *pBaseData, unsigned int objectCount = 1 ) |
|
{ |
|
for ( unsigned int i = 0; i < objectCount; ++i, ++pOutputBuffer ) |
|
{ |
|
SwapFieldsToTargetEndian( (void*)pOutputBuffer, pBaseData, &T::m_DataMap ); |
|
pBaseData = (byte*)pBaseData + sizeof(T); |
|
} |
|
} |
|
|
|
// Swaps fields for the templated type in place. |
|
template<typename T> inline void SwapFieldsToTargetEndian( T* pOutputBuffer, unsigned int objectCount = 1 ) |
|
{ |
|
SwapFieldsToTargetEndian<T>( pOutputBuffer, (void*)pOutputBuffer, objectCount ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// True if the current machine is detected as big endian. |
|
// (Endienness is effectively detected at compile time when optimizations are |
|
// enabled) |
|
//----------------------------------------------------------------------------- |
|
static bool IsMachineBigEndian() |
|
{ |
|
short nIsBigEndian = 1; |
|
|
|
// if we are big endian, the first byte will be a 0, if little endian, it will be a one. |
|
return (bool)(0 == *(char *)&nIsBigEndian ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the target byte ordering we are swapping to or from. |
|
// |
|
// Braindead Endian Reference: |
|
// x86 is LITTLE Endian |
|
// PowerPC is BIG Endian |
|
//----------------------------------------------------------------------------- |
|
inline void SetTargetBigEndian( bool bigEndian ) |
|
{ |
|
m_bBigEndian = bigEndian; |
|
m_bSwapBytes = IsMachineBigEndian() != bigEndian; |
|
} |
|
|
|
// Changes target endian |
|
inline void FlipTargetEndian( void ) |
|
{ |
|
m_bSwapBytes = !m_bSwapBytes; |
|
m_bBigEndian = !m_bBigEndian; |
|
} |
|
|
|
// Forces byte swapping state, regardless of endianess |
|
inline void ActivateByteSwapping( bool bActivate ) |
|
{ |
|
SetTargetBigEndian( IsMachineBigEndian() != bActivate ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns true if the target machine is the same as this one in endianness. |
|
// |
|
// Used to determine when a byteswap needs to take place. |
|
//----------------------------------------------------------------------------- |
|
inline bool IsSwappingBytes( void ) // Are bytes being swapped? |
|
{ |
|
return m_bSwapBytes; |
|
} |
|
|
|
inline bool IsTargetBigEndian( void ) // What is the current target endian? |
|
{ |
|
return m_bBigEndian; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// IsByteSwapped() |
|
// |
|
// When supplied with a chunk of input data and a constant or magic number |
|
// (in native format) determines the endienness of the current machine in |
|
// relation to the given input data. |
|
// |
|
// Returns: |
|
// 1 if input is the same as nativeConstant. |
|
// 0 if input is byteswapped relative to nativeConstant. |
|
// -1 if input is not the same as nativeConstant and not byteswapped either. |
|
// |
|
// ( This is useful for detecting byteswapping in magic numbers in structure |
|
// headers for example. ) |
|
//----------------------------------------------------------------------------- |
|
template<typename T> inline int SourceIsNativeEndian( T input, T nativeConstant ) |
|
{ |
|
// If it's the same, it isn't byteswapped: |
|
if( input == nativeConstant ) |
|
return 1; |
|
|
|
int output; |
|
LowLevelByteSwap<T>( &output, &input ); |
|
if( output == nativeConstant ) |
|
return 0; |
|
|
|
assert( 0 ); // if we get here, input is neither a swapped nor unswapped version of nativeConstant. |
|
return -1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Swaps an input buffer full of type T into the given output buffer. |
|
// |
|
// Swaps [count] items from the inputBuffer to the outputBuffer. |
|
// If inputBuffer is omitted or NULL, then it is assumed to be the same as |
|
// outputBuffer - effectively swapping the contents of the buffer in place. |
|
//----------------------------------------------------------------------------- |
|
template<typename T> inline void SwapBuffer( T* outputBuffer, T* inputBuffer = NULL, int count = 1 ) |
|
{ |
|
assert( count >= 0 ); |
|
assert( outputBuffer ); |
|
|
|
// Fail gracefully in release: |
|
if( count <=0 || !outputBuffer ) |
|
return; |
|
|
|
// Optimization for the case when we are swapping in place. |
|
if( inputBuffer == NULL ) |
|
{ |
|
inputBuffer = outputBuffer; |
|
} |
|
|
|
// Swap everything in the buffer: |
|
for( int i = 0; i < count; i++ ) |
|
{ |
|
LowLevelByteSwap<T>( &outputBuffer[i], &inputBuffer[i] ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Swaps an input buffer full of type T into the given output buffer. |
|
// |
|
// Swaps [count] items from the inputBuffer to the outputBuffer. |
|
// If inputBuffer is omitted or NULL, then it is assumed to be the same as |
|
// outputBuffer - effectively swapping the contents of the buffer in place. |
|
//----------------------------------------------------------------------------- |
|
template<typename T> inline void SwapBufferToTargetEndian( T* outputBuffer, T* inputBuffer = NULL, int count = 1 ) |
|
{ |
|
assert( count >= 0 ); |
|
assert( outputBuffer ); |
|
|
|
// Fail gracefully in release: |
|
if( count <=0 || !outputBuffer ) |
|
return; |
|
|
|
// Optimization for the case when we are swapping in place. |
|
if( inputBuffer == NULL ) |
|
{ |
|
inputBuffer = outputBuffer; |
|
} |
|
|
|
// Are we already the correct endienness? ( or are we swapping 1 byte items? ) |
|
if( !m_bSwapBytes || ( sizeof(T) == 1 ) ) |
|
{ |
|
// If we were just going to swap in place then return. |
|
if( !inputBuffer ) |
|
return; |
|
|
|
// Otherwise copy the inputBuffer to the outputBuffer: |
|
memcpy( outputBuffer, inputBuffer, count * sizeof( T ) ); |
|
return; |
|
|
|
} |
|
|
|
// Swap everything in the buffer: |
|
for( int i = 0; i < count; i++ ) |
|
{ |
|
LowLevelByteSwap<T>( &outputBuffer[i], &inputBuffer[i] ); |
|
} |
|
} |
|
|
|
private: |
|
//----------------------------------------------------------------------------- |
|
// The lowest level byte swapping workhorse of doom. output always contains the |
|
// swapped version of input. ( Doesn't compare machine to target endianness ) |
|
//----------------------------------------------------------------------------- |
|
template<typename T> static void LowLevelByteSwap( T *output, T *input ) |
|
{ |
|
T temp = *output; |
|
#if defined( _X360 ) |
|
// Intrinsics need the source type to be fixed-point |
|
DWORD* word = (DWORD*)input; |
|
switch( sizeof(T) ) |
|
{ |
|
case 8: |
|
{ |
|
__storewordbytereverse( *word, 0, &temp ); |
|
__storewordbytereverse( *(word+1), 4, &temp ); |
|
} |
|
break; |
|
|
|
case 4: |
|
__storewordbytereverse( *word, 0, &temp ); |
|
break; |
|
|
|
case 2: |
|
__storeshortbytereverse( *input, 0, &temp ); |
|
break; |
|
|
|
default: |
|
Assert( "Invalid size in CByteswap::LowLevelByteSwap" && 0 ); |
|
} |
|
#else |
|
for( size_t i = 0; i < sizeof(T); i++ ) |
|
{ |
|
((unsigned char* )&temp)[i] = ((unsigned char*)input)[sizeof(T)-(i+1)]; |
|
} |
|
#endif |
|
Q_memcpy( output, &temp, sizeof(T) ); |
|
} |
|
|
|
unsigned int m_bSwapBytes : 1; |
|
unsigned int m_bBigEndian : 1; |
|
}; |
|
|
|
#endif /* !BYTESWAP_H */
|
|
|