source-sdk-2013-mapbase/sp/src/common/studiobyteswap.cpp
Joe Ludwig beaae8ac45 Updated the SDK with the latest code from the TF and HL2 branches
* Adds support for Visual Studio 2012 and 2013
* VR Mode:
. Switches from headtrack.dll to sourcevr.dll
. Improved readability of the UI in VR
. Removed the IPD calibration tool. TF2 will now obey the Oculus
configuration file. Use the Oculus calibration tool in your SDK or
install and run "OpenVR" under Tools in Steam to calibrate your IPD.
. Added dropdown to enable VR mode in the Video options. Removed the -vr
command line option.
. Added the ability to switch in and out of VR mode without quitting the
game
. By default VR mode will run full screen. To switch back to a
borderless window set the vr_force_windowed convar.
. Added support for VR mode on Linux
* Many assorted bug fixes and other changes from Team Fortress in
various shared files
2013-12-03 08:54:16 -08:00

3169 lines
108 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Swaps the bytes in all file types generated by studiomdl
// (.vvd, .vtx, .mdl, .phy, .ani) so the files can be loaded
// on a big-endian machine, specifically the Xbox360. A new file is generated
// with an extra extension in the form of <filename>.360.<ext>
//
//=============================================================================//
#include "studio.h"
#include "optimize.h"
#include "phyfile.h"
#include "studiobyteswap.h"
#include "vphysics_interface.h"
#undef ALIGN4
#undef ALIGN16
#undef ALIGN32
#define ALIGN4( a ) a = (byte *)((int)((byte *)a + 3) & ~ 3)
#define ALIGN16( a ) a = (byte *)((int)((byte *)a + 15) & ~ 15)
#define ALIGN32( a ) a = (byte *)((int)((byte *)a + 31) & ~ 31)
#define ALIGN64( a ) a = (byte *)((int)((byte *)a + 63) & ~ 63)
// Fixup macros create variables that may not be referenced
#pragma warning( push )
#pragma warning( disable:4189 ) // local variable is initialized but not referenced
#pragma warning( disable:4366 ) // The result of the unary '&' operator may be unaligned
namespace StudioByteSwap
{
static bool g_bVerbose = true;
static bool g_bNativeSrc;
static CByteswap g_Swap;
static IPhysicsCollision *pCollision;
static CompressFunc_t g_pCompressFunc;
void ActivateByteSwapping( bool activate )
{
g_Swap.ActivateByteSwapping( activate );
SourceIsNative( IsPC() );
}
void SourceIsNative( bool bNative )
{
g_bNativeSrc = bNative;
}
void SetCollisionInterface( IPhysicsCollision *pPhysicsCollision )
{
pCollision = pPhysicsCollision;
}
void SetVerbose( bool bVerbose )
{
g_bVerbose = bVerbose;
}
//----------------------------------------------------------------------
// Helper to write a chunk of objects of the same type, and increment the buffer pointers.
//----------------------------------------------------------------------
template<class T> inline void WriteObjects( byte **pOutputBuffer, byte **pBaseData, int objectCount = 1 )
{
T tempObject;
for ( int i = 0; i < objectCount; ++i )
{
Q_memcpy( &tempObject, *pBaseData, sizeof(T) );
g_Swap.SwapFieldsToTargetEndian( &tempObject, &tempObject );
Q_memcpy( *pOutputBuffer, &tempObject, sizeof(T) );
*pOutputBuffer += sizeof(T);
*pBaseData += sizeof(T);
}
}
//----------------------------------------------------------------------
// Helper to write a chunk of objects of the same type, and increment the buffer pointers.
//----------------------------------------------------------------------
template<class T> inline void WriteObjects( T **pOutputBuffer, T **pBaseData, int objectCount = 1 )
{
T tempObject;
for ( int i = 0; i < objectCount; ++i )
{
Q_memcpy( &tempObject, *pBaseData, sizeof(T) );
g_Swap.SwapFieldsToTargetEndian( &tempObject, &tempObject );
Q_memcpy( *pOutputBuffer, &tempObject, sizeof(T) );
++*pOutputBuffer;
++*pBaseData;
}
}
//----------------------------------------------------------------------
// Helper to write a chunk of objects of the same type.
//----------------------------------------------------------------------
template<class T> inline void WriteObjects( byte *pOutputBuffer, byte *pBaseData, int objectCount = 1 )
{
T tempObject;
for ( int i = 0; i < objectCount; ++i )
{
Q_memcpy( &tempObject, pBaseData, sizeof(T) );
g_Swap.SwapFieldsToTargetEndian( &tempObject, &tempObject );
Q_memcpy( pOutputBuffer, &tempObject, sizeof(T) );
pOutputBuffer += sizeof(T);
pBaseData += sizeof(T);
}
}
//----------------------------------------------------------------------
// Helper to write a chunk of objects of the same type.
//----------------------------------------------------------------------
template<class T> inline void WriteObjects( T *pOutputBuffer, T *pBaseData, int objectCount = 1 )
{
T tempObject;
for ( int i = 0; i < objectCount; ++i )
{
Q_memcpy( &tempObject, pBaseData, sizeof(T) );
g_Swap.SwapFieldsToTargetEndian( &tempObject, &tempObject );
Q_memcpy( pOutputBuffer, &tempObject, sizeof(T) );
++pOutputBuffer;
++pBaseData;
}
}
//----------------------------------------------------------------------
// Helper to write a buffer of some integral type, and increment the buffer pointers.
//----------------------------------------------------------------------
template<class T> inline void WriteBuffer( byte **pOutputBuffer, byte **pBaseData, int objectCount = 1 )
{
T tempObject;
for ( int i = 0; i < objectCount; ++i )
{
Q_memcpy( &tempObject, *pBaseData, sizeof(T) );
g_Swap.SwapBufferToTargetEndian( &tempObject, &tempObject );
Q_memcpy( *pOutputBuffer, &tempObject, sizeof(T) );
*pOutputBuffer += sizeof(T);
*pBaseData += sizeof(T);
}
}
//----------------------------------------------------------------------
// Helper to write a buffer of some integral type
//----------------------------------------------------------------------
template<class T> inline void WriteBuffer( byte *pOutputBuffer, byte *pBaseData, int objectCount = 1 )
{
T tempObject;
for ( int i = 0; i < objectCount; ++i )
{
Q_memcpy( &tempObject, pBaseData, sizeof(T) );
g_Swap.SwapBufferToTargetEndian( &tempObject, &tempObject );
Q_memcpy( pOutputBuffer, &tempObject, sizeof(T) );
pOutputBuffer += sizeof(T);
pBaseData += sizeof(T);
}
}
//----------------------------------------------------------------------
// For getting values in the correct source/dest endian format
//----------------------------------------------------------------------
template< class T >
T SrcNative( T *idx )
{
T ret = *idx;
if ( !g_bNativeSrc )
{
g_Swap.SwapBuffer( &ret, idx );
}
return ret;
}
template< class T >
T DestNative( T *idx )
{
T ret = *idx;
if ( g_bNativeSrc )
{
g_Swap.SwapBuffer( &ret, idx );
}
return ret;
}
//----------------------------------------------------------------------
// Declares objects pointers for src/dest buffer
//----------------------------------------------------------------------
#define DECLARE_OBJECT_POINTERS( objPtr, base, type ) \
type* objPtr##Src = (type*)base##Src; \
type* objPtr##Dest = (type*)base##Dest; \
type* objPtr = objPtr##Src;
//----------------------------------------------------------------------
// Declares src/dest byte pointers and sets them to some index offset in the buffers.
//----------------------------------------------------------------------
#define DECLARE_INDEX_POINTERS( ptr, base, index ) \
byte *ptr##Src = (byte*)base##Src + SrcNative( &base##Src->index ); \
byte *ptr##Dest = (byte*)base##Dest + SrcNative( &base##Src->index );
//----------------------------------------------------------------------
// Declares src/dest byte pointers and sets them to some index offset in the buffers.
// If src pointer is misaligned, the fixup method is called.
//----------------------------------------------------------------------
#define DECLARE_INDEX_POINTERS_FIXUP( ptr, base, index ) \
byte *ptr##Src = (byte*)base##Src + SrcNative( &base##Src->index ); \
byte *ptr##Dest = (byte*)base##Dest + SrcNative( &base##Src->index ); \
FIXUP_OFFSETS( ptr, base, index )
//----------------------------------------------------------------------
// Same as DECLARE_OBJECT_POINTERS, but reuses existing type pointers.
//----------------------------------------------------------------------
#define SET_OBJECT_POINTERS( objPtr, base, type ) \
objPtr##Src = (type*)base##Src; \
objPtr##Dest = (type*)base##Dest; \
objPtr = objPtr##Src;
//----------------------------------------------------------------------
// Same as DECLARE_INDEX_POINTERS, but reuses existing byte pointers.
//----------------------------------------------------------------------
#define SET_INDEX_POINTERS( ptr, base, index ) \
ptr##Src = (byte*)base##Src + SrcNative( &base##Src->index ); \
ptr##Dest = (byte*)base##Dest + SrcNative( &base##Src->index );
//----------------------------------------------------------------------
// Same as DECLARE_INDEX_POINTERS, but reuses existing byte pointers.
// If src pointer is misaligned, the fixup method is called.
//----------------------------------------------------------------------
#define SET_INDEX_POINTERS_FIXUP( ptr, base, index ) \
ptr##Src = (byte*)base##Src + SrcNative( &base##Src->index ); \
ptr##Dest = (byte*)base##Dest + SrcNative( &base##Src->index ); \
FIXUP_OFFSETS( ptr, base, index )
//----------------------------------------------------------------------
// for() loop header, updates all three object pointers (src,dest,native)
//----------------------------------------------------------------------
#define ITERATE_BLOCK( objPtr, count ) \
for ( int objPtr##_idx = 0; objPtr##_idx < SrcNative( &count ); ++objPtr##_idx, ++objPtr, ++objPtr##Src, ++objPtr##Dest )
//----------------------------------------------------------------------
// Checks for misaligned source pointer, then calculates the necessary fixup,
// calls the fixup function, and sets the src pointer to the new position.
//----------------------------------------------------------------------
#define FIXUP_OFFSETS( ptr, base, index ) \
{ \
byte *ptr##Fixup = ptr##Src; \
ALIGN4( ptr##Fixup ); \
if ( ptr##Fixup != ptr##Src ) \
{ \
int nShiftBytes = ptr##Fixup - ptr##Src; \
if ( g_bVerbose ) \
Warning( "Shifting misaligned data block by %d bytes at " #base "->" #index "\n", nShiftBytes ); \
int prevBytes = (byte*)ptr##Src - (byte*)g_pDataSrcBase; \
Q_memmove( (byte*)ptr##Src + nShiftBytes, ptr##Src, fixedFileSize - prevBytes ); \
g_pFixPoint = ptr##Src; \
g_nFixupBytes = nShiftBytes; \
fixedFileSize += nShiftBytes; \
if ( fixedFileSize > fileSize + BYTESWAP_ALIGNMENT_PADDING ) \
{ \
Error( "Byteswap buffer overrun - increase BYTESWAP_ALIGNMENT_PADDING!\n" ); \
return 0; \
} \
g_pfnFileProcessFunc( pHdrSrc, UpdateSrcIndexFields ); \
g_pFixPoint = NULL; \
g_nFixupBytes = 0; \
ptr##Src = ptr##Fixup; \
} \
}
typedef void ( *datadescProcessFunc_t)( void *pBase, void *pData, typedescription_t *pFields );
typedef void ( *pfnFixupFunc_t )( void *pDestBase, datadescProcessFunc_t );
static pfnFixupFunc_t g_pfnFileProcessFunc;
static studiohdr_t *g_pHdr;
static const void *g_pDataSrcBase;
static void *g_pFixPoint;
static int g_nFixupBytes;
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool UpdateIndex( void *pBase, int *indexMember )
{
bool bUpdateIndex = false;
int idx = *indexMember;
// Update the index fields
if ( pBase < g_pFixPoint )
{
if ( (byte*)pBase + idx >= g_pFixPoint )
{
bUpdateIndex = true;
}
}
else
{
if ( (byte*)pBase + idx < g_pFixPoint )
{
bUpdateIndex = true;
}
}
// Update the member offset by the global fixup
if ( bUpdateIndex && *indexMember )
{
*indexMember = idx + g_nFixupBytes * Sign(idx);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int GetIntegerFromField( void *pData, int fieldType )
{
if ( fieldType == FIELD_INTEGER )
{
return SrcNative( (int*)pData );
}
else if ( fieldType == FIELD_SHORT )
{
return SrcNative( (short*)pData );
}
Error( "Byteswap macro DEFINE_INDEX using unsupported fieldType %d\n", fieldType );
return 0;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void PutIntegerInField( void *pData, int index, int fieldType )
{
if ( fieldType == FIELD_INTEGER )
{
*(int*)pData = SrcNative( &index );
}
else if ( fieldType == FIELD_SHORT )
{
*(short*)pData = SrcNative( &index );
}
else
{
Error( "Byteswap macro DEFINE_INDEX using unsupported fieldType %d\n", fieldType );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void UpdateSrcIndexFields( void *pBase, void *pData, typedescription_t *pField )
{
if ( pField->flags & FTYPEDESC_INDEX )
{
int index = GetIntegerFromField( pData, pField->fieldType );
if ( UpdateIndex( pBase, &index ) )
{
PutIntegerInField( pData, index, pField->fieldType );
}
}
}
//-----------------------------------------------------------------------------
// Pass a datadesc field to a processing function
//-----------------------------------------------------------------------------
void ProcessField( void *pBase, void *pData, typedescription_t *pField, datadescProcessFunc_t pfn )
{
if ( pfn )
{
pfn( pBase, pData, pField );
}
}
//-----------------------------------------------------------------------------
// Process the fields of a datadesc.
//-----------------------------------------------------------------------------
void ProcessFields( void *pBaseAddress, void *pData, datamap_t *pDataMap, datadescProcessFunc_t pfnProcessFunc )
{
// deal with base class first
if ( pDataMap->baseMap )
{
ProcessFields( pBaseAddress, pData, pDataMap->baseMap, pfnProcessFunc );
}
typedescription_t *pFields = pDataMap->dataDesc;
int fieldCount = pDataMap->dataNumFields;
for ( int i = 0; i < fieldCount; ++i )
{
typedescription_t *pField = &pFields[i];
ProcessField( pBaseAddress, (BYTE*)pData + pField->fieldOffset[ TD_OFFSET_NORMAL ], pField, pfnProcessFunc );
}
}
//-----------------------------------------------------------------------------
// Process the fields of a datadesc.
//-----------------------------------------------------------------------------
void ProcessFields( void *pData, datamap_t *pDataMap, datadescProcessFunc_t pfnProcessFunc )
{
ProcessFields( pData, pData, pDataMap, pfnProcessFunc );
}
//-----------------------------------------------------------------------------
// Process a datadesc field by name
//-----------------------------------------------------------------------------
void ProcessFieldByName( void *pBaseAddress, void *pData, datamap_t *pDataMap, const char *pName, datadescProcessFunc_t pfnProcessFunc )
{
// deal with base class first
if ( pDataMap->baseMap )
{
ProcessFieldByName( pBaseAddress, pData, pDataMap->baseMap, pName, pfnProcessFunc );
}
typedescription_t *pFields = pDataMap->dataDesc;
int fieldCount = pDataMap->dataNumFields;
for ( int i = 0; i < fieldCount; ++i )
{
typedescription_t *pField = &pFields[i];
if ( !Q_stricmp( pField->fieldName, pName ) )
{
ProcessField( pBaseAddress, (BYTE*)pData + pField->fieldOffset[ TD_OFFSET_NORMAL ], pField, pfnProcessFunc );
break;
}
}
}
//-----------------------------------------------------------------------------
// Process a datadesc field by name.
//-----------------------------------------------------------------------------
void ProcessFieldByName( void *pData, datamap_t *pDataMap, const char *pName, datadescProcessFunc_t pfnProcessFunc )
{
ProcessFieldByName( pData, pData, pDataMap, pName, pfnProcessFunc );
}
void ProcessANIFields( void *pDataBase, datadescProcessFunc_t pfnProcessFunc );
void ProcessMDLFields( void *pDataBase, datadescProcessFunc_t pfnProcessFunc );
// Fake header declaration for easier phy swapping
struct swapcompactsurfaceheader_t
{
DECLARE_BYTESWAP_DATADESC();
int size;
int vphysicsID;
short version;
short modelType;
int surfaceSize;
Vector dragAxisAreas;
int axisMapSize;
};
BEGIN_BYTESWAP_DATADESC( swapcompactsurfaceheader_t )
DEFINE_FIELD( size, FIELD_INTEGER ),
DEFINE_FIELD( vphysicsID, FIELD_INTEGER ),
DEFINE_FIELD( version, FIELD_SHORT ),
DEFINE_FIELD( modelType, FIELD_SHORT ),
DEFINE_FIELD( surfaceSize, FIELD_INTEGER ),
DEFINE_FIELD( dragAxisAreas, FIELD_VECTOR ),
DEFINE_FIELD( axisMapSize, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
// Fake header declaration for old style phy format
#if defined( _X360 )
#pragma bitfield_order( push, lsb_to_msb )
#endif
struct legacysurfaceheader_t
{
DECLARE_BYTESWAP_DATADESC();
int size;
float mass_center[3];
float rotation_inertia[3];
float upper_limit_radius;
BEGIN_BITFIELD( bf )
int max_deviation : 8;
int byte_size : 24;
END_BITFIELD()
int offset_ledgetree_root;
int dummy[3];
};
#if defined( _X360 )
#pragma bitfield_order( pop )
#endif
BEGIN_BYTESWAP_DATADESC( legacysurfaceheader_t )
DEFINE_FIELD( size, FIELD_INTEGER ),
DEFINE_ARRAY( mass_center, FIELD_FLOAT, 3 ),
DEFINE_ARRAY( rotation_inertia, FIELD_FLOAT, 3 ),
DEFINE_FIELD( upper_limit_radius, FIELD_FLOAT ),
DEFINE_BITFIELD( bf, FIELD_INTEGER, 32 ),
DEFINE_FIELD( offset_ledgetree_root, FIELD_INTEGER ),
DEFINE_ARRAY( dummy, FIELD_INTEGER, 3 ),
END_BYTESWAP_DATADESC()
//----------------------------------------------------------------------
// Swap a .phy file
// Fixes alignment errors
//----------------------------------------------------------------------
int ByteswapPHY( void *pDestBase, const void *pSrcBase, const int fileSize )
{
Assert( pCollision );
if ( !pCollision )
return 0;
Q_memset( pDestBase, 0, fileSize );
byte *pSrc = (byte*)pSrcBase;
byte *pDest = (byte*)pDestBase;
vcollide_t collide = {0};
// file header
phyheader_t *pHdr = (phyheader_t*)( g_bNativeSrc ? pSrc : pDest );
WriteObjects<phyheader_t>( &pDest, &pSrc );
if ( g_bNativeSrc )
{
// Reset the pointers and let ivp swap the binary physics data
pSrc = (byte*)pSrcBase + pHdr->size;
pDest = (byte*)pDestBase + pHdr->size;
int bufSize = fileSize - pHdr->size;
pCollision->VCollideLoad( &collide, pHdr->solidCount, (const char *)pSrc, bufSize, false );
}
// Swap the collision data headers
for ( int i = 0; i < pHdr->solidCount; ++i )
{
swapcompactsurfaceheader_t *baseHdr = (swapcompactsurfaceheader_t*)( g_bNativeSrc ? pSrc : pDest );
WriteObjects<swapcompactsurfaceheader_t>( pDest, pSrc );
int srcIncrement = baseHdr->surfaceSize + sizeof(swapcompactsurfaceheader_t);
int destIncrement = srcIncrement;
bool bCopyToSrc = !g_bNativeSrc;
if ( baseHdr->vphysicsID != MAKEID('V','P','H','Y') )
{
// May be old phy format
legacysurfaceheader_t *legacyHdr = (legacysurfaceheader_t*)( g_bNativeSrc ? pSrc : pDest );
WriteObjects<legacysurfaceheader_t>( pDest, pSrc );
if ( legacyHdr->dummy[2] == MAKEID('I','V','P','S') || legacyHdr->dummy[2] == 0 )
{
srcIncrement = legacyHdr->byte_size + sizeof(int);
destIncrement = legacyHdr->byte_size + sizeof(swapcompactsurfaceheader_t);
bCopyToSrc = false;
if ( !g_bNativeSrc )
{
// src needs the size member to be native to load vcollides
Q_memcpy( pSrc, pDest, sizeof(int) );
}
}
else
{
// Not recognized
Assert(0);
return 0;
}
}
if ( bCopyToSrc )
{
// src needs the native header data to load the vcollides
Q_memcpy( pSrc, pDest, sizeof(swapcompactsurfaceheader_t) );
}
pSrc += srcIncrement;
pDest += destIncrement;
}
// the rest of the file is text
int currPos = pSrc - (byte*)pSrcBase;
int remainingBytes = fileSize - currPos;
WriteBuffer<char>( &pDest, &pSrc, remainingBytes );
if ( !g_bNativeSrc )
{
// let ivp swap the ledge tree
pSrc = (byte*)pSrcBase + pHdr->size;
int bufSize = fileSize - pHdr->size;
pCollision->VCollideLoad( &collide, pHdr->solidCount, (const char *)pSrc, bufSize, true );
}
// Write out the ledge tree data
pDest = (byte*)pDestBase + pHdr->size;
for ( int i = 0; i < collide.solidCount; ++i )
{
// skip over the size
pDest += sizeof(int);
int offset = pCollision->CollideWrite( (char*)pDest, collide.solids[i], g_bNativeSrc );
int destSize = g_bNativeSrc ? SwapLong( offset ) : offset;
Q_memcpy( pDest - sizeof(int), &destSize, sizeof(int) );
pDest += offset;
}
// Free the memory
pCollision->VCollideUnload( &collide );
int newFileSize = pDest - (byte*)pDestBase + remainingBytes;
if ( g_pCompressFunc )
{
// compress entire swapped PHY
void *pInput = pDestBase;
int inputSize = newFileSize;
void *pOutput;
int outputSize;
if ( g_pCompressFunc( pInput, inputSize, &pOutput, &outputSize ) )
{
// put the compressed version in its place
V_memcpy( pDestBase, pOutput, outputSize );
free( pOutput );
newFileSize = outputSize;
}
}
return newFileSize;
}
//----------------------------------------------------------------------
// Swap a .vvd file
// Doesn't do any alignment fixups
//----------------------------------------------------------------------
int ByteswapVVD( void *pDestBase, const void *pSrcBase, const int fileSize )
{
Q_memset( pDestBase, 0, fileSize );
byte *pDataSrc = (byte*)pSrcBase;
byte *pDataDest = (byte*)pDestBase;
/** FILE HEADER **/
DECLARE_OBJECT_POINTERS( pHdr, pData, vertexFileHeader_t )
WriteObjects<vertexFileHeader_t>( &pDataDest, &pDataSrc );
/** FIXUP TABLE **/
SET_INDEX_POINTERS( pData, pHdr, fixupTableStart )
WriteObjects<vertexFileFixup_t>( &pDataDest, &pDataSrc, SrcNative( &pHdr->numFixups ) );
/** VERTEX DATA **/
SET_INDEX_POINTERS( pData, pHdr, vertexDataStart )
WriteObjects<mstudiovertex_t>( &pDataDest, &pDataSrc, SrcNative( &pHdr->numLODVertexes[0] ) );
/** TANGENT DATA **/
if ( pHdr->tangentDataStart != 0 )
{
SET_INDEX_POINTERS( pData, pHdr, tangentDataStart )
WriteBuffer<float>( &pDataDest, &pDataSrc, 4 * SrcNative( &pHdr->numLODVertexes[0] ) );
}
int newFileSize = pDataDest - (byte*)pDestBase;
if ( g_pCompressFunc )
{
void *pInput = (byte*)pDestBase + sizeof( vertexFileHeader_t );
int inputSize = newFileSize - sizeof( vertexFileHeader_t );
void *pOutput;
int outputSize;
if ( g_pCompressFunc( pInput, inputSize, &pOutput, &outputSize ) )
{
// place the compressed data after the header
V_memcpy( pInput, pOutput, outputSize );
free( pOutput );
newFileSize = sizeof( vertexFileHeader_t ) + outputSize;
}
}
return newFileSize;
}
//----------------------------------------------------------------------
// Swap a .vtx file
// Doesn't do any alignment fixups
//----------------------------------------------------------------------
int ByteswapVTX( void *pDestBase, const void *pSrcBase, const int fileSize )
{
Q_memset( pDestBase, 0, fileSize );
// Do a straight copy first so the string table is transferred
memcpy( pDestBase, pSrcBase, fileSize );
// Start writing the file
byte *pDataSrc = (byte*)pSrcBase;
byte *pDataDest = (byte*)pDestBase;
DECLARE_OBJECT_POINTERS( pVtxHeader, pData, OptimizedModel::FileHeader_t )
WriteObjects( pVtxHeaderDest, pVtxHeaderSrc );
/** BODY PARTS **/
SET_INDEX_POINTERS( pData, pVtxHeader, bodyPartOffset )
DECLARE_OBJECT_POINTERS( pBodyPartHeader, pData, OptimizedModel::BodyPartHeader_t )
ITERATE_BLOCK( pBodyPartHeader, pVtxHeader->numBodyParts )
{
WriteObjects( pBodyPartHeaderDest, pBodyPartHeaderSrc );
/** MODELS **/
SET_INDEX_POINTERS( pData, pBodyPartHeader, modelOffset )
DECLARE_OBJECT_POINTERS( pModelHeader, pData, OptimizedModel::ModelHeader_t )
ITERATE_BLOCK( pModelHeader, pBodyPartHeader->numModels )
{
WriteObjects( pModelHeaderDest, pModelHeaderSrc );
/** MODEL LODS **/
unsigned int meshOffset = 0;
SET_INDEX_POINTERS( pData, pModelHeader, lodOffset )
DECLARE_OBJECT_POINTERS( pModelLODHeader, pData, OptimizedModel::ModelLODHeader_t )
ITERATE_BLOCK( pModelLODHeader, pModelHeader->numLODs )
{
WriteObjects( pModelLODHeaderDest, pModelLODHeaderSrc );
/** MESHES **/
unsigned int prevOffset = meshOffset;
meshOffset = SrcNative( &pModelLODHeader->meshOffset );
if ( prevOffset - sizeof(OptimizedModel::ModelLODHeader_t) == meshOffset )
{
// This LOD shares data with the previous LOD - don't reswap.
continue;
}
SET_INDEX_POINTERS( pData, pModelLODHeader, meshOffset )
DECLARE_OBJECT_POINTERS( pMeshHeader, pData, OptimizedModel::MeshHeader_t )
ITERATE_BLOCK( pMeshHeader, pModelLODHeader->numMeshes )
{
WriteObjects( pMeshHeaderDest, pMeshHeaderSrc );
/** STRIP GROUPS **/
SET_INDEX_POINTERS( pData, pMeshHeader, stripGroupHeaderOffset )
DECLARE_OBJECT_POINTERS( pStripGroupHeader, pData, OptimizedModel::StripGroupHeader_t )
ITERATE_BLOCK( pStripGroupHeader, pMeshHeader->numStripGroups )
{
WriteObjects( pStripGroupHeaderDest, pStripGroupHeaderSrc );
/** STRIP VERTS **/
SET_INDEX_POINTERS( pData, pStripGroupHeader, vertOffset )
WriteObjects<OptimizedModel::Vertex_t>( pDataDest, pDataSrc, SrcNative( &pStripGroupHeader->numVerts ) );
/** VERT INDICES **/
SET_INDEX_POINTERS( pData, pStripGroupHeader, indexOffset )
WriteBuffer<short>( pDataDest, pDataSrc, SrcNative( &pStripGroupHeader->numIndices ) );
/** STRIPS **/
SET_INDEX_POINTERS( pData, pStripGroupHeader, stripOffset )
DECLARE_OBJECT_POINTERS( pStripHeader, pData, OptimizedModel::StripHeader_t )
ITERATE_BLOCK( pStripHeader, pStripGroupHeader->numStrips )
{
WriteObjects( pStripHeaderDest, pStripHeaderSrc );
/** BONE STATE CHANGES **/
SET_INDEX_POINTERS( pData, pStripHeader, boneStateChangeOffset )
WriteObjects<OptimizedModel::BoneStateChangeHeader_t>( pDataDest, pDataSrc, SrcNative( &pStripHeader->numBoneStateChanges ) );
}
}
}
}
}
}
/** MATERIAL REPLACEMENT HEADERS **/
SET_INDEX_POINTERS( pData, pVtxHeader, materialReplacementListOffset )
DECLARE_OBJECT_POINTERS( pMatRepListHeader, pData, OptimizedModel::MaterialReplacementListHeader_t )
ITERATE_BLOCK( pMatRepListHeader, pVtxHeader->numLODs )
{
WriteObjects( pMatRepListHeaderDest, pMatRepListHeaderSrc );
/** MATERIAL REPLACEMENTS **/
SET_INDEX_POINTERS( pData, pMatRepListHeader, replacementOffset )
WriteObjects<OptimizedModel::MaterialReplacementHeader_t>( &pDataDest, &pDataSrc, SrcNative( &pMatRepListHeader->numReplacements ) );
}
int newFileSize = fileSize;
if ( g_pCompressFunc )
{
void *pInput = (byte*)pDestBase + sizeof( OptimizedModel::FileHeader_t );
int inputSize = fileSize - sizeof( OptimizedModel::FileHeader_t );
void *pOutput;
int outputSize;
if ( g_pCompressFunc( pInput, inputSize, &pOutput, &outputSize ) )
{
// place the compressed data after the header
V_memcpy( pInput, pOutput, outputSize );
free( pOutput );
newFileSize = sizeof( OptimizedModel::FileHeader_t ) + outputSize;
}
}
return newFileSize;
}
//----------------------------------------------------------------------
// Swap animation data
// Fixes alignment errors
//----------------------------------------------------------------------
void ByteswapAnimData( mstudioanimdesc_t *pAnimDesc, int section, byte *&pDataSrc, byte *&pDataDest )
{
/** ANIMATIONS **/
DECLARE_OBJECT_POINTERS( pAnimation, pData, mstudioanim_t )
WriteObjects( pAnimationDest, pAnimationSrc );
if ( pAnimation->bone == 255 )
{
// No animation data
pAnimation = 0;
}
while( pAnimation )
{
if ( pAnimation->flags & ( STUDIO_ANIM_RAWROT | STUDIO_ANIM_RAWPOS | STUDIO_ANIM_RAWROT2 ) )
{
if ( pAnimation->flags & STUDIO_ANIM_RAWROT )
{
int offset = (byte*)pAnimation->pQuat48() - (byte*)pAnimation;
pDataSrc = (byte*)pAnimationSrc + offset;
pDataDest = (byte*)pAnimationDest + offset;
// Write the quaternion (bit fields contained in 3 unsigned shorts)
WriteBuffer<short>( &pDataDest, &pDataSrc, 3 );
}
if ( pAnimation->flags & STUDIO_ANIM_RAWROT2 )
{
int offset = (byte*)pAnimation->pQuat64() - (byte*)pAnimation;
pDataSrc = (byte*)pAnimationSrc + offset;
pDataDest = (byte*)pAnimationDest + offset;
// Write the quaternion (bit fields contained in 1 64 bit int
WriteBuffer<int64>( &pDataDest, &pDataSrc, 1 );
}
if ( pAnimation->flags & STUDIO_ANIM_RAWPOS )
{
int offset = (byte*)pAnimation->pPos() - (byte*)pAnimation;
pDataSrc = (byte*)pAnimationSrc + offset;
pDataDest = (byte*)pAnimationDest + offset;
// Write the vector (3 float16)
WriteBuffer<short>( &pDataDest, &pDataSrc, 3 );
}
}
else
{
int offset = (byte*)pAnimation->pRotV() - (byte*)pAnimation;
pDataSrc = (byte*)pAnimationSrc + offset;
pDataDest = (byte*)pAnimationDest + offset;
mstudioanim_valueptr_t *rotvptr = (mstudioanim_valueptr_t*)pDataSrc;
WriteObjects<mstudioanim_valueptr_t>( &pDataDest, &pDataSrc );
int animValueCt = 0;
for ( int idx = 0; idx < 3; ++idx )
{
animValueCt += rotvptr->offset[idx] ? 1 : 0;
}
if ( pAnimation->flags & STUDIO_ANIM_ANIMPOS )
{
int offset = (byte*)pAnimation->pPosV() - (byte*)pAnimation;
pDataSrc = (byte*)pAnimationSrc + offset;
pDataDest = (byte*)pAnimationDest + offset;
mstudioanim_valueptr_t *posvptr = (mstudioanim_valueptr_t*)pDataSrc;
WriteObjects<mstudioanim_valueptr_t>( &pDataDest, &pDataSrc );
for ( int idx = 0; idx < 3; ++idx )
{
animValueCt += posvptr->offset[idx] ? 1 : 0;
}
}
// Write position and rotation animations
// Note: destanimvalue_t is a union that can be either two bytes or a short.
// This structure is used to compress animation data using RLE.
// The first object of a chunk acts as the header, and uses the two bytes to
// store how many objects follow, and how many frames are encoded by them.
// The objects that follow use the short to store a value.
// The total number of chunks has been determined by counting the number of valid (non-zero) offsets.
for ( int animValue = 0; animValue < animValueCt; ++animValue )
{
int encodedFrames = 0;
int totalFrames = SrcNative( &pAnimDesc->numframes );
int sectionFrames = SrcNative( &pAnimDesc->sectionframes );
if ( sectionFrames )
{
int iStartFrame = section * sectionFrames;
int iEndFrame = (section + 1) * sectionFrames;
iStartFrame = min( iStartFrame, totalFrames - 1 );
iEndFrame = min( iEndFrame, totalFrames - 1 );
totalFrames = iEndFrame - iStartFrame + 1;
}
while ( encodedFrames < totalFrames )
{
// Write the first animation value (struct of 2 bytes)
mstudioanimvalue_t *pDestAnimvalue = (mstudioanimvalue_t*)( g_bNativeSrc ? pDataSrc : pDataDest );
WriteBuffer<char>( &pDataDest, &pDataSrc, 2 );
// Write the remaining animation values from this group (shorts)
WriteBuffer<short>( &pDataDest, &pDataSrc, pDestAnimvalue->num.valid );
encodedFrames += pDestAnimvalue->num.total;
}
}
}
// TODOKD: Could add a fixup here with some more work, hasn't been necessary yet
if ( pAnimation->nextoffset )
{
// Set pointers to the next animation
pAnimationSrc = (mstudioanim_t*)( (byte*)pAnimationSrc + SrcNative( &pAnimation->nextoffset ) );
pAnimationDest = (mstudioanim_t*)( (byte*)pAnimationDest + SrcNative( &pAnimation->nextoffset ) );
pAnimation = pAnimationSrc;
// Swap the next animation
WriteObjects( pAnimationDest, pAnimationSrc );
}
else
{
pAnimation = 0;
pDataSrc += sizeof( mstudioanim_t );
pDataDest += sizeof( mstudioanim_t );
}
}
ALIGN4( pDataSrc );
ALIGN4( pDataDest );
}
int ByteswapIKRules( studiohdr_t *&pHdrSrc, int numikrules, int numFrames, byte *&pDataSrc, byte *&pDataDest, int &fixedFileSize, const int fileSize )
{
DECLARE_OBJECT_POINTERS( pIKRule, pData, mstudioikrule_t )
ITERATE_BLOCK( pIKRule, numikrules )
{
WriteObjects<mstudioikrule_t>( pIKRuleDest, pIKRuleSrc );
/** IK ERROR KEYS **/
// Calculate the number of ikerrors by converting the ikerror start and end float values to
// frame numbers. (See the generation of these values in simplify.cpp: ProcessIKRules()).
float start = floorf( SrcNative( &pIKRule->start ) * (numFrames - 1) + 0.5f );
float end = floorf( SrcNative( &pIKRule->end ) * (numFrames - 1) + 0.5f );
int totalerror = (int)( end - start + 1 );
if ( end >= numFrames )
totalerror += 2;
// Uncompressed - only found in some older models (shipped hl2)
if ( pIKRule->ikerrorindex )
{
SET_INDEX_POINTERS_FIXUP( pData, pIKRule, ikerrorindex )
WriteObjects<mstudioikerror_t>( pDataDest, pDataSrc, totalerror );
}
// Compressed - all models since hl2
if ( pIKRule->compressedikerrorindex )
{
SET_INDEX_POINTERS_FIXUP( pData, pIKRule, compressedikerrorindex )
WriteObjects<mstudiocompressedikerror_t>( pDataDest, pDataSrc );
mstudiocompressedikerror_t *pCompressed = (mstudiocompressedikerror_t *)pDataSrc;
// Write the animvalues.
for ( int idx = 0; idx < 6; ++idx )
{
if ( pCompressed->offset[idx] )
{
byte *pAnimvalueSrc = pDataSrc + SrcNative( &pCompressed->offset[idx] );
byte *pAnimvalueDest = pDataDest + SrcNative( &pCompressed->offset[idx] );
int numerror = 0;
while ( numerror < totalerror )
{
// Write the first animation value (struct of 2 bytes)
mstudioanimvalue_t *pDestAnimvalue = (mstudioanimvalue_t*)( g_bNativeSrc ? pAnimvalueSrc : pAnimvalueDest );
WriteBuffer<char>( &pAnimvalueDest, &pAnimvalueSrc, 2 );
// Write the remaining animation values from this group (shorts)
WriteBuffer<short>( &pAnimvalueDest, &pAnimvalueSrc, pDestAnimvalue->num.valid );
numerror += pDestAnimvalue->num.total;
}
}
}
if ( pIKRule->szattachmentindex )
{
SET_INDEX_POINTERS( pData, pIKRule, szattachmentindex )
int size = strlen( (char*)pDataSrc ) + 1;
WriteBuffer<char>( pDataDest, pDataSrc, size );
}
}
}
return fixedFileSize;
}
//----------------------------------------------------------------------
// Swap an .ani file
// Fixes alignment errors
//----------------------------------------------------------------------
int ByteswapANIFile( studiohdr_t* pHdr, void *pDestBase, const void *pSrcBase, const int fileSize )
{
// Note, pHdr came from a native .mdl -
// so the header, animdescs and animblocks are already in native format.
Assert( pHdr );
if ( !pHdr )
return false;
Q_memset( pDestBase, 0, fileSize );
// swap file header
{
byte *pHeaderSrc = (byte *)pSrcBase;
byte *pHeaderDest = (byte *)pDestBase;
DECLARE_OBJECT_POINTERS( pAniHeader, pHeader, studiohdr_t )
WriteObjects( pAniHeaderDest, pAniHeaderSrc );
}
// for fixup functions
int fixedFileSize = fileSize;
g_pfnFileProcessFunc = ProcessANIFields;
g_pDataSrcBase = pSrcBase;
g_pHdr = pHdr;
studiohdr_t *pHdrSrc = pHdr;
// The animdesc_t header is always contained in the mdl file, but its data may be in
// the mdl or the ani. When the data is contained in the mdl, the animdesc index fields
// represent offsets from the location of the animdesc header. When the data is in the ani,
// the index fields contain offsets from the start of the animblock in which the animdesc data is contained.
mstudioanimdesc_t *pAnimDesc = pHdr->pLocalAnimdesc( 0 );
for ( int i = 0; i < pHdr->numlocalanim; ++i, ++pAnimDesc )
{
// printf("anim %d : %d : %d\n", i, pAnimDesc->animblock, pAnimDesc->sectionframes );
if ( pAnimDesc->animblock == -1)
{
// out of date model format
continue;
}
if ( pAnimDesc->animblock == 0 && pAnimDesc->sectionframes == 0)
{
// already saved out
continue;
}
if ( pAnimDesc->sectionframes == 0 )
{
mstudioanimblock_t *pAnimBlock = pHdr->pAnimBlock( pAnimDesc->animblock );
// printf("block %d : start %d + %d\n", pAnimDesc->animblock, pAnimBlock->datastart, pAnimDesc->animindex );
// Base address of the animblock
byte *pBlockBaseSrc = (byte*)pSrcBase + pAnimBlock->datastart;
byte *pBlockBaseDest = (byte*)pDestBase + pAnimBlock->datastart;
// Base address of the animation in the animblock
byte *pDataSrc = pBlockBaseSrc + pAnimDesc->animindex;
byte *pDataDest = pBlockBaseDest + pAnimDesc->animindex;
ByteswapAnimData( pAnimDesc, 0, pDataSrc, pDataDest );
}
else
{
int numsections = pAnimDesc->numframes / pAnimDesc->sectionframes + 2;
for ( int i = 0; i < numsections; ++i )
{
int block = pAnimDesc->pSection( i )->animblock;
int index = pAnimDesc->pSection( i )->animindex;
if ( block != 0 )
{
// printf("%s %d %d\n", pAnimDesc->pszName(), block, index );
mstudioanimblock_t *pAnimBlock = pHdr->pAnimBlock( block );
// Base address of the animblock
byte *pBlockBaseSrc = (byte*)pSrcBase + pAnimBlock->datastart;
byte *pBlockBaseDest = (byte*)pDestBase + pAnimBlock->datastart;
FIXUP_OFFSETS( pBlockBase, pAnimBlock, datastart )
// Base address of the animation in the animblock
byte *pDataSrc = pBlockBaseSrc + index;
byte *pDataDest = pBlockBaseDest + index;
ByteswapAnimData( pAnimDesc, i, pDataSrc, pDataDest );
}
}
}
if ( pAnimDesc->animblock == 0)
{
// already saved out
continue;
}
mstudioanimblock_t *pAnimBlock = pHdr->pAnimBlock( pAnimDesc->animblock );
// Base address of the animblock
byte *pBlockBaseSrc = (byte*)pSrcBase + pAnimBlock->datastart;
byte *pBlockBaseDest = (byte*)pDestBase + pAnimBlock->datastart;
FIXUP_OFFSETS( pBlockBase, pAnimBlock, datastart )
// Base address of the animation in the animblock
byte *pDataSrc = pBlockBaseSrc + pAnimDesc->animindex;
byte *pDataDest = pBlockBaseDest + pAnimDesc->animindex;
FIXUP_OFFSETS( pData, pAnimDesc, animindex )
/** IK RULES **/
if ( pAnimDesc->animblockikruleindex )
{
pDataSrc = (byte*)pBlockBaseSrc + pAnimDesc->animblockikruleindex;
pDataDest = (byte*)pBlockBaseDest + pAnimDesc->animblockikruleindex;
FIXUP_OFFSETS( pData, pAnimDesc, animblockikruleindex )
int numikrules = SrcNative( &pAnimDesc->numikrules );
ByteswapIKRules( pHdrSrc, pAnimDesc->numikrules, pAnimDesc->numframes, pDataSrc, pDataDest, fixedFileSize, fileSize );
}
/** LOCAL HIERARCHY **/
if ( pAnimDesc->localhierarchyindex )
{
pDataSrc = (byte*)pBlockBaseSrc + pAnimDesc->localhierarchyindex;
pDataDest = (byte*)pBlockBaseDest + pAnimDesc->localhierarchyindex;
DECLARE_OBJECT_POINTERS( pLocalHierarchy, pData, mstudiolocalhierarchy_t )
// HACK: Since animdescs are already native, pre-swap pAnimDesc->numlocalhierarchy
// here so the automatic swap inside ITERATE_BLOCK will restore it
int numlocalhierarchy = SrcNative( &pAnimDesc->numlocalhierarchy );
ITERATE_BLOCK( pLocalHierarchy, numlocalhierarchy )
{
WriteObjects<mstudiolocalhierarchy_t>( pLocalHierarchyDest, pLocalHierarchySrc, pAnimDesc->numlocalhierarchy );
/** COMPRESSED IK ERRORS **/
if ( pLocalHierarchy->localanimindex != 0 )
{
// Calculate the number of ikerrors by converting the ikerror start and end float values to
// frame numbers. (See the generation of these values in simplify.cpp: ProcessIKRules()).
int numFrames = pAnimDesc->numframes;
float start = floorf( SrcNative( &pLocalHierarchy->start ) * (numFrames - 1) + 0.5f );
float end = floorf( SrcNative( &pLocalHierarchy->end ) * (numFrames - 1) + 0.5f );
int totalerror = (int)( end - start + 1 );
if ( end >= numFrames )
totalerror += 2;
SET_INDEX_POINTERS( pData, pLocalHierarchy, localanimindex )
WriteObjects<mstudiocompressedikerror_t>( pDataDest, pDataSrc );
mstudiocompressedikerror_t *pCompressed = (mstudiocompressedikerror_t *)pDataSrc;
// Write the animvalues.
for ( int idx = 0; idx < 6; ++idx )
{
if ( pCompressed->offset[idx] )
{
byte *pAnimvalueSrc = pDataSrc + SrcNative( &pCompressed->offset[idx] );
byte *pAnimvalueDest = pDataDest + SrcNative( &pCompressed->offset[idx] );
int numerror = 0;
while ( numerror < totalerror )
{
// Write the first animation value (struct of 2 bytes)
mstudioanimvalue_t *pDestAnimvalue = (mstudioanimvalue_t*)( g_bNativeSrc ? pAnimvalueSrc : pAnimvalueDest );
WriteBuffer<char>( &pAnimvalueDest, &pAnimvalueSrc, 2 );
// Write the remaining animation values from this group (shorts)
WriteBuffer<short>( &pAnimvalueDest, &pAnimvalueSrc, pDestAnimvalue->num.valid );
numerror += pDestAnimvalue->num.total;
}
}
}
}
} // Local Hierarchy block
}
}
// printf("returning %d\n", fixedFileSize );
return fixedFileSize;
}
int ByteswapANI( studiohdr_t* pHdr, void *pDestBase, const void *pSrcBase, const int fileSize )
{
// Make a working copy of the source to allow for alignment fixups
void *pNewSrcBase = malloc( fileSize + BYTESWAP_ALIGNMENT_PADDING );
Q_memcpy( pNewSrcBase, pSrcBase, fileSize );
int fixedFileSize = ByteswapANIFile( pHdr, pDestBase, pNewSrcBase, fileSize );
if ( fixedFileSize != fileSize )
{
int finalSize = ByteswapANIFile( pHdr, pDestBase, pNewSrcBase, fixedFileSize );
if ( finalSize != fixedFileSize )
{
if ( g_bVerbose )
Warning( "Alignment fixups failed on ANI swap!\n" );
fixedFileSize = 0;
}
}
free( pNewSrcBase );
// the compression needs to happen on the final or "fixed" pass
if ( g_pCompressFunc && pHdr->numanimblocks >= 2 && fixedFileSize )
{
// assemble a new anim of compressed anim blocks
// start with original size, with room for alignment padding
fixedFileSize += (pHdr->numanimblocks + 1) * 2048;
byte *pNewDestBase = (byte *)malloc( fixedFileSize );
Q_memset( pNewDestBase, 0, fixedFileSize );
byte *pNewDest = pNewDestBase;
// get the header payload as is
// assuming the header is up to the first anim block
mstudioanimblock_t *pAnimBlock = pHdr->pAnimBlock( 1 );
V_memcpy( pNewDest, pDestBase, pAnimBlock->datastart );
pNewDest += pAnimBlock->datastart;
int padding = AlignValue( (unsigned int)pNewDest - (unsigned int)pNewDestBase, 2048 );
padding -= (unsigned int)pNewDest - (unsigned int)pNewDestBase;
pNewDest += padding;
// iterate and compress anim blocks
for ( int i = 1; i < pHdr->numanimblocks; ++i )
{
pAnimBlock = pHdr->pAnimBlock( i );
void *pInput = (byte *)pDestBase + pAnimBlock->datastart;
int inputSize = pAnimBlock->dataend - pAnimBlock->datastart;
pAnimBlock->datastart = (unsigned int)pNewDest - (unsigned int)pNewDestBase;
void *pOutput;
int outputSize;
if ( g_pCompressFunc( pInput, inputSize, &pOutput, &outputSize ) )
{
V_memcpy( pNewDest, pOutput, outputSize );
pNewDest += outputSize;
free( pOutput );
}
else
{
// as is
V_memcpy( pNewDest, pInput, inputSize );
pNewDest += inputSize;
}
padding = AlignValue( (unsigned int)pNewDest - (unsigned int)pNewDestBase, 2048 );
padding -= (unsigned int)pNewDest - (unsigned int)pNewDestBase;
pNewDest += padding;
pAnimBlock->dataend = (unsigned int)pNewDest - (unsigned int)pNewDestBase;
}
fixedFileSize = pNewDest - pNewDestBase;
V_memcpy( pDestBase, pNewDestBase, fixedFileSize );
free( pNewDestBase );
}
return fixedFileSize;
}
//----------------------------------------------------------------------
// Write a .mdl file in big-endian format
//----------------------------------------------------------------------
int ByteswapMDLFile( void *pDestBase, void *pSrcBase, const int fileSize )
{
// Needed by fixup functions
g_pDataSrcBase = pSrcBase;
g_pfnFileProcessFunc = ProcessMDLFields;
int fixedFileSize = fileSize;
Q_memset( pDestBase, 0, fileSize );
byte *pDataSrc = (byte*)pSrcBase;
byte *pDataDest = (byte*)pDestBase;
/** FILE HEADER **/
DECLARE_OBJECT_POINTERS( pHdr, pData, studiohdr_t )
WriteObjects( pHdrDest, pHdrSrc );
/** BONES **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, boneindex )
DECLARE_OBJECT_POINTERS( pStudioBone, pData, mstudiobone_t )
ITERATE_BLOCK( pStudioBone, pHdr->numbones )
{
WriteObjects( pStudioBoneDest, pStudioBoneSrc );
if ( pStudioBone->procindex )
{
SET_INDEX_POINTERS_FIXUP( pData, pStudioBone, procindex )
unsigned int index = SrcNative( &pStudioBone->proctype );
switch( index )
{
case STUDIO_PROC_AXISINTERP:
{
/** AXIS-INTERP BONES **/
DECLARE_OBJECT_POINTERS( pAxisInterpBone, pData, mstudioaxisinterpbone_t )
WriteObjects( pAxisInterpBoneDest, pAxisInterpBoneSrc );
break;
}
case STUDIO_PROC_QUATINTERP:
{
/** QUAT-INTERP BONES **/
DECLARE_OBJECT_POINTERS( pQuatInterpBone, pData, mstudioquatinterpbone_t )
WriteObjects( pQuatInterpBoneDest, pQuatInterpBoneSrc );
/** QUAT-INTERP TRIGGERS **/
SET_INDEX_POINTERS_FIXUP( pData, pQuatInterpBone, triggerindex )
WriteObjects<mstudioquatinterpinfo_t>( pDataDest, pDataSrc, SrcNative( &pQuatInterpBone->numtriggers ) );
break;
}
case STUDIO_PROC_JIGGLE:
{
/** JIGGLE BONES **/
DECLARE_OBJECT_POINTERS( pJiggleBone, pData, mstudiojigglebone_t )
WriteObjects( pJiggleBoneDest, pJiggleBoneSrc );
break;
}
case STUDIO_PROC_AIMATBONE:
case STUDIO_PROC_AIMATATTACH:
{
/** AIM AT BONES **/
DECLARE_OBJECT_POINTERS( pAimAtBone, pData, mstudioaimatbone_t )
WriteObjects( pAimAtBoneDest, pAimAtBoneSrc );
break;
}
default:
Assert( 0 );
Warning( "Unknown bone type %d found!\n", index );
}
}
}
/** BONE CONTROLLERS **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, bonecontrollerindex )
WriteObjects<mstudiobonecontroller_t>( pDataDest, pDataSrc, SrcNative( &pHdr->numbonecontrollers ) );
/** ATTACHMENTS **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, localattachmentindex )
WriteObjects<mstudioattachment_t>( pDataDest, pDataSrc, SrcNative( &pHdr->numlocalattachments ) );
/** HITBOX SETS **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, hitboxsetindex )
DECLARE_OBJECT_POINTERS( pHitboxSet, pData, mstudiohitboxset_t )
ITERATE_BLOCK( pHitboxSet, pHdr->numhitboxsets )
{
WriteObjects( pHitboxSetDest, pHitboxSetSrc );
/** HITBOXES **/
SET_INDEX_POINTERS_FIXUP( pData, pHitboxSet, hitboxindex )
WriteObjects<mstudiobbox_t>( pDataDest, pDataSrc, SrcNative( &pHitboxSet->numhitboxes ) );
}
/** BONE TABLE **/
SET_INDEX_POINTERS( pData, pHdr, bonetablebynameindex )
WriteBuffer<char>( pDataDest, pDataSrc, SrcNative( &pHdr->numbones ) );
/** ANIMATION DESCRIPTIONS **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, localanimindex )
DECLARE_OBJECT_POINTERS( pAnimDesc, pData, mstudioanimdesc_t )
ITERATE_BLOCK( pAnimDesc, pHdr->numlocalanim )
{
WriteObjects( pAnimDescDest, pAnimDescSrc );
if ( pAnimDesc->animblock == -1 )
{
// out of date model format
continue;
}
// section data can point to both internal and external blocks
int numsections = 0;
if ( pAnimDesc->sectionframes != 0 )
{
numsections = pAnimDesc->numframes / pAnimDesc->sectionframes + 2;
SET_INDEX_POINTERS( pData, pAnimDesc, sectionindex )
DECLARE_OBJECT_POINTERS( pSection, pData, mstudioanimsections_t )
WriteObjects( pSectionDest, pSectionSrc, numsections );
}
if ( pAnimDesc->animblock == 0 )
{
if ( numsections == 0 )
{
SET_INDEX_POINTERS( pData, pAnimDesc, animindex )
ByteswapAnimData( pAnimDesc, 0, pDataSrc, pDataDest );
}
else
{
for ( int i = 0; i < numsections; ++i )
{
if ( pAnimDesc->pSection( i )->animblock == 0 )
{
int index = pAnimDesc->pSection( i )->animindex;
// Base address of the animation in the animblock
byte *pDataSrc = (byte *)pAnimDescSrc + index;
byte *pDataDest = (byte *)pAnimDescDest + index;
ByteswapAnimData( pAnimDesc, i, pDataSrc, pDataDest );
}
}
}
/** IK RULES **/
if ( pAnimDesc->ikruleindex )
{
SET_INDEX_POINTERS_FIXUP( pData, pAnimDesc, ikruleindex )
DECLARE_OBJECT_POINTERS( pIKRule, pData, mstudioikrule_t )
int numframes = SrcNative( &pAnimDesc->numframes );
ByteswapIKRules( pHdrSrc, pAnimDesc->numikrules, numframes, pDataSrc, pDataDest, fixedFileSize, fileSize );
}
/** LOCAL HIERARCHY **/
if ( pAnimDesc->localhierarchyindex )
{
SET_INDEX_POINTERS( pData, pAnimDesc, localhierarchyindex )
DECLARE_OBJECT_POINTERS( pLocalHierarchy, pData, mstudiolocalhierarchy_t )
ITERATE_BLOCK( pLocalHierarchy, pAnimDesc->numlocalhierarchy )
{
WriteObjects<mstudiolocalhierarchy_t>( pLocalHierarchyDest, pLocalHierarchySrc, SrcNative( &pAnimDesc->numlocalhierarchy ) );
/** COMPRESSED IK ERRORS **/
if ( pLocalHierarchy->localanimindex != 0 )
{
// Calculate the number of ikerrors by converting the ikerror start and end float values to
// frame numbers. (See the generation of these values in simplify.cpp: ProcessIKRules()).
int numFrames = SrcNative( &pAnimDesc->numframes );
float start = floorf( SrcNative( &pLocalHierarchy->start ) * (numFrames - 1) + 0.5f );
float end = floorf( SrcNative( &pLocalHierarchy->end ) * (numFrames - 1) + 0.5f );
int totalerror = (int)( end - start + 1 );
if ( end >= numFrames )
totalerror += 2;
SET_INDEX_POINTERS( pData, pLocalHierarchy, localanimindex )
WriteObjects<mstudiocompressedikerror_t>( pDataDest, pDataSrc );
mstudiocompressedikerror_t *pCompressed = (mstudiocompressedikerror_t *)pDataSrc;
// Write the animvalues.
for ( int idx = 0; idx < 6; ++idx )
{
if ( pCompressed->offset[idx] )
{
byte *pAnimvalueSrc = pDataSrc + SrcNative( &pCompressed->offset[idx] );
byte *pAnimvalueDest = pDataDest + SrcNative( &pCompressed->offset[idx] );
int numerror = 0;
while ( numerror < totalerror )
{
// Write the first animation value (struct of 2 bytes)
mstudioanimvalue_t *pDestAnimvalue = (mstudioanimvalue_t*)( g_bNativeSrc ? pAnimvalueSrc : pAnimvalueDest );
WriteBuffer<char>( &pAnimvalueDest, &pAnimvalueSrc, 2 );
// Write the remaining animation values from this group (shorts)
WriteBuffer<short>( &pAnimvalueDest, &pAnimvalueSrc, pDestAnimvalue->num.valid );
numerror += pDestAnimvalue->num.total;
}
}
}
}
}
} // Local Hierarchy block
}
} // Animdesc block
/** MOVEMENTS **/
// Separate loop required by format of mstudioanimdesc_t data
SET_INDEX_POINTERS( pData, pHdr, localanimindex )
SET_OBJECT_POINTERS( pAnimDesc, pData, mstudioanimdesc_t )
ITERATE_BLOCK( pAnimDesc, pHdr->numlocalanim )
{
if ( pAnimDesc->nummovements )
{
SET_INDEX_POINTERS_FIXUP( pData, pAnimDesc, movementindex )
WriteObjects<mstudiomovement_t>( pDataDest, pDataSrc, SrcNative( &pAnimDesc->nummovements ) );
}
}
/** IK RULES (2nd pass) **/
// This is required to support older models that had these lumps in a different
// order - the model version didn't change, so there's no reliable way to detect it.
SET_INDEX_POINTERS( pData, pHdr, localanimindex )
SET_OBJECT_POINTERS( pAnimDesc, pData, mstudioanimdesc_t )
ITERATE_BLOCK( pAnimDesc, pHdr->numlocalanim )
{
if ( pAnimDesc->ikruleindex )
{
// Only need to write the data again if a fixup happens
byte *pTest = (byte*)pAnimDesc + SrcNative( &pAnimDesc->ikruleindex );
SET_INDEX_POINTERS_FIXUP( pData, pAnimDesc, ikruleindex )
if ( pTest == pDataSrc )
continue;
DECLARE_OBJECT_POINTERS( pIKRule, pData, mstudioikrule_t )
ITERATE_BLOCK( pIKRule, pAnimDesc->numikrules )
{
WriteObjects<mstudioikrule_t>( pIKRuleDest, pIKRuleSrc );
/** IK ERROR KEYS **/
// Calculate the number of ikerrors by converting the ikerror start and end float values to
// frame numbers. (See the generation of these values in simplify.cpp: ProcessIKRules()).
int numFrames = SrcNative( &pAnimDesc->numframes );
float start = floorf( SrcNative( &pIKRule->start ) * (numFrames - 1) + 0.5f );
float end = floorf( SrcNative( &pIKRule->end ) * (numFrames - 1) + 0.5f );
int totalerror = (int)( end - start + 1 );
if ( end >= numFrames )
totalerror += 2;
// Uncompressed - only found in some older models (shipped hl2)
if ( pIKRule->ikerrorindex )
{
SET_INDEX_POINTERS_FIXUP( pData, pIKRule, ikerrorindex )
WriteObjects<mstudioikerror_t>( pDataDest, pDataSrc, totalerror );
}
// Compressed - all models since hl2
if ( pIKRule->compressedikerrorindex )
{
SET_INDEX_POINTERS_FIXUP( pData, pIKRule, compressedikerrorindex )
WriteObjects<mstudiocompressedikerror_t>( pDataDest, pDataSrc );
mstudiocompressedikerror_t *pCompressed = (mstudiocompressedikerror_t *)pDataSrc;
// Write the animvalues.
for ( int idx = 0; idx < 6; ++idx )
{
if ( pCompressed->offset[idx] )
{
byte *pAnimvalueSrc = pDataSrc + SrcNative( &pCompressed->offset[idx] );
byte *pAnimvalueDest = pDataDest + SrcNative( &pCompressed->offset[idx] );
int numerror = 0;
while ( numerror < totalerror )
{
// Write the first animation value (struct of 2 bytes)
mstudioanimvalue_t *pDestAnimvalue = (mstudioanimvalue_t*)( g_bNativeSrc ? pAnimvalueSrc : pAnimvalueDest );
WriteBuffer<char>( &pAnimvalueDest, &pAnimvalueSrc, 2 );
// Write the remaining animation values from this group (shorts)
WriteBuffer<short>( &pAnimvalueDest, &pAnimvalueSrc, pDestAnimvalue->num.valid );
numerror += pDestAnimvalue->num.total;
}
}
}
if ( pIKRule->szattachmentindex )
{
SET_INDEX_POINTERS( pData, pIKRule, szattachmentindex )
int size = strlen( (char*)pDataSrc ) + 1;
WriteBuffer<char>( pDataDest, pDataSrc, size );
}
}
}
}
// Local hierarchy keys don't exist in older models
}
/** ZERO FRAMES **/
SET_INDEX_POINTERS( pData, pHdr, localanimindex )
SET_OBJECT_POINTERS( pAnimDesc, pData, mstudioanimdesc_t )
ITERATE_BLOCK( pAnimDesc, pHdr->numlocalanim )
{
if ( pAnimDesc->pZeroFrameData( ) != NULL )
{
int offset = pAnimDesc->pZeroFrameData( ) - (byte *)pAnimDesc;
// Base address of the animation in the animblock
byte *pZeroFrameSrc = (byte *)pAnimDescSrc + offset;
byte *pZeroFrameDest = (byte *)pAnimDescDest + offset;
SET_INDEX_POINTERS( pData, pHdr, boneindex )
SET_OBJECT_POINTERS( pStudioBone, pData, mstudiobone_t )
ITERATE_BLOCK( pStudioBone, pHdr->numbones )
{
if ( pStudioBone->flags & BONE_HAS_SAVEFRAME_POS )
{
for ( int j = 0; j < pAnimDesc->zeroframecount; j++)
{
WriteBuffer<short>( &pZeroFrameDest, &pZeroFrameSrc, 3 );
}
}
if ( pStudioBone->flags & BONE_HAS_SAVEFRAME_ROT )
{
for ( int j = 0; j < pAnimDesc->zeroframecount; j++)
{
WriteBuffer<int64>( &pZeroFrameDest, &pZeroFrameSrc, 1 );
}
}
}
}
}
/** SEQUENCE INFO **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, localseqindex )
DECLARE_OBJECT_POINTERS( pSequence, pData, mstudioseqdesc_t )
ITERATE_BLOCK( pSequence, pHdr->numlocalseq )
{
WriteObjects( pSequenceDest, pSequenceSrc );
/** POSE KEYS **/
if ( pSequence->posekeyindex )
{
SET_INDEX_POINTERS_FIXUP( pData, pSequence, posekeyindex )
WriteBuffer<float>( pDataDest, pDataSrc, SrcNative( &pSequence->groupsize[0] ) + SrcNative( &pSequence->groupsize[1] ) );
}
/** STUDIO EVENTS **/
SET_INDEX_POINTERS_FIXUP( pData, pSequence, eventindex )
WriteObjects<mstudioevent_t>( pDataDest, pDataSrc, SrcNative( &pSequence->numevents ) );
/** AUTOLAYERS **/
SET_INDEX_POINTERS_FIXUP( pData, pSequence, autolayerindex )
WriteObjects<mstudioautolayer_t>( pDataDest, pDataSrc, SrcNative( &pSequence->numautolayers ) );
/** BONE WEIGHTS **/
// Data may be shared across sequences
DECLARE_INDEX_POINTERS_FIXUP( pWeight, pSequence, weightlistindex )
if ( pWeightSrc >= pDataSrc )
{
int numBoneWeights = ( SrcNative( &pSequence->iklockindex ) - SrcNative( &pSequence->weightlistindex ) ) / sizeof(float);
WriteBuffer<float>( pWeightDest, pWeightSrc, numBoneWeights );
}
/** IK LOCKS **/
SET_INDEX_POINTERS_FIXUP( pData, pSequence, iklockindex )
WriteObjects<mstudioiklock_t>( pDataDest, pDataSrc, SrcNative( &pSequence->numiklocks ) );
/** ANIMATION INDICES **/
if ( pSequence->animindexindex )
{
SET_INDEX_POINTERS( pData, pSequence, animindexindex )
WriteBuffer<short>( pDataDest, pDataSrc, SrcNative( &pSequence->groupsize[0] ) * SrcNative( &pSequence->groupsize[1] ) );
}
/** KEYVALUES **/
SET_INDEX_POINTERS( pData, pSequence, keyvalueindex )
WriteBuffer<char>( pDataDest, pDataSrc, SrcNative( &pSequence->keyvaluesize ) );
}
/** TRANSITION GRAPH **/
int numLocalNodes = SrcNative( &pHdr->numlocalnodes );
SET_INDEX_POINTERS_FIXUP( pData, pHdr, localnodenameindex )
WriteBuffer<int>( pDataDest, pDataSrc, numLocalNodes );
/** LOCAL NODES **/
SET_INDEX_POINTERS( pData, pHdr, localnodeindex )
WriteBuffer<char>( pDataDest, pDataSrc, numLocalNodes * numLocalNodes );
/** BODYPART INFO **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, bodypartindex )
DECLARE_OBJECT_POINTERS( pBodypart, pData, mstudiobodyparts_t )
ITERATE_BLOCK( pBodypart, pHdr->numbodyparts )
{
WriteObjects( pBodypartDest, pBodypartSrc );
/** MODEL INFO **/
SET_INDEX_POINTERS_FIXUP( pData, pBodypart, modelindex )
DECLARE_OBJECT_POINTERS( pModel, pData, mstudiomodel_t )
ITERATE_BLOCK( pModel, pBodypart->nummodels )
{
WriteObjects( pModelDest, pModelSrc );
/** MESHES **/
SET_INDEX_POINTERS_FIXUP( pData, pModel, meshindex )
DECLARE_OBJECT_POINTERS( pMesh, pData, mstudiomesh_t )
ITERATE_BLOCK( pMesh, pModel->nummeshes )
{
WriteObjects( pMeshDest, pMeshSrc );
if ( !pMesh->numflexes )
continue;
/** FLEXES **/
SET_INDEX_POINTERS_FIXUP( pData, pMesh, flexindex )
DECLARE_OBJECT_POINTERS( pFlex, pData, mstudioflex_t )
ITERATE_BLOCK( pFlex, pMesh->numflexes )
{
WriteObjects( pFlexDest, pFlexSrc );
/** VERT ANIMS **/
SET_INDEX_POINTERS_FIXUP( pData, pFlex, vertindex )
WriteObjects<mstudiovertanim_t>( pDataDest, pDataSrc, SrcNative( &pFlex->numverts ) );
}
}
/** EYEBALLS **/
SET_INDEX_POINTERS_FIXUP( pData, pModel, eyeballindex )
WriteObjects<mstudioeyeball_t>( pDataDest, pDataSrc, SrcNative( &pModel->numeyeballs ) );
}
}
/** GLOBAL FLEX NAMES **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, flexdescindex )
WriteObjects<mstudioflexdesc_t>( pDataDest, pDataSrc, SrcNative( &pHdr->numflexdesc ) );
/** GLOBAL FLEX CONTROLLERS **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, flexcontrollerindex )
WriteObjects<mstudioflexcontroller_t>( pDataDest, pDataSrc, SrcNative( &pHdr->numflexcontrollers ) );
/** GLOBAL FLEX CONTROLLER REMAPS **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, flexcontrolleruiindex )
WriteObjects<mstudioflexcontrollerui_t>( pDataDest, pDataSrc, SrcNative( &pHdr->numflexcontrollerui ) );
// TODOKD: The remap indices after the flex controller remap headers need to be swapped as well?
/** FLEX RULES **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, flexruleindex )
DECLARE_OBJECT_POINTERS( pFlexRule, pData, mstudioflexrule_t )
ITERATE_BLOCK( pFlexRule, pHdr->numflexrules )
{
WriteObjects( pFlexRuleDest, pFlexRuleSrc );
/** FLEX OPS **/
SET_INDEX_POINTERS_FIXUP( pData, pFlexRule, opindex )
WriteObjects<mstudioflexop_t>( pDataDest, pDataSrc, SrcNative( &pFlexRule->numops ) );
}
/** IK CHAINS **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, ikchainindex )
DECLARE_OBJECT_POINTERS( pIKChain, pData, mstudioikchain_t )
ITERATE_BLOCK( pIKChain, pHdr->numikchains )
{
WriteObjects( pIKChainDest, pIKChainSrc );
/** IK LINKS **/
SET_INDEX_POINTERS( pData, pIKChain, linkindex )
WriteObjects<mstudioiklink_t>( pDataDest, pDataSrc, SrcNative( &pIKChain->numlinks ) );
}
/** IK AUTOPLAY LOCKS **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, localikautoplaylockindex )
WriteObjects<mstudioiklock_t>( pDataDest, pDataSrc, SrcNative( &pHdr->numlocalikautoplaylocks ) );
/** MOUTH INFO **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, mouthindex )
WriteObjects<mstudiomouth_t>( pDataDest, pDataSrc, SrcNative( &pHdr->nummouths ) );
/** POSE PARAMATERS **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, localposeparamindex )
WriteObjects<mstudioposeparamdesc_t>( pDataDest, pDataSrc, SrcNative( &pHdr->numlocalposeparameters ) );
/** MODEL GROUPS **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, includemodelindex )
WriteObjects<mstudiomodelgroup_t>( pDataDest, pDataSrc, SrcNative( &pHdr->numincludemodels ) );
/** ANIMBLOCK GROUP INFO **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, animblockindex )
WriteObjects<mstudioanimblock_t>( pDataDest, pDataSrc, SrcNative( &pHdr->numanimblocks ) );
/** TEXTURE INFO **/
// While swapping, kill off unwanted textures by name
SET_INDEX_POINTERS_FIXUP( pData, pHdr, textureindex )
DECLARE_OBJECT_POINTERS( pTexture, pData, mstudiotexture_t )
int textureCt = SrcNative( &pHdr->numtextures );
int nameOffset = 0;
for ( int i = 0; i < SrcNative( &pHdr->numtextures ); ++i, ++pTexture, ++pTextureSrc )
{
WriteObjects<mstudiotexture_t>( pTextureDest, pTextureSrc );
int destnameindex = SrcNative( &pTexture->sznameindex ) + nameOffset;
pTextureDest->sznameindex = DestNative( &destnameindex );
char *pName = (char*)pTexture + SrcNative( &pTexture->sznameindex );
#if 0 // Undone: Killing textures here can cause crashes at runtime.
// Don't need pupil textures
if ( Q_stristr( pName, "pupil_" ) || !Q_stricmp( pName, "pupil" ) )
{
--textureCt;
nameOffset += sizeof(mstudiotexture_t);
}
else
#endif
{
++pTextureDest;
}
}
pHdrDest->numtextures = DestNative( &textureCt );
/** TEXTURE INDICES **/
SET_INDEX_POINTERS_FIXUP( pData, pHdr, cdtextureindex )
WriteBuffer<int>( &pDataDest, &pDataSrc, SrcNative( &pHdr->numcdtextures ) );
/** TEXTURE DICTIONARY **/
SET_INDEX_POINTERS( pData, pHdr, skinindex )
WriteBuffer<short>( &pDataDest, &pDataSrc, SrcNative( &pHdr->numskinfamilies ) * SrcNative( &pHdr->numskinref ) );
/** KEYVALUES **/
SET_INDEX_POINTERS( pData, pHdr, keyvalueindex )
WriteBuffer<char>( &pDataDest, &pDataSrc, SrcNative( &pHdr->keyvaluesize ) );
/** STUDIOHDR2 **/
if ( pHdr->studiohdr2index )
{
DECLARE_INDEX_POINTERS_FIXUP( pLocalData, pHdr, studiohdr2index )
DECLARE_OBJECT_POINTERS( pStudioHdr2, pLocalData, studiohdr2_t )
// HACK: Pre-swap the constant "1" here so the automatic swap inside ITERATE_BLOCK will restore it
int studiohdr2ct = 1;
studiohdr2ct = SrcNative( &studiohdr2ct );
ITERATE_BLOCK( pStudioHdr2, studiohdr2ct )
{
WriteObjects( pStudioHdr2Dest, pStudioHdr2Src );
/** SRC BONE TRANSFORMS **/
if ( pStudioHdr2->numsrcbonetransform )
{
// Note, srcbonetransformindex is an offset from the start of the file, not the start of the studiohdr2
// as is the convention. That's why the macros can't be used here.
pDataSrc = (byte*)pHdrSrc + SrcNative( &pStudioHdr2->srcbonetransformindex );
pDataDest = (byte*)pHdrDest + SrcNative( &pStudioHdr2->srcbonetransformindex );
WriteObjects<mstudiosrcbonetransform_t>( &pDataDest, &pDataSrc, SrcNative( &pStudioHdr2->numsrcbonetransform ) );
}
if ( pStudioHdr2->linearboneindex )
{
SET_INDEX_POINTERS_FIXUP( pData, pStudioHdr2, linearboneindex )
DECLARE_OBJECT_POINTERS( pLinearBone, pData, mstudiolinearbone_t )
WriteObjects( pLinearBoneDest, pLinearBoneSrc );
int numBones = SrcNative( &pLinearBone->numbones );
SET_INDEX_POINTERS_FIXUP( pData, pLinearBone, flagsindex )
WriteBuffer<int>( &pDataDest, &pDataSrc, numBones );
SET_INDEX_POINTERS_FIXUP( pData, pLinearBone, parentindex )
WriteBuffer<int>( &pDataDest, &pDataSrc, numBones );
SET_INDEX_POINTERS_FIXUP( pData, pLinearBone, posindex )
WriteBuffer<float>( &pDataDest, &pDataSrc, 3*numBones );
SET_INDEX_POINTERS_FIXUP( pData, pLinearBone, quatindex )
WriteBuffer<float>( &pDataDest, &pDataSrc, 4*numBones );
SET_INDEX_POINTERS_FIXUP( pData, pLinearBone, rotindex )
WriteBuffer<float>( &pDataDest, &pDataSrc, 3*numBones );
SET_INDEX_POINTERS_FIXUP( pData, pLinearBone, posetoboneindex )
WriteBuffer<float>( &pDataDest, &pDataSrc, 12*numBones );
SET_INDEX_POINTERS_FIXUP( pData, pLinearBone, posscaleindex )
WriteBuffer<float>( &pDataDest, &pDataSrc, 3*numBones );
SET_INDEX_POINTERS_FIXUP( pData, pLinearBone, rotscaleindex )
WriteBuffer<float>( &pDataDest, &pDataSrc, 3*numBones );
SET_INDEX_POINTERS_FIXUP( pData, pLinearBone, qalignmentindex )
WriteBuffer<float>( &pDataDest, &pDataSrc, 4*numBones );
}
/** BONE FLEX DRIVERS **/
if ( pStudioHdr2->m_nBoneFlexDriverIndex )
{
SET_INDEX_POINTERS_FIXUP( pData, pStudioHdr2, m_nBoneFlexDriverIndex )
DECLARE_OBJECT_POINTERS( pBoneFlexDriver, pData, mstudioboneflexdriver_t )
ITERATE_BLOCK( pBoneFlexDriver, pStudioHdr2->m_nBoneFlexDriverCount )
{
WriteObjects( pBoneFlexDriverDest, pBoneFlexDriverSrc );
/** BONE FLEX DRIVER CONTROLS **/
SET_INDEX_POINTERS_FIXUP( pData, pBoneFlexDriver, m_nControlIndex );
WriteObjects< mstudioboneflexdrivercontrol_t >( &pDataDest, &pDataSrc, SrcNative( &pBoneFlexDriver->m_nControlCount ) );
}
}
}
}
/** STRING TABLE **/
// NOTE: The block of data (above) swapped immediately before the string table MUST update the
// pDataSrc pointer position, in order for this string table offset calculation to work correctly.
// To update the pointer position, pass the pointer address to WriteObjects().
int offset = pDataSrc - (byte*)pSrcBase;
int stringTableBytes = fixedFileSize - offset;
WriteBuffer<char>( pDataDest, pDataSrc, stringTableBytes );
pHdrDest->length = DestNative( &fixedFileSize );
// Cleanup texture paths
// Some older MDL's have double terminal slashes
SET_INDEX_POINTERS( pData, pHdr, cdtextureindex )
int numCdTextures = SrcNative( &pHdr->numcdtextures );
for ( int i = 0; i < numCdTextures; ++i )
{
char *pPath = (char*)pHdrDest + SrcNative( &((int *)pDataSrc)[i] );
int len = strlen( pPath );
if ( len >= 2 && ( pPath[len-1] == '\\' || pPath[len-1] == '/' ) && ( pPath[len-2] == '\\' || pPath[len-2] == '/' ) )
{
pPath[len-1] = '\0';
}
}
return fixedFileSize;
}
//----------------------------------------------------------------------
// Swap a .mdl in two passes - first pass fixes alignment errors by shifting
// the data and updating offsets, then the second pass does the final swap.
//----------------------------------------------------------------------
int ByteswapMDL( void *pDestBase, const void *pSrcBase, const int fileSize )
{
// Make a working copy of the source to allow for alignment fixups
void *pNewSrcBase = malloc( fileSize + BYTESWAP_ALIGNMENT_PADDING );
Q_memcpy( pNewSrcBase, pSrcBase, fileSize );
int fixedFileSize = ByteswapMDLFile( pDestBase, pNewSrcBase, fileSize );
if ( fixedFileSize != fileSize )
{
int finalSize = ByteswapMDLFile( pDestBase, pNewSrcBase, fixedFileSize );
if ( finalSize != fixedFileSize )
{
Warning( "Alignment fixups failed on MDL swap!\n" );
fixedFileSize = 0;
}
}
free( pNewSrcBase );
// the compression needs to happen on the final or "fixed" pass
if ( g_pCompressFunc && fixedFileSize )
{
void *pInput = pDestBase;
int inputSize = fixedFileSize;
void *pOutput;
int outputSize;
if ( g_pCompressFunc( pInput, inputSize, &pOutput, &outputSize ) )
{
V_memcpy( pDestBase, pOutput, outputSize );
free( pOutput );
fixedFileSize = outputSize;
}
}
return fixedFileSize;
}
//----------------------------------------------------------------------
// Determines what kind of file this is and calls the correct swap function
//----------------------------------------------------------------------
int ByteswapStudioFile( const char *pFilename, void *pOutBase, const void *pFileBase, int fileSize, studiohdr_t *pHdr, CompressFunc_t pCompressFunc )
{
assert( pFilename );
assert( pOutBase != pFileBase );
g_pCompressFunc = pCompressFunc;
int retVal = 0;
if ( Q_stristr( pFilename, ".mdl" ) )
{
retVal = ByteswapMDL( pOutBase, pFileBase, fileSize );
}
else if ( Q_stristr( pFilename, ".vvd" ) )
{
retVal = ByteswapVVD( pOutBase, pFileBase, fileSize );
}
else if ( Q_stristr( pFilename, ".vtx" ) )
{
retVal = ByteswapVTX( pOutBase, pFileBase, fileSize );
}
else if ( Q_stristr( pFilename, ".phy" ) )
{
retVal = ByteswapPHY( pOutBase, pFileBase, fileSize );
}
else if ( Q_stristr( pFilename, ".ani" ) )
{
// some dead .ani files exist in the tree
// only process valid .ani files properly hooked to their .mdl
if ( pHdr && pHdr->numanimblocks != 0 )
{
retVal = ByteswapANI( pHdr, pOutBase, pFileBase, fileSize );
}
}
g_pCompressFunc = NULL;
return retVal;
}
//----------------------------------------------------------------------
// LEGACY ANI FIXUPS - No need to update this function
//
// If model data needs to be shifted to fix a misalignment, this function is
// called with the process func "UpdateSrcIndexFields" to update every datadesc field
// that was marked as FIELD_INDEX. Any index that "points" across
// the shifted data location will have its value incremented or decremented
// by the appropriate number of bytes. This misalignment issue only
// applies to some pre-EP2 models, as all newly compiled models are guaranteed
// to be aligned correctly. Therefore, this function should not need to change
// when model formats are updated, as any model compiled with a new format will
// naturally not require this fixup function to be called.
//----------------------------------------------------------------------
void ProcessANIFields( void *pDataBase, datadescProcessFunc_t pfnProcessFunc )
{
studiohdr_t *pHdr = g_pHdr;
byte *pData = (byte*)pDataBase;
byte *pSrcBase = (byte*)g_pDataSrcBase;
// Since the animblocks and animdescs are native data, trick the system
// into not swapping their index values during processing.
bool bNativeSrc = g_bNativeSrc;
g_bNativeSrc = true;
// Update the .mdl's animblock offsets into the .ani file
mstudioanimblock_t *pAnimBlock = pHdr->pAnimBlock( 1 );
for ( int i = 1; i < pHdr->numanimblocks; ++i, ++pAnimBlock )
{
ProcessFields( pSrcBase, pAnimBlock, &mstudioanimblock_t::m_DataMap, pfnProcessFunc );
}
// Update the .mdl's animindex offsets into the .ani file
// Don't bother with local hierarchy keys - they only exist in newer, properly aligned models
mstudioanimdesc_t *pAnimDesc = pHdr->pLocalAnimdesc( 0 );
for ( int i = 0; i < pHdr->numlocalanim; ++i, ++pAnimDesc )
{
mstudioanimblock_t *pAnimBlock = pHdr->pAnimBlock( pAnimDesc->animblock );
byte *pBlockBase = (byte*)pSrcBase + pAnimBlock->datastart;
ProcessFieldByName( pBlockBase, pAnimDesc, &mstudioanimdesc_t::m_DataMap, "animindex", pfnProcessFunc );
ProcessFieldByName( pBlockBase, pAnimDesc, &mstudioanimdesc_t::m_DataMap, "animblockikruleindex", pfnProcessFunc );
}
// Restore the correct native setting
g_bNativeSrc = bNativeSrc;
// Update the .ani file's internal offsets
pAnimDesc = pHdr->pLocalAnimdesc( 0 );
for ( int i = 0; i < pHdr->numlocalanim; ++i, ++pAnimDesc )
{
pAnimBlock = pHdr->pAnimBlock( pAnimDesc->animblock );
byte *pBlockBase = (byte*)pSrcBase + pAnimBlock->datastart;
byte *pData = pBlockBase + pAnimDesc->animindex;
mstudioanim_t* pAnimation = (mstudioanim_t*)pData;
if ( pAnimation->bone == 255 )
{
// No animation data
pAnimation = 0;
}
while( pAnimation )
{
ProcessFields( pAnimation, &mstudioanim_t::m_DataMap, pfnProcessFunc );
if ( pAnimation->nextoffset )
{
pData = (byte*)pAnimation + SrcNative( &pAnimation->nextoffset );
pAnimation = (mstudioanim_t*)pData;
}
else
{
pAnimation = NULL;
}
}
if ( pAnimDesc->animblockikruleindex )
{
pData = (byte*)pBlockBase + pAnimDesc->animblockikruleindex;
mstudioikrule_t *pIKRule = (mstudioikrule_t *)pData;
for ( int i = 0; i < pAnimDesc->numikrules; ++i, ++pIKRule )
{
ProcessFields( pIKRule, &mstudioikrule_t::m_DataMap, pfnProcessFunc );
}
}
}
}
//----------------------------------------------------------------------
// LEGACY MDL FIXUPS - No need to update this function
//
// If model data needs to be shifted to fix a misalignment, this function is
// called with the process func "UpdateSrcIndexFields" to update every datadesc field
// that was marked as FIELD_INDEX. Any index that "points" across
// the shifted data location will have its value incremented or decremented
// by the appropriate number of bytes. This misalignment issue only
// applies to some pre-EP2 models, as all newly compiled models are guaranteed
// to be aligned correctly. Therefore, this function should not need to change
// when model formats are updated, as any model compiled with a new format will
// naturally not require this fixup function to be called.
//----------------------------------------------------------------------
void ProcessMDLFields( void *pDataBase, datadescProcessFunc_t pfnProcessFunc )
{
byte *pData = (byte*)pDataBase;
/** FILE HEADER **/
studiohdr_t *pHdr = (studiohdr_t*)pData;
ProcessFields( pHdr, &studiohdr_t::m_DataMap, pfnProcessFunc );
/** BONES **/
pData = (byte*)pHdr + SrcNative( &pHdr->boneindex );
mstudiobone_t *pBone = (mstudiobone_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numbones ); ++i, ++pBone )
{
ProcessFields( pBone, &mstudiobone_t::m_DataMap, pfnProcessFunc );
if ( pBone->procindex )
{
pData = (byte*)pBone + SrcNative( &pBone->procindex );
unsigned int proctype = SrcNative( &pBone->proctype );
switch( proctype )
{
case STUDIO_PROC_AXISINTERP:
/** AXIS-INTERP BONES **/
ProcessFields( pData, &mstudioaxisinterpbone_t::m_DataMap, pfnProcessFunc );
break;
case STUDIO_PROC_QUATINTERP:
/** QUAT-INTERP BONES **/
ProcessFields( pData, &mstudioquatinterpbone_t::m_DataMap, pfnProcessFunc );
break;
case STUDIO_PROC_JIGGLE:
/** JIGGLE BONES **/
ProcessFields( pData, &mstudiojigglebone_t::m_DataMap, pfnProcessFunc );
break;
case STUDIO_PROC_AIMATBONE:
case STUDIO_PROC_AIMATATTACH:
/** AIM AT BONES **/
ProcessFields( pData, &mstudioaimatbone_t::m_DataMap, pfnProcessFunc );
break;
}
}
}
/** ATTACHMENTS **/
pData = (byte*)pHdr + SrcNative( &pHdr->localattachmentindex );
mstudioattachment_t *pAttachment = (mstudioattachment_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numlocalattachments ); ++i, ++pAttachment )
{
ProcessFields( pAttachment, &mstudioattachment_t::m_DataMap, pfnProcessFunc );
}
/** HITBOX SETS **/
pData = (byte*)pHdr + SrcNative( &pHdr->hitboxsetindex );
mstudiohitboxset_t* pHitboxSet = (mstudiohitboxset_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numhitboxsets ); ++i, ++pHitboxSet )
{
ProcessFields( pHitboxSet, &mstudiohitboxset_t::m_DataMap, pfnProcessFunc );
/** HITBOXES **/
pData = (byte*)pHitboxSet + SrcNative( &pHitboxSet->hitboxindex );
mstudiobbox_t *pBBox = (mstudiobbox_t*)pData;
for ( int i = 0; i < SrcNative( &pHitboxSet->numhitboxes ); ++i, ++pBBox )
{
ProcessFields( pBBox, &mstudiobbox_t::m_DataMap, pfnProcessFunc );
}
}
/** ANIMATION DESCRIPTIONS **/
pData = (byte*)pHdr + SrcNative( &pHdr->localanimindex );
mstudioanimdesc_t* pAnimDesc = (mstudioanimdesc_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numlocalanim ); ++i, ++pAnimDesc )
{
// Can't update animindex or animblockindex for animations that are in a separate .ani file
ProcessFieldByName( pAnimDesc, &mstudioanimdesc_t::m_DataMap, "baseptr", pfnProcessFunc );
ProcessFieldByName( pAnimDesc, &mstudioanimdesc_t::m_DataMap, "sznameindex", pfnProcessFunc );
ProcessFieldByName( pAnimDesc, &mstudioanimdesc_t::m_DataMap, "movementindex", pfnProcessFunc );
ProcessFieldByName( pAnimDesc, &mstudioanimdesc_t::m_DataMap, "ikruleindex", pfnProcessFunc );
/** ANIMATIONS **/
if ( pAnimDesc->animblock == 0 )
{
// Now it's safe to update the animindex
ProcessFieldByName( pAnimDesc, &mstudioanimdesc_t::m_DataMap, "animindex", pfnProcessFunc );
pData = (byte*)pAnimDesc + SrcNative( &pAnimDesc->animindex );
mstudioanim_t* pAnimation = (mstudioanim_t*)pData;
while( pAnimation )
{
ProcessFields( pAnimation, &mstudioanim_t::m_DataMap, pfnProcessFunc );
if ( pAnimation->nextoffset )
{
pData = (byte*)pAnimation + SrcNative( &pAnimation->nextoffset );
pAnimation = (mstudioanim_t*)pData;
}
else
{
pAnimation = NULL;
}
}
}
if ( pAnimDesc->ikruleindex )
{
pData = (byte*)pAnimDesc + SrcNative( &pAnimDesc->ikruleindex );
mstudioikrule_t *pIKRule = (mstudioikrule_t *)pData;
for ( int i = 0; i < SrcNative( &pAnimDesc->numikrules ); ++i, ++pIKRule )
{
ProcessFields( pIKRule, &mstudioikrule_t::m_DataMap, pfnProcessFunc );
}
}
}
/** SEQUENCE INFO **/
pData = (byte*)pHdr + SrcNative( &pHdr->localseqindex );
mstudioseqdesc_t *pSequenceHdr = (mstudioseqdesc_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numlocalseq ); ++i, ++pSequenceHdr )
{
ProcessFields( pSequenceHdr, &mstudioseqdesc_t::m_DataMap, pfnProcessFunc );
/** STUDIO EVENTS **/
pData = (byte*)pHdr + SrcNative( &pSequenceHdr->eventindex );
mstudioevent_t *pEvent = (mstudioevent_t*)pData;
for ( int i = 0; i < SrcNative( &pSequenceHdr->numevents ); ++i, ++pEvent )
{
ProcessFields( pSequenceHdr, &mstudioevent_t::m_DataMap, pfnProcessFunc );
}
}
/** BODYPART INFO **/
pData = (byte*)pHdr + SrcNative( &pHdr->bodypartindex );
mstudiobodyparts_t *pBodypart = (mstudiobodyparts_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numbodyparts ); ++i, ++pBodypart )
{
ProcessFields( pBodypart, &mstudiobodyparts_t::m_DataMap, pfnProcessFunc );
/** MODEL INFO **/
byte *pData = (byte*)pBodypart + SrcNative( &pBodypart->modelindex );
mstudiomodel_t *pModel = (mstudiomodel_t*)pData;
for ( int i = 0; i < SrcNative( &pBodypart->nummodels ); ++i, ++pModel )
{
ProcessFields( pModel, &mstudiomodel_t::m_DataMap, pfnProcessFunc );
/** MESHES **/
pData = (byte*)pModel + SrcNative( &pModel->meshindex );
mstudiomesh_t *pMesh = (mstudiomesh_t*)pData;
for ( int i = 0; i < SrcNative( &pModel->nummeshes ); ++i, ++pMesh )
{
ProcessFields( pMesh, &mstudiomesh_t::m_DataMap, pfnProcessFunc );
if ( !pMesh->numflexes )
continue;
/** FLEXES **/
pData = (byte*)pMesh + SrcNative( &pMesh->flexindex );
mstudioflex_t *pFlex = (mstudioflex_t*)pData;
for ( int i = 0; i < SrcNative( &pMesh->numflexes ); ++i, ++pFlex )
{
ProcessFields( pFlex, &mstudioflex_t::m_DataMap, pfnProcessFunc );
}
}
/** EYEBALLS **/
pData= (byte*)pModel + SrcNative( &pModel->eyeballindex );
mstudioeyeball_t *pEyeball = (mstudioeyeball_t*)pData;
for ( int i = 0; i < SrcNative( &pModel->numeyeballs ); ++i, ++pEyeball )
{
ProcessFields( pEyeball, &mstudioeyeball_t::m_DataMap, pfnProcessFunc );
}
}
}
/** GLOBAL FLEX NAMES **/
pData = (byte*)pHdr + SrcNative( &pHdr->flexdescindex );
mstudioflexdesc_t *pFlexDesc = (mstudioflexdesc_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numflexdesc ); ++i, ++pFlexDesc )
{
ProcessFields( pFlexDesc, &mstudioflexdesc_t::m_DataMap, pfnProcessFunc );
}
/** GLOBAL FLEX CONTROLLERS **/
pData = (byte*)pHdr + SrcNative( &pHdr->flexcontrollerindex );
mstudioflexcontroller_t *pFlexController = (mstudioflexcontroller_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numflexcontrollers ); ++i, ++pFlexController )
{
ProcessFields( pFlexController, &mstudioflexcontroller_t::m_DataMap, pfnProcessFunc );
}
/** GLOBAL FLEX CONTROLLER REMAPS **/
pData = (byte*)pHdr + SrcNative( &pHdr->flexcontrolleruiindex );
mstudioflexcontrollerui_t *pFlexControllerRemap = (mstudioflexcontrollerui_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numflexcontrollerui ); ++i, ++pFlexControllerRemap )
{
ProcessFields( pFlexControllerRemap, &mstudioflexcontrollerui_t::m_DataMap, pfnProcessFunc );
}
/** FLEX RULES **/
pData = (byte*)pHdr + SrcNative( &pHdr->flexruleindex );
mstudioflexrule_t *pFlexRule = (mstudioflexrule_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numflexrules ); ++i, ++pFlexRule )
{
ProcessFields( pFlexRule, &mstudioflexrule_t::m_DataMap, pfnProcessFunc );
}
/** IK CHAINS **/
pData = (byte*)pHdr + SrcNative( &pHdr->ikchainindex );
mstudioikchain_t *pIKChain = (mstudioikchain_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numikchains ); ++i, ++pIKChain )
{
ProcessFields( pIKChain, &mstudioikchain_t::m_DataMap, pfnProcessFunc );
}
/** POSE PARAMATERS **/
pData = (byte*)pHdr + SrcNative( &pHdr->localposeparamindex );
mstudioposeparamdesc_t *pPoseParam = (mstudioposeparamdesc_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numlocalposeparameters ); ++i, ++pPoseParam )
{
ProcessFields( pPoseParam, &mstudioposeparamdesc_t::m_DataMap, pfnProcessFunc );
}
/** MODEL GROUPS **/
pData = (byte*)pHdr + SrcNative( &pHdr->includemodelindex );
mstudiomodelgroup_t *pMdl = (mstudiomodelgroup_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numincludemodels ); ++i, ++pMdl )
{
ProcessFields( pMdl, &mstudiomodelgroup_t::m_DataMap, pfnProcessFunc );
}
/** TEXTURE INFO **/
pData = (byte*)pHdr + SrcNative( &pHdr->textureindex );
mstudiotexture_t *pTexture = (mstudiotexture_t*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numtextures ); ++i, ++pTexture )
{
ProcessFields( pTexture, &mstudiotexture_t::m_DataMap, pfnProcessFunc );
}
/** CDTEXTURE OFFSETS **/
pData = (byte*)pHdr + SrcNative( &pHdr->cdtextureindex );
int *pIdx = (int*)pData;
for ( int i = 0; i < SrcNative( &pHdr->numcdtextures ); ++i, ++pIdx )
{
int idx = SrcNative( pIdx );
UpdateIndex( pHdr, &idx );
*pIdx = SrcNative( &idx );
}
/** STUDIOHDR2 **/
if ( pHdr->studiohdr2index )
{
pData = (byte*)pHdr + SrcNative( &pHdr->studiohdr2index );
studiohdr2_t *pStudioHdr2 = (studiohdr2_t*)pData;
for ( int i = 0; i < 1; ++i, ++pStudioHdr2 )
{
ProcessFields( pStudioHdr2, &studiohdr2_t::m_DataMap, pfnProcessFunc );
/** SRC BONE TRANSFORMS **/
pData = (byte*)pHdr + SrcNative( &pStudioHdr2->srcbonetransformindex );
mstudiosrcbonetransform_t *pSrcBoneTransform = (mstudiosrcbonetransform_t*)pData;
for ( int i = 0; i < SrcNative( &pStudioHdr2->numsrcbonetransform ); ++i, ++pSrcBoneTransform )
{
ProcessFields( pSrcBoneTransform, &mstudiosrcbonetransform_t::m_DataMap, pfnProcessFunc );
}
}
}
}
#pragma warning( pop ) // local variable is initialized but not referenced
} // namespace StudioByteSwap
// Data descriptions for byte swapping - only needed
// for structures that are written to file for use by the game.
// For any fields that reference other data in the file, use the
// DEFINE_INDEX macro to identify them as such.
BEGIN_BYTESWAP_DATADESC( studiohdr_t )
DEFINE_FIELD( id, FIELD_INTEGER ),
DEFINE_FIELD( version, FIELD_INTEGER ),
DEFINE_FIELD( checksum, FIELD_INTEGER ),
DEFINE_ARRAY( name, FIELD_CHARACTER, 64 ),
DEFINE_FIELD( length, FIELD_INTEGER ),
DEFINE_FIELD( eyeposition, FIELD_VECTOR ),
DEFINE_FIELD( illumposition, FIELD_VECTOR ),
DEFINE_FIELD( hull_min, FIELD_VECTOR ),
DEFINE_FIELD( hull_max, FIELD_VECTOR ),
DEFINE_FIELD( view_bbmin, FIELD_VECTOR ),
DEFINE_FIELD( view_bbmax, FIELD_VECTOR ),
DEFINE_FIELD( flags, FIELD_INTEGER ),
DEFINE_FIELD( numbones, FIELD_INTEGER ), // bones
DEFINE_INDEX( boneindex, FIELD_INTEGER ),
DEFINE_FIELD( numbonecontrollers, FIELD_INTEGER ), // bone controllers
DEFINE_INDEX( bonecontrollerindex, FIELD_INTEGER ),
DEFINE_FIELD( numhitboxsets, FIELD_INTEGER ),
DEFINE_INDEX( hitboxsetindex, FIELD_INTEGER ),
DEFINE_FIELD( numlocalanim, FIELD_INTEGER ), // animations/poses
DEFINE_INDEX( localanimindex, FIELD_INTEGER ), // animation descriptions
DEFINE_FIELD( numlocalseq, FIELD_INTEGER ), // sequences
DEFINE_INDEX( localseqindex, FIELD_INTEGER ),
DEFINE_FIELD( activitylistversion, FIELD_INTEGER ), // initialization flag - have the sequences been indexed?
DEFINE_FIELD( eventsindexed, FIELD_INTEGER ),
DEFINE_FIELD( numtextures, FIELD_INTEGER ),
DEFINE_INDEX( textureindex, FIELD_INTEGER ),
DEFINE_FIELD( numcdtextures, FIELD_INTEGER ),
DEFINE_INDEX( cdtextureindex, FIELD_INTEGER ),
DEFINE_FIELD( numskinref, FIELD_INTEGER ),
DEFINE_FIELD( numskinfamilies, FIELD_INTEGER ),
DEFINE_INDEX( skinindex, FIELD_INTEGER ),
DEFINE_FIELD( numbodyparts, FIELD_INTEGER ),
DEFINE_INDEX( bodypartindex, FIELD_INTEGER ),
DEFINE_FIELD( numlocalattachments, FIELD_INTEGER ),
DEFINE_INDEX( localattachmentindex, FIELD_INTEGER ),
DEFINE_FIELD( numlocalnodes, FIELD_INTEGER ),
DEFINE_INDEX( localnodeindex, FIELD_INTEGER ),
DEFINE_INDEX( localnodenameindex, FIELD_INTEGER ),
DEFINE_FIELD( numflexdesc, FIELD_INTEGER ),
DEFINE_INDEX( flexdescindex, FIELD_INTEGER ),
DEFINE_FIELD( numflexcontrollers, FIELD_INTEGER ),
DEFINE_INDEX( flexcontrollerindex, FIELD_INTEGER ),
DEFINE_FIELD( numflexrules, FIELD_INTEGER ),
DEFINE_INDEX( flexruleindex, FIELD_INTEGER ),
DEFINE_FIELD( numikchains, FIELD_INTEGER ),
DEFINE_INDEX( ikchainindex, FIELD_INTEGER ),
DEFINE_FIELD( nummouths, FIELD_INTEGER ),
DEFINE_INDEX( mouthindex, FIELD_INTEGER ),
DEFINE_FIELD( numlocalposeparameters, FIELD_INTEGER ),
DEFINE_INDEX( localposeparamindex, FIELD_INTEGER ),
DEFINE_INDEX( surfacepropindex, FIELD_INTEGER ),
DEFINE_INDEX( keyvalueindex, FIELD_INTEGER ),
DEFINE_FIELD( keyvaluesize, FIELD_INTEGER ),
DEFINE_FIELD( numlocalikautoplaylocks, FIELD_INTEGER ),
DEFINE_INDEX( localikautoplaylockindex, FIELD_INTEGER ),
DEFINE_FIELD( mass, FIELD_FLOAT ),
DEFINE_FIELD( contents, FIELD_INTEGER ),
DEFINE_FIELD( numincludemodels, FIELD_INTEGER ),
DEFINE_INDEX( includemodelindex, FIELD_INTEGER ),
DEFINE_FIELD( virtualModel, FIELD_INTEGER ), // void*
DEFINE_INDEX( szanimblocknameindex, FIELD_INTEGER ),
DEFINE_FIELD( numanimblocks, FIELD_INTEGER ),
DEFINE_INDEX( animblockindex, FIELD_INTEGER ),
DEFINE_FIELD( animblockModel, FIELD_INTEGER ), // void*
DEFINE_INDEX( bonetablebynameindex, FIELD_INTEGER ),
DEFINE_FIELD( pVertexBase, FIELD_INTEGER ), // void*
DEFINE_FIELD( pIndexBase, FIELD_INTEGER ), // void*
DEFINE_FIELD( constdirectionallightdot, FIELD_CHARACTER ), // byte
DEFINE_FIELD( rootLOD, FIELD_CHARACTER ), // byte
DEFINE_FIELD( numAllowedRootLODs, FIELD_CHARACTER ), // byte
DEFINE_ARRAY( unused, FIELD_CHARACTER, 1 ), // byte
DEFINE_INDEX( unused4, FIELD_INTEGER ),
DEFINE_FIELD( numflexcontrollerui, FIELD_INTEGER ),
DEFINE_INDEX( flexcontrolleruiindex, FIELD_INTEGER ),
DEFINE_FIELD( flVertAnimFixedPointScale, FIELD_FLOAT ),
DEFINE_ARRAY( unused3, FIELD_INTEGER, 1 ),
DEFINE_INDEX( studiohdr2index, FIELD_INTEGER ),
DEFINE_ARRAY( unused2, FIELD_INTEGER, 1 ),
END_BYTESWAP_DATADESC()
// NOTE! Next time we up the .mdl file format, remove studiohdr2_t
// and insert all fields in this structure into studiohdr_t.
BEGIN_BYTESWAP_DATADESC( studiohdr2_t )
DEFINE_FIELD( numsrcbonetransform, FIELD_INTEGER ),
DEFINE_INDEX( srcbonetransformindex, FIELD_INTEGER ),
DEFINE_FIELD( illumpositionattachmentindex, FIELD_INTEGER ),
DEFINE_FIELD( flMaxEyeDeflection, FIELD_FLOAT ),
DEFINE_INDEX( linearboneindex, FIELD_INTEGER ),
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_INDEX( m_nBoneFlexDriverCount, FIELD_INTEGER ),
DEFINE_INDEX( m_nBoneFlexDriverIndex, FIELD_INTEGER ),
DEFINE_ARRAY( reserved, FIELD_INTEGER, 56 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiobone_t )
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_FIELD( parent, FIELD_INTEGER ),
DEFINE_ARRAY( bonecontroller, FIELD_INTEGER, 6 ),
DEFINE_FIELD( pos, FIELD_VECTOR ),
DEFINE_FIELD( quat, FIELD_QUATERNION ),
DEFINE_ARRAY( rot, FIELD_FLOAT, 3 ), // RadianEuler
DEFINE_FIELD( posscale, FIELD_VECTOR ),
DEFINE_FIELD( rotscale, FIELD_VECTOR ),
DEFINE_ARRAY( poseToBone, FIELD_FLOAT, 12 ), // matrix3x4_t
DEFINE_FIELD( qAlignment, FIELD_QUATERNION ),
DEFINE_FIELD( flags, FIELD_INTEGER ),
DEFINE_FIELD( proctype, FIELD_INTEGER ),
DEFINE_INDEX( procindex, FIELD_INTEGER ),
DEFINE_INDEX( physicsbone, FIELD_INTEGER ),
DEFINE_INDEX( surfacepropidx, FIELD_INTEGER ),
DEFINE_FIELD( contents, FIELD_INTEGER ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiolinearbone_t )
DEFINE_FIELD( numbones, FIELD_INTEGER ),
DEFINE_INDEX( flagsindex, FIELD_INTEGER ),
DEFINE_INDEX( parentindex, FIELD_INTEGER ),
DEFINE_INDEX( posindex, FIELD_INTEGER ),
DEFINE_INDEX( quatindex, FIELD_INTEGER ),
DEFINE_INDEX( rotindex, FIELD_INTEGER ),
DEFINE_INDEX( posetoboneindex, FIELD_INTEGER ),
DEFINE_INDEX( posscaleindex, FIELD_INTEGER ),
DEFINE_INDEX( rotscaleindex, FIELD_INTEGER ),
DEFINE_INDEX( qalignmentindex, FIELD_INTEGER ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 6 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioboneflexdrivercontrol_t )
DEFINE_INDEX( m_nBoneComponent, FIELD_INTEGER ),
DEFINE_FIELD( m_nFlexControllerIndex, FIELD_INTEGER ),
DEFINE_INDEX( m_flMin, FIELD_FLOAT ),
DEFINE_INDEX( m_flMax, FIELD_FLOAT ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioboneflexdriver_t )
DEFINE_INDEX( m_nBoneIndex, FIELD_INTEGER ),
DEFINE_FIELD( m_nControlCount, FIELD_INTEGER ),
DEFINE_INDEX( m_nControlIndex, FIELD_FLOAT ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 3 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioaxisinterpbone_t )
DEFINE_FIELD( control, FIELD_INTEGER ),
DEFINE_FIELD( axis, FIELD_INTEGER ),
DEFINE_ARRAY( pos, FIELD_VECTOR, 6 ),
DEFINE_ARRAY( quat, FIELD_QUATERNION, 6 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioquatinterpbone_t )
DEFINE_FIELD( control, FIELD_INTEGER ),
DEFINE_FIELD( numtriggers, FIELD_INTEGER ),
DEFINE_INDEX( triggerindex, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiojigglebone_t )
DEFINE_FIELD( flags, FIELD_INTEGER ),
DEFINE_FIELD( length, FIELD_FLOAT ),
DEFINE_FIELD( tipMass, FIELD_FLOAT ),
DEFINE_FIELD( yawStiffness, FIELD_FLOAT ),
DEFINE_FIELD( yawDamping, FIELD_FLOAT ),
DEFINE_FIELD( pitchStiffness, FIELD_FLOAT ),
DEFINE_FIELD( pitchDamping, FIELD_FLOAT ),
DEFINE_FIELD( alongStiffness, FIELD_FLOAT ),
DEFINE_FIELD( alongDamping, FIELD_FLOAT ),
DEFINE_FIELD( angleLimit, FIELD_FLOAT ),
DEFINE_FIELD( minYaw, FIELD_FLOAT ),
DEFINE_FIELD( maxYaw, FIELD_FLOAT ),
DEFINE_FIELD( yawFriction, FIELD_FLOAT ),
DEFINE_FIELD( yawBounce, FIELD_FLOAT ),
DEFINE_FIELD( minPitch, FIELD_FLOAT ),
DEFINE_FIELD( maxPitch, FIELD_FLOAT ),
DEFINE_FIELD( pitchFriction, FIELD_FLOAT ),
DEFINE_FIELD( pitchBounce, FIELD_FLOAT ),
DEFINE_FIELD( baseMass, FIELD_FLOAT ),
DEFINE_FIELD( baseStiffness, FIELD_FLOAT ),
DEFINE_FIELD( baseDamping, FIELD_FLOAT ),
DEFINE_FIELD( baseMinLeft, FIELD_FLOAT ),
DEFINE_FIELD( baseMaxLeft, FIELD_FLOAT ),
DEFINE_FIELD( baseLeftFriction, FIELD_FLOAT ),
DEFINE_FIELD( baseMinUp, FIELD_FLOAT ),
DEFINE_FIELD( baseMaxUp, FIELD_FLOAT ),
DEFINE_FIELD( baseUpFriction, FIELD_FLOAT ),
DEFINE_FIELD( baseMinForward, FIELD_FLOAT ),
DEFINE_FIELD( baseMaxForward, FIELD_FLOAT ),
DEFINE_FIELD( baseForwardFriction, FIELD_FLOAT ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioaimatbone_t )
DEFINE_FIELD( parent, FIELD_INTEGER ),
DEFINE_FIELD( aim, FIELD_INTEGER ),
DEFINE_ARRAY( aimvector, FIELD_FLOAT, 3 ),
DEFINE_ARRAY( upvector, FIELD_FLOAT, 3 ),
DEFINE_ARRAY( basepos, FIELD_FLOAT, 3 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioquatinterpinfo_t )
DEFINE_FIELD( inv_tolerance, FIELD_FLOAT ),
DEFINE_FIELD( trigger, FIELD_QUATERNION ),
DEFINE_FIELD( pos, FIELD_VECTOR ),
DEFINE_FIELD( quat, FIELD_QUATERNION ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiobonecontroller_t )
DEFINE_FIELD( bone, FIELD_INTEGER ),
DEFINE_FIELD( type, FIELD_INTEGER ),
DEFINE_FIELD( start, FIELD_FLOAT ),
DEFINE_FIELD( end, FIELD_FLOAT ),
DEFINE_FIELD( rest, FIELD_INTEGER ),
DEFINE_FIELD( inputfield, FIELD_INTEGER ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioattachment_t )
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_FIELD( flags, FIELD_INTEGER ),
DEFINE_FIELD( localbone, FIELD_INTEGER ),
DEFINE_ARRAY( local, FIELD_FLOAT, 12 ), // matrix3x4_t
DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiohitboxset_t )
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_FIELD( numhitboxes, FIELD_INTEGER ),
DEFINE_INDEX( hitboxindex, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiosrcbonetransform_t )
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_ARRAY( pretransform, FIELD_FLOAT, 12 ), // matrix3x4_t
DEFINE_ARRAY( posttransform, FIELD_FLOAT, 12 ), // matrix3x4_t
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiobbox_t )
DEFINE_FIELD( bone, FIELD_INTEGER ),
DEFINE_FIELD( group, FIELD_INTEGER ),
DEFINE_FIELD( bbmin, FIELD_VECTOR ),
DEFINE_FIELD( bbmax, FIELD_VECTOR ),
DEFINE_INDEX( szhitboxnameindex, FIELD_INTEGER ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioanim_valueptr_t )
DEFINE_ARRAY( offset, FIELD_SHORT, 3 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiolocalhierarchy_t )
DEFINE_FIELD( iBone, FIELD_INTEGER ),
DEFINE_FIELD( iNewParent, FIELD_INTEGER ),
DEFINE_FIELD( start, FIELD_FLOAT ),
DEFINE_FIELD( peak, FIELD_FLOAT ),
DEFINE_FIELD( tail, FIELD_FLOAT ),
DEFINE_FIELD( end, FIELD_FLOAT ),
DEFINE_FIELD( iStart, FIELD_INTEGER ),
DEFINE_INDEX( localanimindex, FIELD_INTEGER ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 4 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioanimsections_t )
DEFINE_FIELD( animblock, FIELD_INTEGER ),
DEFINE_INDEX( animindex, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioanimdesc_t )
DEFINE_INDEX( baseptr, FIELD_INTEGER ),
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_FIELD( fps, FIELD_FLOAT ),
DEFINE_FIELD( flags, FIELD_INTEGER ),
DEFINE_FIELD( numframes, FIELD_INTEGER ),
DEFINE_FIELD( nummovements, FIELD_INTEGER ),
DEFINE_INDEX( movementindex, FIELD_INTEGER ),
DEFINE_ARRAY( unused1, FIELD_INTEGER, 6 ),
DEFINE_FIELD( animblock, FIELD_INTEGER ),
DEFINE_INDEX( animindex, FIELD_INTEGER ),
DEFINE_FIELD( numikrules, FIELD_INTEGER ),
DEFINE_INDEX( ikruleindex, FIELD_INTEGER ),
DEFINE_INDEX( animblockikruleindex, FIELD_INTEGER ),
DEFINE_FIELD( numlocalhierarchy, FIELD_INTEGER ),
DEFINE_INDEX( localhierarchyindex, FIELD_INTEGER ),
DEFINE_INDEX( sectionindex, FIELD_INTEGER ),
DEFINE_FIELD( sectionframes, FIELD_INTEGER ),
DEFINE_FIELD( zeroframespan, FIELD_SHORT ),
DEFINE_FIELD( zeroframecount, FIELD_SHORT ),
DEFINE_INDEX( zeroframeindex, FIELD_INTEGER ),
DEFINE_FIELD( zeroframestalltime, FIELD_FLOAT ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioanim_t )
DEFINE_FIELD( bone, FIELD_CHARACTER ),
DEFINE_FIELD( flags, FIELD_CHARACTER ),
DEFINE_INDEX( nextoffset, FIELD_SHORT ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioikerror_t )
DEFINE_FIELD( pos, FIELD_VECTOR ),
DEFINE_FIELD( q, FIELD_QUATERNION ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiocompressedikerror_t )
DEFINE_ARRAY( scale, FIELD_FLOAT, 6 ),
DEFINE_ARRAY( offset, FIELD_SHORT, 6 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioikrule_t )
DEFINE_FIELD( index, FIELD_INTEGER ),
DEFINE_FIELD( type, FIELD_INTEGER ),
DEFINE_FIELD( chain, FIELD_INTEGER ),
DEFINE_FIELD( bone, FIELD_INTEGER ),
DEFINE_FIELD( slot, FIELD_INTEGER ),
DEFINE_FIELD( height, FIELD_FLOAT ),
DEFINE_FIELD( radius, FIELD_FLOAT ),
DEFINE_FIELD( floor, FIELD_FLOAT ),
DEFINE_FIELD( pos, FIELD_VECTOR ),
DEFINE_FIELD( q, FIELD_QUATERNION ),
DEFINE_INDEX( compressedikerrorindex, FIELD_INTEGER ),
DEFINE_FIELD( unused2, FIELD_INTEGER ),
DEFINE_FIELD( iStart, FIELD_INTEGER ),
DEFINE_INDEX( ikerrorindex, FIELD_INTEGER ),
DEFINE_FIELD( start, FIELD_FLOAT ),
DEFINE_FIELD( peak, FIELD_FLOAT ),
DEFINE_FIELD( tail, FIELD_FLOAT ),
DEFINE_FIELD( end, FIELD_FLOAT ),
DEFINE_FIELD( unused3, FIELD_FLOAT ),
DEFINE_FIELD( contact, FIELD_FLOAT ),
DEFINE_FIELD( drop, FIELD_FLOAT ),
DEFINE_FIELD( top, FIELD_FLOAT ),
DEFINE_FIELD( unused6, FIELD_INTEGER ),
DEFINE_FIELD( unused7, FIELD_INTEGER ),
DEFINE_FIELD( unused8, FIELD_INTEGER ),
DEFINE_INDEX( szattachmentindex, FIELD_INTEGER ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 7 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiomovement_t )
DEFINE_FIELD( endframe, FIELD_INTEGER ),
DEFINE_FIELD( motionflags, FIELD_INTEGER ),
DEFINE_FIELD( v0, FIELD_FLOAT ),
DEFINE_FIELD( v1, FIELD_FLOAT ),
DEFINE_FIELD( angle, FIELD_FLOAT ),
DEFINE_FIELD( vector, FIELD_VECTOR ),
DEFINE_FIELD( position, FIELD_VECTOR ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioseqdesc_t )
DEFINE_INDEX( baseptr, FIELD_INTEGER ),
DEFINE_INDEX( szlabelindex, FIELD_INTEGER ),
DEFINE_INDEX( szactivitynameindex, FIELD_INTEGER ),
DEFINE_FIELD( flags, FIELD_INTEGER ), // looping/non-looping flags
DEFINE_FIELD( activity, FIELD_INTEGER ), // initialized at loadtime to game DLL values
DEFINE_FIELD( actweight, FIELD_INTEGER ),
DEFINE_FIELD( numevents, FIELD_INTEGER ),
DEFINE_INDEX( eventindex, FIELD_INTEGER ),
DEFINE_FIELD( bbmin, FIELD_VECTOR ),
DEFINE_FIELD( bbmax, FIELD_VECTOR ),
DEFINE_FIELD( numblends, FIELD_INTEGER ),
DEFINE_INDEX( animindexindex, FIELD_INTEGER ),
DEFINE_INDEX( movementindex, FIELD_INTEGER ), // [blend] float array for blended movement
DEFINE_ARRAY( groupsize, FIELD_INTEGER, 2 ),
DEFINE_ARRAY( paramindex, FIELD_INTEGER, 2 ), // X, Y, Z, XR, YR, ZR
DEFINE_ARRAY( paramstart, FIELD_FLOAT, 2 ), // local (0..1) starting value
DEFINE_ARRAY( paramend, FIELD_FLOAT, 2 ), // local (0..1) ending value
DEFINE_FIELD( paramparent, FIELD_INTEGER ),
DEFINE_FIELD( fadeintime, FIELD_FLOAT ), // ideal cross fate in time (0.2 default)
DEFINE_FIELD( fadeouttime, FIELD_FLOAT ), // ideal cross fade out time (0.2 default)
DEFINE_FIELD( localentrynode, FIELD_INTEGER ), // transition node at entry
DEFINE_FIELD( localexitnode, FIELD_INTEGER ), // transition node at exit
DEFINE_FIELD( nodeflags, FIELD_INTEGER ), // transition rules
DEFINE_FIELD( entryphase, FIELD_FLOAT ), // used to match entry gait
DEFINE_FIELD( exitphase, FIELD_FLOAT ), // used to match exit gait
DEFINE_FIELD( lastframe, FIELD_FLOAT ), // frame that should generation EndOfSequence
DEFINE_FIELD( nextseq, FIELD_INTEGER ), // auto advancing sequences
DEFINE_FIELD( pose, FIELD_INTEGER ), // index of delta animation between end and nextseq
DEFINE_FIELD( numikrules, FIELD_INTEGER ),
DEFINE_FIELD( numautolayers, FIELD_INTEGER ),
DEFINE_INDEX( autolayerindex, FIELD_INTEGER ),
DEFINE_INDEX( weightlistindex, FIELD_INTEGER ),
DEFINE_INDEX( posekeyindex, FIELD_INTEGER ),
DEFINE_FIELD( numiklocks, FIELD_INTEGER ),
DEFINE_INDEX( iklockindex, FIELD_INTEGER ),
DEFINE_INDEX( keyvalueindex, FIELD_INTEGER ),
DEFINE_FIELD( keyvaluesize, FIELD_INTEGER ),
DEFINE_INDEX( cycleposeindex, FIELD_INTEGER ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 7 ), // remove/add as appropriate (grow back to 8 ints on version change!)
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioevent_t )
DEFINE_FIELD( cycle, FIELD_FLOAT ),
DEFINE_FIELD( event, FIELD_INTEGER ),
DEFINE_FIELD( type, FIELD_INTEGER ),
DEFINE_ARRAY( options, FIELD_CHARACTER, 64 ),
DEFINE_INDEX( szeventindex, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioautolayer_t )
DEFINE_FIELD( iSequence, FIELD_SHORT ),
DEFINE_FIELD( iPose, FIELD_SHORT ),
DEFINE_FIELD( flags, FIELD_INTEGER ),
DEFINE_FIELD( start, FIELD_FLOAT ),
DEFINE_FIELD( peak, FIELD_FLOAT ),
DEFINE_FIELD( tail, FIELD_FLOAT ),
DEFINE_FIELD( end, FIELD_FLOAT ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioiklock_t )
DEFINE_FIELD( chain, FIELD_INTEGER ),
DEFINE_FIELD( flPosWeight, FIELD_FLOAT ),
DEFINE_FIELD( flLocalQWeight, FIELD_FLOAT ),
DEFINE_FIELD( flags, FIELD_INTEGER ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 4 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiobodyparts_t )
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_FIELD( nummodels, FIELD_INTEGER ),
DEFINE_FIELD( base, FIELD_INTEGER ),
DEFINE_INDEX( modelindex, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiomodel_t )
DEFINE_ARRAY( name, FIELD_CHARACTER, 64 ),
DEFINE_FIELD( type, FIELD_INTEGER ),
DEFINE_FIELD( boundingradius, FIELD_FLOAT ),
DEFINE_FIELD( nummeshes, FIELD_INTEGER ),
DEFINE_INDEX( meshindex, FIELD_INTEGER ),
DEFINE_FIELD( numvertices, FIELD_INTEGER ),
DEFINE_INDEX( vertexindex, FIELD_INTEGER ),
DEFINE_INDEX( tangentsindex, FIELD_INTEGER ),
DEFINE_FIELD( numattachments, FIELD_INTEGER ),
DEFINE_INDEX( attachmentindex, FIELD_INTEGER ),
DEFINE_FIELD( numeyeballs, FIELD_INTEGER ),
DEFINE_INDEX( eyeballindex, FIELD_INTEGER ),
DEFINE_EMBEDDED( vertexdata ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudio_modelvertexdata_t )
DEFINE_FIELD( pVertexData, FIELD_INTEGER ), // void*
DEFINE_FIELD( pTangentData, FIELD_INTEGER ), // void*
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioflexdesc_t )
DEFINE_INDEX( szFACSindex, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioflexcontroller_t )
DEFINE_INDEX( sztypeindex, FIELD_INTEGER ),
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_FIELD( localToGlobal, FIELD_INTEGER ),
DEFINE_FIELD( min, FIELD_FLOAT ),
DEFINE_FIELD( max, FIELD_FLOAT ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioflexcontrollerui_t )
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_INDEX( szindex0, FIELD_INTEGER ),
DEFINE_INDEX( szindex1, FIELD_INTEGER ),
DEFINE_INDEX( szindex2, FIELD_INTEGER ),
DEFINE_FIELD( remaptype, FIELD_CHARACTER ),
DEFINE_FIELD( stereo, FIELD_BOOLEAN ),
DEFINE_ARRAY( unused, FIELD_CHARACTER, 2 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioflexrule_t )
DEFINE_FIELD( flex, FIELD_INTEGER ),
DEFINE_FIELD( numops, FIELD_INTEGER ),
DEFINE_INDEX( opindex, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioflexop_t )
DEFINE_FIELD( op, FIELD_INTEGER ),
DEFINE_FIELD( d, FIELD_INTEGER ), // int/float union
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioikchain_t )
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_FIELD( linktype, FIELD_INTEGER ),
DEFINE_FIELD( numlinks, FIELD_INTEGER ),
DEFINE_INDEX( linkindex, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioiklink_t )
DEFINE_FIELD( bone, FIELD_INTEGER ),
DEFINE_FIELD( kneeDir, FIELD_VECTOR ),
DEFINE_FIELD( unused0, FIELD_VECTOR ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiomouth_t )
DEFINE_FIELD( bone, FIELD_INTEGER ),
DEFINE_FIELD( forward, FIELD_VECTOR ),
DEFINE_FIELD( flexdesc, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioposeparamdesc_t )
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_FIELD( flags, FIELD_INTEGER ),
DEFINE_FIELD( start, FIELD_FLOAT ),
DEFINE_FIELD( end, FIELD_FLOAT ),
DEFINE_FIELD( loop, FIELD_FLOAT ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiomesh_t )
DEFINE_FIELD( material, FIELD_INTEGER ),
DEFINE_INDEX( modelindex, FIELD_INTEGER ),
DEFINE_FIELD( numvertices, FIELD_INTEGER ),
DEFINE_FIELD( vertexoffset, FIELD_INTEGER ),
DEFINE_FIELD( numflexes, FIELD_INTEGER ),
DEFINE_INDEX( flexindex, FIELD_INTEGER ),
DEFINE_FIELD( materialtype, FIELD_INTEGER ),
DEFINE_FIELD( materialparam, FIELD_INTEGER ),
DEFINE_FIELD( meshid, FIELD_INTEGER ),
DEFINE_FIELD( center, FIELD_VECTOR ),
DEFINE_EMBEDDED( vertexdata ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudio_meshvertexdata_t )
DEFINE_FIELD( modelvertexdata, FIELD_INTEGER ), // mstudio_modelvertexdata_t*
DEFINE_ARRAY( numLODVertexes, FIELD_INTEGER, MAX_NUM_LODS ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioeyeball_t )
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_FIELD( bone, FIELD_INTEGER ),
DEFINE_FIELD( org, FIELD_VECTOR ),
DEFINE_FIELD( zoffset, FIELD_FLOAT ),
DEFINE_FIELD( radius, FIELD_FLOAT ),
DEFINE_FIELD( up, FIELD_VECTOR ),
DEFINE_FIELD( forward, FIELD_VECTOR ),
DEFINE_FIELD( texture, FIELD_INTEGER ),
DEFINE_FIELD( unused1, FIELD_INTEGER ),
DEFINE_FIELD( iris_scale, FIELD_FLOAT ),
DEFINE_FIELD( unused2, FIELD_INTEGER ),
DEFINE_ARRAY( upperflexdesc, FIELD_INTEGER, 3 ),
DEFINE_ARRAY( lowerflexdesc, FIELD_INTEGER, 3 ),
DEFINE_ARRAY( uppertarget, FIELD_FLOAT, 3 ),
DEFINE_ARRAY( lowertarget, FIELD_FLOAT, 3 ),
DEFINE_FIELD( upperlidflexdesc, FIELD_INTEGER ),
DEFINE_FIELD( lowerlidflexdesc, FIELD_INTEGER ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 4 ),
DEFINE_FIELD( m_bNonFACS, FIELD_BOOLEAN ),
DEFINE_ARRAY( unused3, FIELD_CHARACTER, 3 ),
DEFINE_ARRAY( unused4, FIELD_INTEGER, 7 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioflex_t )
DEFINE_FIELD( flexdesc, FIELD_INTEGER ),
DEFINE_FIELD( target0, FIELD_FLOAT ),
DEFINE_FIELD( target1, FIELD_FLOAT ),
DEFINE_FIELD( target2, FIELD_FLOAT ),
DEFINE_FIELD( target3, FIELD_FLOAT ),
DEFINE_FIELD( numverts, FIELD_INTEGER ),
DEFINE_INDEX( vertindex, FIELD_INTEGER ),
DEFINE_FIELD( flexpair, FIELD_INTEGER ),
DEFINE_FIELD( vertanimtype, FIELD_CHARACTER ),
DEFINE_ARRAY( unusedchar, FIELD_CHARACTER, 3 ),
DEFINE_ARRAY( unused, FIELD_INTEGER, 6 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiovertanim_t )
DEFINE_FIELD( index, FIELD_SHORT ),
DEFINE_FIELD( speed, FIELD_CHARACTER ),
DEFINE_FIELD( side, FIELD_CHARACTER ),
DEFINE_ARRAY( delta, FIELD_SHORT, 3 ), // short[3]/float16[3]union
DEFINE_ARRAY( ndelta, FIELD_SHORT, 3 ), // short[3]/float16[3] union
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiomodelgroup_t )
DEFINE_INDEX( szlabelindex, FIELD_INTEGER ),
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioanimblock_t )
DEFINE_INDEX( datastart, FIELD_INTEGER ),
DEFINE_INDEX( dataend, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiotexture_t )
DEFINE_INDEX( sznameindex, FIELD_INTEGER ),
DEFINE_FIELD( flags, FIELD_INTEGER ),
DEFINE_FIELD( used, FIELD_INTEGER ),
DEFINE_FIELD( unused1, FIELD_INTEGER ),
DEFINE_FIELD( material, FIELD_INTEGER ), // IMaterial*
DEFINE_FIELD( clientmaterial, FIELD_INTEGER ), // void*
DEFINE_ARRAY( unused, FIELD_INTEGER, 10 ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( vertexFileHeader_t )
DEFINE_FIELD( id, FIELD_INTEGER ),
DEFINE_FIELD( version, FIELD_INTEGER ),
DEFINE_FIELD( checksum, FIELD_INTEGER ),
DEFINE_FIELD( numLODs, FIELD_INTEGER ),
DEFINE_ARRAY( numLODVertexes, FIELD_INTEGER, MAX_NUM_LODS ),
DEFINE_FIELD( numFixups, FIELD_INTEGER ),
DEFINE_FIELD( fixupTableStart, FIELD_INTEGER ),
DEFINE_FIELD( vertexDataStart, FIELD_INTEGER ),
DEFINE_FIELD( tangentDataStart, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( vertexFileFixup_t )
DEFINE_FIELD( lod, FIELD_INTEGER ),
DEFINE_FIELD( sourceVertexID, FIELD_INTEGER ),
DEFINE_FIELD( numVertexes, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudioboneweight_t )
DEFINE_ARRAY( weight, FIELD_FLOAT, MAX_NUM_BONES_PER_VERT ),
DEFINE_ARRAY( bone, FIELD_CHARACTER, MAX_NUM_BONES_PER_VERT ),
DEFINE_FIELD( numbones, FIELD_CHARACTER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( mstudiovertex_t )
DEFINE_EMBEDDED( m_BoneWeights ),
DEFINE_FIELD( m_vecPosition, FIELD_VECTOR ),
DEFINE_FIELD( m_vecNormal, FIELD_VECTOR ),
DEFINE_ARRAY( m_vecTexCoord, FIELD_FLOAT, 2 ),
END_BYTESWAP_DATADESC()
// Data descriptions from OptimizedModel.h
namespace OptimizedModel
{
BEGIN_BYTESWAP_DATADESC( BoneStateChangeHeader_t )
DEFINE_FIELD( hardwareID, FIELD_INTEGER ),
DEFINE_FIELD( newBoneID, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( Vertex_t )
DEFINE_ARRAY( boneWeightIndex, FIELD_CHARACTER, MAX_NUM_BONES_PER_VERT ),
DEFINE_FIELD( numBones, FIELD_CHARACTER ),
DEFINE_FIELD( origMeshVertID, FIELD_SHORT ),
DEFINE_ARRAY( boneID, FIELD_CHARACTER, MAX_NUM_BONES_PER_VERT ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( StripHeader_t )
DEFINE_FIELD( numIndices, FIELD_INTEGER ),
DEFINE_FIELD( indexOffset, FIELD_INTEGER ),
DEFINE_FIELD( numVerts, FIELD_INTEGER ),
DEFINE_FIELD( vertOffset, FIELD_INTEGER ),
DEFINE_FIELD( numBones, FIELD_SHORT ),
DEFINE_FIELD( flags, FIELD_CHARACTER ),
DEFINE_FIELD( numBoneStateChanges, FIELD_INTEGER ),
DEFINE_FIELD( boneStateChangeOffset, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( StripGroupHeader_t )
DEFINE_FIELD( numVerts, FIELD_INTEGER ),
DEFINE_FIELD( vertOffset, FIELD_INTEGER ),
DEFINE_FIELD( numIndices, FIELD_INTEGER ),
DEFINE_FIELD( indexOffset, FIELD_INTEGER ),
DEFINE_FIELD( numStrips, FIELD_INTEGER ),
DEFINE_FIELD( stripOffset, FIELD_INTEGER ),
DEFINE_FIELD( flags, FIELD_CHARACTER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( MeshHeader_t )
DEFINE_FIELD( numStripGroups, FIELD_INTEGER ),
DEFINE_FIELD( stripGroupHeaderOffset, FIELD_INTEGER ),
DEFINE_FIELD( flags, FIELD_CHARACTER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( ModelLODHeader_t )
DEFINE_FIELD( numMeshes, FIELD_INTEGER ),
DEFINE_FIELD( meshOffset, FIELD_INTEGER ),
DEFINE_FIELD( switchPoint, FIELD_FLOAT ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( ModelHeader_t )
DEFINE_FIELD( numLODs, FIELD_INTEGER ),
DEFINE_FIELD( lodOffset, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( BodyPartHeader_t )
DEFINE_FIELD( numModels, FIELD_INTEGER ),
DEFINE_FIELD( modelOffset, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( MaterialReplacementHeader_t )
DEFINE_FIELD( materialID, FIELD_SHORT ),
DEFINE_FIELD( replacementMaterialNameOffset, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( MaterialReplacementListHeader_t )
DEFINE_FIELD( numReplacements, FIELD_INTEGER ),
DEFINE_FIELD( replacementOffset, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
BEGIN_BYTESWAP_DATADESC( FileHeader_t )
DEFINE_FIELD( version, FIELD_INTEGER ),
DEFINE_FIELD( vertCacheSize, FIELD_INTEGER ),
DEFINE_FIELD( maxBonesPerStrip, FIELD_SHORT ),
DEFINE_FIELD( maxBonesPerTri, FIELD_SHORT ),
DEFINE_FIELD( maxBonesPerVert, FIELD_INTEGER ),
DEFINE_FIELD( checkSum, FIELD_INTEGER ),
DEFINE_FIELD( numLODs, FIELD_INTEGER ),
DEFINE_FIELD( materialReplacementListOffset, FIELD_INTEGER ),
DEFINE_FIELD( numBodyParts, FIELD_INTEGER ),
DEFINE_FIELD( bodyPartOffset, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
} // namespace OptimizedModel
// Data descriptions from phyfile.h
BEGIN_BYTESWAP_DATADESC( phyheader_t )
DEFINE_FIELD( size, FIELD_INTEGER ),
DEFINE_FIELD( id, FIELD_INTEGER ),
DEFINE_FIELD( solidCount, FIELD_INTEGER ),
DEFINE_FIELD( checkSum, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()