mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
269 lines
8.7 KiB
C++
269 lines
8.7 KiB
C++
//========= Copyright <20> 1996-2006, Valve LLC, All rights reserved. ============
|
||
//
|
||
// Purpose: Low level byte swapping routines.
|
||
//
|
||
// $NoKeywords: $
|
||
//=============================================================================
|
||
#ifndef BYTESWAP_H
|
||
#define BYTESWAP_H
|
||
#if defined(_WIN32)
|
||
#pragma once
|
||
#endif
|
||
|
||
#include "tier0/dbg.h"
|
||
#include "public/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.
|
||
// (Endianness 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:
|
||
if (outputBuffer != inputBuffer)
|
||
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 + 1), 0, &temp);
|
||
__storewordbytereverse(*(word + 0), 4, &temp);
|
||
}
|
||
break;
|
||
|
||
case 4:
|
||
__storewordbytereverse(*word, 0, &temp);
|
||
break;
|
||
|
||
case 2:
|
||
__storeshortbytereverse(*input, 0, &temp);
|
||
break;
|
||
|
||
case 1:
|
||
Q_memcpy(&temp, input, 1);
|
||
break;
|
||
|
||
default:
|
||
Assert("Invalid size in CByteswap::LowLevelByteSwap" && 0);
|
||
}
|
||
#else
|
||
for (unsigned int i = 0; i < sizeof(T); i++)
|
||
{
|
||
((unsigned char*)&temp)[i] = ((unsigned char*)input)[sizeof(T) - (i + 1)];
|
||
}
|
||
#endif
|
||
memcpy(output, &temp, sizeof(T));
|
||
}
|
||
|
||
#if defined( _X360 )
|
||
// specialized for void * to get 360 XDK compile working despite changelist 281331
|
||
//-----------------------------------------------------------------------------
|
||
// The lowest level byte swapping workhorse of doom. output always contains the
|
||
// swapped version of input. ( Doesn't compare machine to target endianness )
|
||
//-----------------------------------------------------------------------------
|
||
template<> static void LowLevelByteSwap(void** output, void** input)
|
||
{
|
||
AssertMsgOnce(sizeof(void*) == sizeof(unsigned int), "void *'s on this platform are not four bytes!");
|
||
__storewordbytereverse(*reinterpret_cast<unsigned int*>(input), 0, output);
|
||
}
|
||
#endif
|
||
|
||
unsigned int m_bSwapBytes : 1;
|
||
unsigned int m_bBigEndian : 1;
|
||
};
|
||
|
||
#endif /* !BYTESWAP_H */
|