243 lines
9.1 KiB
C++
243 lines
9.1 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Low level byte swapping routines.
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================
|
|
#ifndef BYTESWAP_H
|
|
#define BYTESWAP_H
|
|
#if defined(_WIN32) && !defined(__GNUG__)
|
|
#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 (int 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 */
|