//===== Copyright (c) 1996-2008, Valve Corporation, All rights reserved. ======//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//

#ifndef STUDIO_PS3_H
#define STUDIO_PS3_H

#include "basetypes.h"

//#include "mathlib/vector2d.h"
#include "mathlib/vector.h"
#include "mathlib/vector4d.h"
#include "mathlib/compressed_vector.h"
// #include "tier0/dbg.h"
// #include "tier0/threadtools.h"
#include "mathlib/mathlib.h"

//#include "utlvector.h"
// #include "utlhash.h"
// #include "datamap.h"
// #include "generichash.h"
// #include "localflexcontroller.h"
// #include "utlsymbol.h"
// #include "convar.h"


// duplicated defined from studio.h for spu
#if defined(__SPU__)

#define STUDIO_LOCAL	0x0200		// sequence has a local context sequence

#define STUDIO_LOOPING	0x0001		// ending frame should be the same as the starting frame


#define BONE_FIXED_ALIGNMENT		0x00100000	// bone can't spin 360 degrees, all interpolation is normalized around a fixed orientation

#define BONE_HAS_SAVEFRAME_POS		0x00200000	// Vector48
#define BONE_HAS_SAVEFRAME_ROT64	0x00400000	// Quaternion64
#define BONE_HAS_SAVEFRAME_ROT32	0x00800000	// Quaternion32

#define STUDIO_FRAME_CONST_POS	0x01 // Vector48 in constants
#define STUDIO_FRAME_CONST_ROT	0x02 // Quaternion48 in constants
#define STUDIO_FRAME_ANIM_POS	0x04 // Vector48 in framedata
#define STUDIO_FRAME_ANIM_ROT	0x08 // Quaternion48 in framedata
#define STUDIO_FRAME_ANIM_POS2	0x10 // Vector in framedata
#define STUDIO_FRAME_CONST_POS2	0x20 // Vector in constants
#define STUDIO_FRAME_CONST_ROT2	0x40 // Quaternion48S in constants
#define STUDIO_FRAME_ANIM_ROT2	0x80 // Quaternion48S in framedata

#define STUDIO_DELTA	0x0004		// this sequence "adds" to the base sequences, not slerp blends
#define STUDIO_FRAMEANIM 0x0040		// animation is encoded as by frame x bone instead of RLE bone x frame

#define STUDIO_POST		0x0010		// 
#define STUDIO_WORLD	0x4000		// sequence blends in worldspace

#endif



// #define STUDIO_ENABLE_PERF_COUNTERS

// #define STUDIO_SEQUENCE_ACTIVITY_LOOKUPS_ARE_SLOW 0 
//-----------------------------------------------------------------------------
// forward declarations
//-----------------------------------------------------------------------------

// class IMaterial;
// class IMesh;
// class IMorph;
// struct virtualmodel_t;
// struct vertexFileHeader_t;
// struct thinModelVertices_t;
// 
// namespace OptimizedModel
// {
// 	struct StripHeader_t;
// }


class CThreadFastMutex_PS3  // here for size only
{
	uint32 m_ownerID;
	int	   m_depth;
};


/*
==============================================================================

STUDIO MODELS

Studio models are position independent, so the cache manager can move them.
==============================================================================
*/

// #define STUDIO_VERSION		49

struct studiohdr_t_PS3;

// #ifdef _GAMECONSOLE
// #define MAXSTUDIOTRIANGLES	65536	// 
// #define MAXSTUDIOVERTS		32768	// These numbers save memory in CCachedRenderData, but restrict usable model sizes on 360
// #define	MAXSTUDIOFLEXVERTS	4096	// 
// #else
// #define MAXSTUDIOTRIANGLES	65536	// TODO: tune this
// #define MAXSTUDIOVERTS		65536	// TODO: tune this
// #define	MAXSTUDIOFLEXVERTS	10000	// max number of verts that can be flexed per mesh.  TODO: tune this
// #endif
// #define MAXSTUDIOSKINS		32		// total textures
#define MAXSTUDIOBONES_PS3		128 // 256		// total bones actually used
// #define MAXSTUDIOFLEXDESC	1024	// maximum number of low level flexes (actual morph targets)
// #define MAXSTUDIOFLEXCTRL	96		// maximum number of flexcontrollers (input sliders)
#define MAXSTUDIOPOSEPARAM_PS3	24
// #define MAXSTUDIOBONECTRLS	4
// #define MAXSTUDIOANIMBLOCKS 256

#define MAXSTUDIOBONEBITS_PS3	7 //8		// NOTE: MUST MATCH MAXSTUDIOBONES

// NOTE!!! : Changing this number also changes the vtx file format!!!!!
// #define MAX_NUM_BONES_PER_VERT 3

//Adrian - Remove this when we completely phase out the old event system.
// #define NEW_EVENT_STYLE ( 1 << 10 )

// struct mstudiodata_t
// {
// 	int		count;
// 	int		offset;
// };

// #define STUDIO_PROC_AXISINTERP	1
// #define STUDIO_PROC_QUATINTERP	2
// #define STUDIO_PROC_AIMATBONE	3
// #define STUDIO_PROC_AIMATATTACH 4
// #define STUDIO_PROC_JIGGLE		5
// #define STUDIO_PROC_TWIST_MASTER 6
// #define STUDIO_PROC_TWIST_SLAVE 7	// Multiple twist bones are computed at once for the same parent/child combo so TWIST_NULL do nothing
// #define STUDIO_PROC_POINT_CONSTRAINT	8
// #define STUDIO_PROC_ORIENT_CONSTRAINT	9
// #define STUDIO_PROC_AIM_CONSTRAINT		10
// #define STUDIO_PROC_IK_CONSTRAINT		11
// #define STUDIO_PROC_PARENT_CONSTRAINT	12

// struct mstudioaxisinterpbone_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int				control;// local transformation of this bone used to calc 3 point blend
// 	int				axis;	// axis to check
// 	Vector			pos[6];	// X+, X-, Y+, Y-, Z+, Z-
// 	Quaternion		quat[6];// X+, X-, Y+, Y-, Z+, Z-
// 
// 	mstudioaxisinterpbone_t(){}
// private:
// 	// No copy constructors allowed
// 	mstudioaxisinterpbone_t(const mstudioaxisinterpbone_t& vOther);
// };


// struct mstudioquatinterpinfo_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	float			inv_tolerance;	// 1 / radian angle of trigger influence
// 	Quaternion		trigger;	// angle to match
// 	Vector			pos;		// new position
// 	Quaternion		quat;		// new angle
// 
// 	mstudioquatinterpinfo_t(){}
// private:
// 	// No copy constructors allowed
// 	mstudioquatinterpinfo_t(const mstudioquatinterpinfo_t& vOther);
// };

// struct mstudioquatinterpbone_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int				control;// local transformation to check
// 	int				numtriggers;
// 	int				triggerindex;
// 	inline mstudioquatinterpinfo_t *pTrigger( int i ) const { return  (mstudioquatinterpinfo_t *)(((byte *)this) + triggerindex) + i; };
// 
// 	mstudioquatinterpbone_t(){}
// private:
// 	// No copy constructors allowed
// 	mstudioquatinterpbone_t(const mstudioquatinterpbone_t& vOther);
// };


// #define JIGGLE_IS_FLEXIBLE				0x01
// #define JIGGLE_IS_RIGID					0x02
// #define JIGGLE_HAS_YAW_CONSTRAINT		0x04
// #define JIGGLE_HAS_PITCH_CONSTRAINT		0x08
// #define JIGGLE_HAS_ANGLE_CONSTRAINT		0x10
// #define JIGGLE_HAS_LENGTH_CONSTRAINT	0x20
// #define JIGGLE_HAS_BASE_SPRING			0x40
// 
// struct mstudiojigglebone_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	int				flags;
// 
// 	// general params
// 	float			length;					// how from from bone base, along bone, is tip
// 	float			tipMass;
// 
// 	// flexible params
// 	float			yawStiffness;
// 	float			yawDamping;	
// 	float			pitchStiffness;
// 	float			pitchDamping;	
// 	float			alongStiffness;
// 	float			alongDamping;	
// 
// 	// angle constraint
// 	float			angleLimit;				// maximum deflection of tip in radians
// 	
// 	// yaw constraint
// 	float			minYaw;					// in radians
// 	float			maxYaw;					// in radians
// 	float			yawFriction;
// 	float			yawBounce;
// 	
// 	// pitch constraint
// 	float			minPitch;				// in radians
// 	float			maxPitch;				// in radians
// 	float			pitchFriction;
// 	float			pitchBounce;
// 
// 	// base spring
// 	float			baseMass;
// 	float			baseStiffness;
// 	float			baseDamping;	
// 	float			baseMinLeft;
// 	float			baseMaxLeft;
// 	float			baseLeftFriction;
// 	float			baseMinUp;
// 	float			baseMaxUp;
// 	float			baseUpFriction;
// 	float			baseMinForward;
// 	float			baseMaxForward;
// 	float			baseForwardFriction;
// 
// private:
// 	// No copy constructors allowed
// 	//mstudiojigglebone_t(const mstudiojigglebone_t& vOther);
// };

// struct mstudioaimatbone_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	int				parent;
// 	int				aim;		// Might be bone or attach
// 	Vector			aimvector;
// 	Vector			upvector;
// 	Vector			basepos;
// 
// 	mstudioaimatbone_t() {}
// private:
// 	// No copy constructors allowed
// 	mstudioaimatbone_t(const mstudioaimatbone_t& vOther);
// };


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
// struct mstudiotwistbonetarget_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	int				m_nBone;
// 	float			m_flWeight;
// 	Vector			m_vBaseTranslate;
// 	Quaternion		m_qBaseRotation;
// 
// 	mstudiotwistbonetarget_t() {}
// private:
// 	// No copy constructors allowed
// 	mstudiotwistbonetarget_t( const mstudiotwistbonetarget_t &vOther );
// };


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
// struct mstudiotwistbone_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	bool			m_bInverse;				// False: Apply child rotation to twist targets True: Apply parent rotation to twist targets
// 	Vector			m_vUpVector;			// In parent space, projected into plane defined by vector between parent & child
// 	int				m_nParentBone;
// 	Quaternion		m_qBaseInv;	// The base rotation of the parent, used if m_bInverse is true
// 	int				m_nChildBone;
// 
// 	int				m_nTargetCount;
// 	int				m_nTargetIndex;
// 	inline mstudiotwistbonetarget_t *pTarget( int i ) const { return ( mstudiotwistbonetarget_t * )( ( ( byte * )this) + m_nTargetIndex ) + i; }
// 
// 	mstudiotwistbone_t() {}
// private:
// 	// No copy constructors allowed
// 	mstudiotwistbone_t( const mstudiotwistbone_t &vOther );
// };


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
// struct mstudioconstraintslave_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	int				m_nBone;
// 	Vector			m_vBasePosition;
// 	Quaternion		m_qBaseOrientation;
// 
// 	mstudioconstraintslave_t() {}
// private:
// 	// No copy constructors allowed
// 	mstudioconstraintslave_t( const mstudioconstraintslave_t &vOther );
// };


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
// struct mstudioconstrainttarget_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	int				m_nBone;
// 	float			m_flWeight;
// 	Vector			m_vOffset;
// 	Quaternion		m_qOffset;
// 
// 	mstudioconstrainttarget_t() {}
// private:
// 	// No copy constructors allowed
// 	mstudioconstrainttarget_t( const mstudioconstrainttarget_t &vOther );
// };


//-----------------------------------------------------------------------------
// Point constraint, slave position matches target position
//-----------------------------------------------------------------------------
// struct mstudiopointconstraint_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	mstudioconstraintslave_t m_slave;	// DEFINE_EMBEDDED
// 
// 	int				m_nTargetCount;
// 	int				m_nTargetIndex;
// 	inline mstudioconstrainttarget_t *pTarget( int i ) const { return ( mstudioconstrainttarget_t * )( ( ( byte * )this) + m_nTargetIndex ) + i; }
// 
// 	mstudiopointconstraint_t() {}
// private:
// 	// No copy constructors allowed
// 	mstudiopointconstraint_t( const mstudiopointconstraint_t &vOther );
// };


//-----------------------------------------------------------------------------
// Orient constraint, slave orientation matches target orientation
//-----------------------------------------------------------------------------
// struct mstudioorientconstraint_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	mstudioconstraintslave_t m_slave;	// DEFINE_EMBEDDED
// 
// 	int				m_nTargetCount;
// 	int				m_nTargetIndex;
// 	inline mstudioconstrainttarget_t *pTarget( int i ) const { return ( mstudioconstrainttarget_t * )( ( ( byte * )this) + m_nTargetIndex ) + i; }
// 
// 	mstudioorientconstraint_t() {}
// private:
// 	// No copy constructors allowed
// 	mstudioorientconstraint_t( const mstudioorientconstraint_t &vOther );
// };


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
// struct mstudioaimconstraint_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	mstudioconstraintslave_t m_slave;	// DEFINE_EMBEDDED
// 
// 	int				m_nTargetCount;
// 	int				m_nTargetIndex;
// 	inline mstudioconstrainttarget_t *pTarget( int i ) const { return ( mstudioconstrainttarget_t * )( ( ( byte * )this) + m_nTargetIndex ) + i; }
// 
// 	Quaternion		m_qAimOffset;
// 	Vector			m_vUp;
// 	int				m_nUpSpaceTarget;
// 
// 	mstudioaimconstraint_t() {}
// private:
// 	// No copy constructors allowed
// 	mstudioaimconstraint_t( const mstudioaimconstraint_t &vOther );
// };


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
// struct mstudioikconstraint_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	mstudioikconstraint_t() {}
// private:
// 	// No copy constructors allowed
// 	mstudioikconstraint_t( const mstudioaimconstraint_t &vOther );
// };


//-----------------------------------------------------------------------------
// Parent constraint, slave position and orientation are updated to behave as children of the target
//-----------------------------------------------------------------------------
// struct mstudioparentconstraint_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	mstudioconstraintslave_t m_slave;	// DEFINE_EMBEDDED
// 
// 	int				m_nTargetCount;
// 	int				m_nTargetIndex;
// 	inline mstudioconstrainttarget_t *pTarget( int i ) const { return ( mstudioconstrainttarget_t * )( ( ( byte * )this) + m_nTargetIndex ) + i; }
// 
// 	mstudioparentconstraint_t() {}
// private:
// 	// No copy constructors allowed
// 	mstudioparentconstraint_t( const mstudioparentconstraint_t &vOther );
// };


// bones
struct mstudiobone_t_PS3
{
// 	DECLARE_BYTESWAP_DATADESC();
	int					sznameindex;
// 	inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
	int		 			parent;		// parent bone
	int					bonecontroller[6];	// bone controller index, -1 == none

	// default values
	Vector				pos;
	Quaternion			quat;
	RadianEuler			rot;
	// compression scale
	Vector				posscale;
	Vector				rotscale;

	matrix3x4_t			poseToBone;
	Quaternion			qAlignment;
	int					flags;
	int					proctype;
	int					procindex;		// procedural rule
	mutable int			physicsbone;	// index into physically simulated bone
// 	inline void *pProcedure( ) const { if (procindex == 0) return NULL; else return  (void *)(((byte *)this) + procindex); };
	int					surfacepropidx;	// index into string tablefor property name
// 	inline char * const pszSurfaceProp( void ) const { return ((char *)this) + surfacepropidx; }
// 	inline int			GetSurfaceProp( void ) const { return surfacepropLookup; }

	int					contents;		// See BSPFlags.h for the contents flags
	int					surfacepropLookup;	// this index must be cached by the loader, not saved in the file
	int					unused[7];		// remove as appropriate

	mstudiobone_t_PS3(){}
private:
	// No copy constructors allowed
	mstudiobone_t_PS3(const mstudiobone_t_PS3& vOther);
};

// middle chunk from mstudiobone_t_PS3
struct mstudiobone_t_PS3_postoqalignment
{
	Vector				pos;
	Quaternion			quat;
	RadianEuler			rot;
	Vector				posscale;
	Vector				rotscale;
	matrix3x4_t			poseToBone;
	Quaternion			qAlignment;
	int					flags;
};



struct mstudiolinearbone_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();

	int numbones;

	int flagsindex;
// 	inline int flags( int i ) const { Assert( i >= 0 && i < numbones); return *((int *)(((byte *)this) + flagsindex) + i); };
// 	inline int *pflags( int i ) { Assert( i >= 0 && i < numbones); return ((int *)(((byte *)this) + flagsindex) + i); };

	int	parentindex;
// 	inline int parent( int i ) const { Assert( i >= 0 && i < numbones); return *((int *)(((byte *)this) + parentindex) + i); };

	int	posindex;
//	inline const Vector &pos( byte *pEA_this, int i ) const { Assert( i >= 0 && i < numbones); return (Vector *)((pEA_this) + posindex) + i; };

	int quatindex;
//	inline const Quaternion &quat( int i ) const { Assert( i >= 0 && i < numbones); return *((Quaternion *)((pEA_this) + quatindex) + i); };

	int rotindex;
//	inline const RadianEuler &rot( int i ) const { Assert( i >= 0 && i < numbones); return *((RadianEuler *)(((byte *)this) + rotindex) + i); };

	int posetoboneindex;
//	inline const matrix3x4_t &poseToBone( int i ) const { Assert( i >= 0 && i < numbones); return *((matrix3x4_t *)(((byte *)this) + posetoboneindex) + i); };

	int	posscaleindex;
//	inline const Vector &posscale( int i ) const { Assert( i >= 0 && i < numbones); return *((Vector *)(((byte *)this) + posscaleindex) + i); };

	int	rotscaleindex;
//	inline const Vector &rotscale( int i ) const { Assert( i >= 0 && i < numbones); return *((Vector *)(((byte *)this) + rotscaleindex) + i); };

	int	qalignmentindex;
//	inline const Quaternion &qalignment( int i ) const { Assert( i >= 0 && i < numbones); return *((Quaternion *)(((byte *)this) + qalignmentindex) + i); };

	int unused[6];

	mstudiolinearbone_t_PS3(){}
private:
	// No copy constructors allowed
	mstudiolinearbone_t_PS3(const mstudiolinearbone_t_PS3& vOther);
};


//-----------------------------------------------------------------------------
// The component of the bone used by mstudioboneflexdriver_t
//-----------------------------------------------------------------------------
// enum StudioBoneFlexComponent_t
// {
// 	STUDIO_BONE_FLEX_INVALID = -1,	// Invalid
// 	STUDIO_BONE_FLEX_TX = 0,		// Translate X
// 	STUDIO_BONE_FLEX_TY = 1,		// Translate Y
// 	STUDIO_BONE_FLEX_TZ = 2			// Translate Z
// };


//-----------------------------------------------------------------------------
// Component is one of Translate X, Y or Z [0,2] (StudioBoneFlexComponent_t)
//-----------------------------------------------------------------------------
// struct mstudioboneflexdrivercontrol_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	int m_nBoneComponent;		// Bone component that drives flex, StudioBoneFlexComponent_t
// 	int m_nFlexControllerIndex;	// Flex controller to drive
// 	float m_flMin;				// Min value of bone component mapped to 0 on flex controller
// 	float m_flMax;				// Max value of bone component mapped to 1 on flex controller
// 
// 	mstudioboneflexdrivercontrol_t(){}
// private:
// 	// No copy constructors allowed
// 	mstudioboneflexdrivercontrol_t( const mstudioboneflexdrivercontrol_t &vOther );
// };


//-----------------------------------------------------------------------------
// Drive flex controllers from bone components
//-----------------------------------------------------------------------------
// struct mstudioboneflexdriver_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	int m_nBoneIndex;			// Bone to drive flex controller
// 	int m_nControlCount;		// Number of flex controllers being driven
// 	int m_nControlIndex;		// Index into data where controllers are (relative to this)
// 
// 	inline mstudioboneflexdrivercontrol_t *pBoneFlexDriverControl( int i ) const
// 	{
// 		Assert( i >= 0 && i < m_nControlCount );
// 		return (mstudioboneflexdrivercontrol_t *)(((byte *)this) + m_nControlIndex) + i;
// 	}
// 
// 	int unused[3];
// 
// 	mstudioboneflexdriver_t(){}
// private:
// 	// No copy constructors allowed
// 	mstudioboneflexdriver_t( const mstudioboneflexdriver_t &vOther );
// };


// #define BONE_CALCULATE_MASK			0x1F
// #define BONE_PHYSICALLY_SIMULATED	0x01	// bone is physically simulated when physics are active
// #define BONE_PHYSICS_PROCEDURAL		0x02	// procedural when physics is active
// #define BONE_ALWAYS_PROCEDURAL		0x04	// bone is always procedurally animated
// #define BONE_SCREEN_ALIGN_SPHERE	0x08	// bone aligns to the screen, not constrained in motion.
// #define BONE_SCREEN_ALIGN_CYLINDER	0x10	// bone aligns to the screen, constrained by it's own axis.
// 
// #define BONE_USED_MASK				0x0007FF00
// #define BONE_USED_BY_ANYTHING		0x0007FF00
// #define BONE_USED_BY_HITBOX			0x00000100	// bone (or child) is used by a hit box
// #define BONE_USED_BY_ATTACHMENT		0x00000200	// bone (or child) is used by an attachment point
// #define BONE_USED_BY_VERTEX_MASK	0x0003FC00
// #define BONE_USED_BY_VERTEX_LOD0	0x00000400	// bone (or child) is used by the toplevel model via skinned vertex
// #define BONE_USED_BY_VERTEX_LOD1	0x00000800	
// #define BONE_USED_BY_VERTEX_LOD2	0x00001000  
// #define BONE_USED_BY_VERTEX_LOD3	0x00002000
// #define BONE_USED_BY_VERTEX_LOD4	0x00004000
// #define BONE_USED_BY_VERTEX_LOD5	0x00008000
// #define BONE_USED_BY_VERTEX_LOD6	0x00010000
// #define BONE_USED_BY_VERTEX_LOD7	0x00020000
// #define BONE_USED_BY_BONE_MERGE		0x00040000	// bone is available for bone merge to occur against it
// 
// #define BONE_USED_BY_VERTEX_AT_LOD(lod) ( BONE_USED_BY_VERTEX_LOD0 << (lod) )
// #define BONE_USED_BY_ANYTHING_AT_LOD(lod) ( ( BONE_USED_BY_ANYTHING & ~BONE_USED_BY_VERTEX_MASK ) | BONE_USED_BY_VERTEX_AT_LOD(lod) )
// 
// #define MAX_NUM_LODS 8
// 
// #define BONE_TYPE_MASK				0x00F00000
// #define BONE_FIXED_ALIGNMENT		0x00100000	// bone can't spin 360 degrees, all interpolation is normalized around a fixed orientation
// 
// #define BONE_HAS_SAVEFRAME_POS		0x00200000	// Vector48
// #define BONE_HAS_SAVEFRAME_ROT64	0x00400000	// Quaternion64
// #define BONE_HAS_SAVEFRAME_ROT32	0x00800000	// Quaternion32

// bone controllers
struct mstudiobonecontroller_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	int					bone;	// -1 == 0
	int					type;	// X, Y, Z, XR, YR, ZR, M
	float				start;
	float				end;
	int					rest;	// byte index value at rest
	int					inputfield;	// 0-3 user set controller, 4 mouth
	int					unused[8];
};

// intersection boxes
struct mstudiobbox_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	int					bone;
	int					group;				// intersection group
	Vector				bbmin;				// bounding box
	Vector				bbmax;	
	int					szhitboxnameindex;	// offset to the name of the hitbox.
	int					unused[8];

// 	const char* pszHitboxName() const
// 	{
// 		if( szhitboxnameindex == 0 )
// 			return "";
// 
// 		return ((const char*)this) + szhitboxnameindex;
// 	}

	mstudiobbox_t_PS3() {}

private:
	// No copy constructors allowed
	mstudiobbox_t_PS3(const mstudiobbox_t_PS3& vOther);
};

// demand loaded sequence groups
// struct mstudiomodelgroup_t_PS3
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					szlabelindex;	// textual name
// 	inline char * const pszLabel( void ) const { return ((char *)this) + szlabelindex; }
// 	int					sznameindex;	// file name
// 	inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
// };

// struct mstudiomodelgrouplookup_t
// {
// 	int					modelgroup;
// 	int					indexwithingroup;
// };

// events
// NOTE: If you modify this struct you MUST also modify mstudioevent_for_client_server_t in npcevent.h!!!
// struct mstudioevent_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	float				cycle;
// 	int					event;
// 	int					type;
// 	inline const char * pszOptions( void ) const { return options; }
// 	char				options[64];
// 
// 	int					szeventindex;
// 	inline char * const pszEventName( void ) const { return ((char *)this) + szeventindex; }
// };

//#define	ATTACHMENT_FLAG_WORLD_ALIGN 0x10000

// attachment
struct mstudioattachment_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	int					sznameindex;
//	inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
	unsigned int		flags;
	int					localbone;
	matrix3x4_t			local; // attachment point
	int					unused[8];
};

#if defined(__SPU__)
#define IK_SELF 1
#define IK_WORLD 2
#define IK_GROUND 3
#define IK_RELEASE 4
#define IK_ATTACHMENT 5
#define IK_UNLATCH 6
#endif

struct mstudioikerror_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	Vector		pos;
	Quaternion	q;

	mstudioikerror_t_PS3() {}

private:
	// No copy constructors allowed
	mstudioikerror_t_PS3(const mstudioikerror_t_PS3& vOther);
};

union mstudioanimvalue_t_PS3;

struct mstudiocompressedikerror_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	float	scale[6];
	short	offset[6];
	inline mstudioanimvalue_t_PS3 *pAnimvalue( void *pEA_this, int i ) const { if (offset[i] > 0) return  (mstudioanimvalue_t_PS3 *)(((byte *)pEA_this) + offset[i]); else return NULL; };

	mstudiocompressedikerror_t_PS3(){}

private:
	// No copy constructors allowed
	mstudiocompressedikerror_t_PS3(const mstudiocompressedikerror_t_PS3& vOther);
};

struct mstudioikrule_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	int			index;

	int			type;
	int			chain;

	int			bone;

	int			slot;	// iktarget slot.  Usually same as chain.
	float		height;
	float		radius;
	float		floor;
	Vector		pos;
	Quaternion	q;

	int			compressedikerrorindex;
//	inline mstudiocompressedikerror_t *pCompressedError() const { return (mstudiocompressedikerror_t *)(((byte *)this) + compressedikerrorindex); };
	inline mstudiocompressedikerror_t_PS3 *pCompressedError( void *pEA_this ) const { return (mstudiocompressedikerror_t_PS3 *)(((byte *)pEA_this) + compressedikerrorindex); };
	int			unused2;

	int			iStart;
	int			ikerrorindex;
//	inline mstudioikerror_t *pError( int i ) const { return  (ikerrorindex) ? (mstudioikerror_t *)(((byte *)this) + ikerrorindex) + (i - iStart) : NULL; };
	inline mstudioikerror_t_PS3 *pError( void *pEA_this, int i ) const { return  (ikerrorindex) ? (mstudioikerror_t_PS3 *)(((byte *)pEA_this) + ikerrorindex) + (i - iStart) : NULL; };

	float		start;	// beginning of influence
	float		peak;	// start of full influence
	float		tail;	// end of full influence
	float		end;	// end of all influence

	float		unused3;	// 
	float		contact;	// frame footstep makes ground concact
	float		drop;		// how far down the foot should drop when reaching for IK
	float		top;		// top of the foot box

	int			unused6;
	int			unused7;
	int			unused8;

	int			szattachmentindex;		// name of world attachment
//	inline char * const pszAttachment( void ) const { return ((char *)this) + szattachmentindex; }

	int			unused[7];
//	int			unused[6];
//	void*		pEA_this;

	mstudioikrule_t_PS3() {}

private:
	// No copy constructors allowed
	mstudioikrule_t_PS3(const mstudioikrule_t_PS3& vOther);
};


struct mstudioikrulezeroframe_t_PS3
{
	short		chain;
	short		slot;
	float16		start;	// beginning of influence
	float16		peak;	// start of full influence
	float16		tail;	// end of full influence
	float16		end;	// end of all influence
};


struct mstudioiklock_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	int			chain;
	float		flPosWeight;
	float		flLocalQWeight;
	int			flags;

	int			unused[4];
};


// struct mstudiolocalhierarchy_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int			iBone;			// bone being adjusted
// 	int			iNewParent;		// the bones new parent
// 
// 	float		start;			// beginning of influence
// 	float		peak;			// start of full influence
// 	float		tail;			// end of full influence
// 	float		end;			// end of all influence
// 
// 	int			iStart;			// first frame 
// 
// 	int			localanimindex;
// 	inline mstudiocompressedikerror_t *pLocalAnim() const { return (mstudiocompressedikerror_t *)(((byte *)this) + localanimindex); };
// 
// 	int			unused[4];
// };

	

// animation frames
union mstudioanimvalue_t_PS3
{
	struct 
	{
		byte	valid;
		byte	total;
	} num;
	short		value;
};

struct mstudioanim_valueptr_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	short	offset[3];
	inline mstudioanimvalue_t_PS3 *pAnimvalue( void *pEA_this, int i ) const { if (offset[i] > 0) return  (mstudioanimvalue_t_PS3 *)(((byte *)pEA_this) + offset[i]); else return NULL; };
};

#if defined(__SPU__)
#define STUDIO_ANIM_RAWPOS	0x01 // Vector48
#define STUDIO_ANIM_RAWROT	0x02 // Quaternion48
#define STUDIO_ANIM_ANIMPOS	0x04 // mstudioanim_valueptr_t
#define STUDIO_ANIM_ANIMROT	0x08 // mstudioanim_valueptr_t
#define STUDIO_ANIM_DELTA	0x10
#define STUDIO_ANIM_RAWROT2	0x20 // Quaternion64
#endif

// per bone per animation DOF and weight pointers, RLE encoded
struct mstudio_rle_anim_t_PS3
{
	//	DECLARE_BYTESWAP_DATADESC();
	byte				bone;
	byte				flags;		// weighing options

	// valid for animating data only
	inline byte						*pData( void *pEA_this ) const { return (((byte *)pEA_this) + sizeof( struct mstudio_rle_anim_t_PS3 )); };
	inline mstudioanim_valueptr_t_PS3	*pRotV( void *pEA_this ) const { return (mstudioanim_valueptr_t_PS3 *)(pData( pEA_this )); };
	inline mstudioanim_valueptr_t_PS3	*pPosV( void *pEA_this ) const { return (mstudioanim_valueptr_t_PS3 *)(pData( pEA_this )) + ((flags & STUDIO_ANIM_ANIMROT) != 0); };

	// valid if animation unvaring over timeline
	inline Quaternion48		*pQuat48( void *pEA_this ) const { return (Quaternion48 *)(pData( pEA_this )); };
	inline Quaternion64		*pQuat64( void *pEA_this ) const { return (Quaternion64 *)(pData( pEA_this )); };
	inline Vector48			*pPos( void *pEA_this ) const { return (Vector48 *)(pData( pEA_this ) + ((flags & STUDIO_ANIM_RAWROT) != 0) * sizeof( *pQuat48( pEA_this ) ) + ((flags & STUDIO_ANIM_RAWROT2) != 0) * sizeof( *pQuat64( pEA_this ) ) ); };
//	inline Vector48			*pPos( void *pEA_this ) const { return (Vector48 *)(pData( pEA_this ) + ((flags & STUDIO_ANIM_RAWROT) != 0) * sizeof( Quaternion48 ) + ((flags & STUDIO_ANIM_RAWROT2) != 0) * sizeof( Quaternion64 ) ); };

	// points to next bone in the list
	short				nextoffset;
	inline mstudio_rle_anim_t_PS3	*pNext( void *pEA_this ) const { if (nextoffset != 0) return  (mstudio_rle_anim_t_PS3 *)(((byte *)pEA_this) + nextoffset); else return NULL; };
};


// #define STUDIO_FRAME_CONST_POS	0x01 // Vector48 in constants
// #define STUDIO_FRAME_CONST_ROT	0x02 // Quaternion48 in constants
// #define STUDIO_FRAME_ANIM_POS	0x04 // Vector48 in framedata
// #define STUDIO_FRAME_ANIM_ROT	0x08 // Quaternion48 in framedata
// #define STUDIO_FRAME_ANIM_POS2	0x10 // Vector in framedata
// #define STUDIO_FRAME_CONST_POS2	0x20 // Vector in constants
// #define STUDIO_FRAME_CONST_ROT2	0x40 // Quaternion48S in constants
// #define STUDIO_FRAME_ANIM_ROT2	0x80 // Quaternion48S in framedata

struct mstudio_frame_anim_t_PS3
{
// 	DECLARE_BYTESWAP_DATADESC();

 	inline byte		*pBoneFlags( void *pEA_this ) const { return (((byte *)pEA_this) + sizeof( struct mstudio_frame_anim_t_PS3 )); };
	
	int				constantsoffset;
 	inline byte		*pConstantData( void *pEA_this ) const { return (((byte *)pEA_this) + constantsoffset); };

	int				frameoffset;
	int 			framelength;
 	inline byte		*pFrameData( void *pEA_this, int iFrame  ) const { return (((byte *)pEA_this) + frameoffset + iFrame * framelength); };

	int				unused[3];
};



// struct mstudiomovement_t_PS3
// {
// // 	DECLARE_BYTESWAP_DATADESC();
// 	int					endframe;				
// 	int					motionflags;
// 	float				v0;			// velocity at start of block
// 	float				v1;			// velocity at end of block
// 	float				angle;		// YAW rotation at end of this blocks movement
// 	Vector				vector;		// movement vector relative to this blocks initial angle
// 	Vector				position;	// relative to start of animation???
// 
// 	mstudiomovement_t_PS3(){}
// private:
// 	// No copy constructors allowed
// 	mstudiomovement_t_PS3(const mstudiomovement_t_PS3& vOther);
// };


// used for piecewise loading of animation data
struct mstudioanimblock_t_PS3
{
// 	DECLARE_BYTESWAP_DATADESC();
	int					datastart;
	int					dataend;
};

struct mstudioanimsections_t_PS3
{
// 	DECLARE_BYTESWAP_DATADESC();
	int					animblock;
	int					animindex;
};

struct mstudioanimdesc_t_PS3
{
// 	DECLARE_BYTESWAP_DATADESC();
	int					baseptr;
//	inline studiohdr_t	*pStudiohdr( void ) const { return (studiohdr_t *)(((byte *)this) + baseptr); }
//	inline studiohdr_t	*pStudiohdr( void *pEA_this ) const { return (studiohdr_t *)(((byte *)pEA_this) + baseptr); }

	int					sznameindex;
// 	inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
 	inline char * const pszName( void *pEA_this ) const { return ((char *)pEA_this) + sznameindex; }

	float				fps;		// frames per second	
	int					flags;		// looping/non-looping flags

	int					numframes;

	// piecewise movement
	int					nummovements;
	int					movementindex;
// 	inline mstudiomovement_t * const pMovement( int i ) const { return (mstudiomovement_t *)(((byte *)this) + movementindex) + i; };
 //	inline mstudiomovement_t * const pMovement( void *pEA_this, int i ) const { return (mstudiomovement_t *)(((byte *)pEA_this) + movementindex) + i; };

	int					ikrulezeroframeindex;
// 	mstudioikrulezeroframe_t *pIKRuleZeroFrame( int i ) const { if (ikrulezeroframeindex) return (mstudioikrulezeroframe_t *)(((byte *)this) + ikrulezeroframeindex) + i; else return NULL; };
// 	mstudioikrulezeroframe_t *pIKRuleZeroFrame( void *pEA_this, int i ) const { if (ikrulezeroframeindex) return (mstudioikrulezeroframe_t *)(((byte *)pEA_this) + ikrulezeroframeindex) + i; else return NULL; };

	int					unused1[5];			// remove as appropriate (and zero if loading older versions)	

	int					animblock;
	int					animindex;	 // non-zero when anim data isn't in sections
// 	byte *pAnimBlock( int block, int index, bool preloadIfMissing = true) const; // returns pointer to a specific anim block (local or external)
// 	bool hasAnimBlockBeenPreloaded( int block ) const;
// 	byte *pAnim( int *piFrame, float &flStall ) const; // returns pointer to data and new frame index
// 	byte *pAnim( int *piFrame ) const; // returns pointer to data and new frame index

	int					numikrules;
	int					ikruleindex;	// non-zero when IK rule is stored in the mdl
	int					animblockikruleindex; // non-zero when IK data is stored in animblock file
// 	mstudioikrule_t *pIKRule( int i ) const;

	int					numlocalhierarchy;
	int					localhierarchyindex;
//	mstudiolocalhierarchy_t *pHierarchy( int i ) const;

	int					sectionindex;
	int					sectionframes; // number of frames used in each fast lookup section, zero if not used
//	inline mstudioanimsections_t * const pSection( int i ) const { return (mstudioanimsections_t *)(((byte *)this) + sectionindex) + i; }
//	inline mstudioanimsections_t * const pSection( void *pEA_this, int i ) const { return (mstudioanimsections_t *)(((byte *)pEA_this) + sectionindex) + i; }

	short				zeroframespan;	// frames per span
	short				zeroframecount; // number of spans
	int					zeroframeindex;
// 	byte				*pZeroFrameData( ) const { if (zeroframeindex) return (((byte *)this) + zeroframeindex); else return NULL; };
 	byte				*pZeroFrameData( void *pEA_this ) const { if (zeroframeindex) return (((byte *)pEA_this) + zeroframeindex); else return NULL; };
	mutable float		zeroframestalltime;		// saved during read stalls

	mstudioanimdesc_t_PS3(){}
private:
	// No copy constructors allowed
	mstudioanimdesc_t_PS3(const mstudioanimdesc_t_PS3& vOther);
};

struct mstudioikrule_t_PS3;

struct mstudioautolayer_t_PS3
{
// 	DECLARE_BYTESWAP_DATADESC();
//private:
	short				iSequence;
	short				iPose;
//public:
	int					flags;
	float				start;	// beginning of influence
	float				peak;	// start of full influence
	float				tail;	// end of full influence
	float				end;	// end of all influence
};

// struct mstudioactivitymodifier_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	
// 	int					sznameindex;
// 	inline char			*pszName() { return (sznameindex) ? (char *)(((byte *)this) + sznameindex ) : NULL; }
// };

// sequence descriptions
struct mstudioseqdesc_t_PS3
{
// 	DECLARE_BYTESWAP_DATADESC();
	int					baseptr;
// 	inline studiohdr_t	*pStudiohdr( void *pEA_this ) const { return (studiohdr_t *)(((byte *)pEA_this) + baseptr); }

	int					szlabelindex;
// 	inline char * const pszLabel( void *pEA_this ) const { return ((char *)pEA_this) + szlabelindex; }

	int					szactivitynameindex;
// 	inline char * const pszActivityName( void ) const { return ((char *)this) + szactivitynameindex; }
// 	inline char * const pszActivityName( void ) const { return pEA_this + szactivitynameindex; }

	int					flags;		// looping/non-looping flags

	int					activity;	// initialized at loadtime to game DLL values
	int					actweight;

	int					numevents;
	int					eventindex;
// 	inline mstudioevent_t *pEvent( int i ) const { Assert( i >= 0 && i < numevents); return (mstudioevent_t *)(((byte *)this) + eventindex) + i; };
// 	inline mstudioevent_t *pEvent( int i ) const { Assert( i >= 0 && i < numevents); return (mstudioevent_t *)(pEA_this + eventindex) + i; };
	
	Vector				bbmin;		// per sequence bounding box
	Vector				bbmax;		

	int					numblends;

	// Index into array of shorts which is groupsize[0] x groupsize[1] in length
	int					animindexindex;

	inline int			anim( void *pEA_this, int x, int y ) const
	{
		if ( x >= groupsize[0] )
		{
			x = groupsize[0] - 1;
		}

		if ( y >= groupsize[1] )
		{
			y = groupsize[ 1 ] - 1;
		}

		int offset = y * groupsize[0] + x;
		short *blends = (short *)(((byte *)pEA_this) + animindexindex);
		int value = (int)blends[ offset ];
		return value;
	}

	int					movementindex;	// [blend] float array for blended movement
	int					groupsize[2];
	int					paramindex[2];	// X, Y, Z, XR, YR, ZR
	float				paramstart[2];	// local (0..1) starting value
	float				paramend[2];	// local (0..1) ending value
	int					paramparent;

	float				fadeintime;		// ideal cross fate in time (0.2 default)
	float				fadeouttime;	// ideal cross fade out time (0.2 default)

	int					localentrynode;		// transition node at entry
	int					localexitnode;		// transition node at exit
	int					nodeflags;		// transition rules

	float				entryphase;		// used to match entry gait
	float				exitphase;		// used to match exit gait
	
	float				lastframe;		// frame that should generation EndOfSequence

	int					nextseq;		// auto advancing sequences
	int					pose;			// index of delta animation between end and nextseq

	int					numikrules;

	int					numautolayers;	//
	int					autolayerindex;
// 	inline mstudioautolayer_t *pAutolayer( int i ) const { Assert( i >= 0 && i < numautolayers); return (mstudioautolayer_t *)(((byte *)this) + autolayerindex) + i; };
// 	inline mstudioautolayer_t *pAutolayer( int i ) const { Assert( i >= 0 && i < numautolayers); return (mstudioautolayer_t *)(pEA_this + autolayerindex) + i; };

	int					weightlistindex;
 	inline float		*pBoneweight( void *pEA_this, int i ) const { return ((float *)(((byte *)pEA_this) + weightlistindex) + i); };
 	inline float		weight( void *pEA_this, int i ) const { return *(pBoneweight( pEA_this, i )); };

	// FIXME: make this 2D instead of 2x1D arrays
	int					posekeyindex;
// 	float				*pPoseKey( int iParam, int iAnim ) const { return (float *)(((byte *)this) + posekeyindex) + iParam * groupsize[0] + iAnim; }
// 	float				poseKey( int iParam, int iAnim ) const { return *(pPoseKey( iParam, iAnim )); }

	int					numiklocks;
	int					iklockindex;
 	inline mstudioiklock_t_PS3 *pIKLock( void *pEA_this, int i ) const { Assert( i >= 0 && i < numiklocks); return (mstudioiklock_t_PS3 *)(((byte *)pEA_this) + iklockindex) + i; };

	// Key values
	int					keyvalueindex;
	int					keyvaluesize;
// 	inline const char * KeyValueText( void ) const { return keyvaluesize != 0 ? ((char *)this) + keyvalueindex : NULL; }

	int					cycleposeindex;		// index of pose parameter to use as cycle index

	int					activitymodifierindex;
	int					numactivitymodifiers;
// 	inline mstudioactivitymodifier_t *pActivityModifier( int i ) const { Assert( i >= 0 && i < numactivitymodifiers); return activitymodifierindex != 0 ? (mstudioactivitymodifier_t *)(((byte *)this) + activitymodifierindex) + i : NULL; };

	int					unused[5];		// remove/add as appropriate (grow back to 8 ints on version change!)

	mstudioseqdesc_t_PS3(){}

private:
	// No copy constructors allowed
	mstudioseqdesc_t_PS3(const mstudioseqdesc_t_PS3& vOther);
};


struct mstudioposeparamdesc_t_PS3
{
// 	DECLARE_BYTESWAP_DATADESC();
	int					sznameindex;
// 	inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
	int					flags;	// ????
	float				start;	// starting value
	float				end;	// ending value
	float				loop;	// looping range, 0 for no looping, 360 for rotations, etc.
};

// struct mstudioflexdesc_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					szFACSindex;
// 	inline char * const pszFACS( void ) const { return ((char *)this) + szFACSindex; }
// };



// struct mstudioflexcontroller_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					sztypeindex;
// 	inline char * const pszType( void ) const { return ((char *)this) + sztypeindex; }
// 	int					sznameindex;
// 	inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
// 	mutable int			localToGlobal;	// remapped at load time to master list
// 	float				min;
// 	float				max;
// };


// enum FlexControllerRemapType_t
// {
// 	FLEXCONTROLLER_REMAP_PASSTHRU = 0,
// 	FLEXCONTROLLER_REMAP_2WAY,	// Control 0 -> ramps from 1-0 from 0->0.5. Control 1 -> ramps from 0-1 from 0.5->1
// 	FLEXCONTROLLER_REMAP_NWAY,	// StepSize = 1 / (control count-1) Control n -> ramps from 0-1-0 from (n-1)*StepSize to n*StepSize to (n+1)*StepSize. A second control is needed to specify amount to use 
// 	FLEXCONTROLLER_REMAP_EYELID
// };


//class CStudioHdr_PS3;

// struct mstudioflexcontrollerui_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					sznameindex;
// 	inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
// 
// 	// These are used like a union to save space
// 	// Here are the possible configurations for a UI controller
// 	//
// 	// SIMPLE NON-STEREO:	0: control	1: unused	2: unused
// 	// STEREO:				0: left		1: right	2: unused
// 	// NWAY NON-STEREO:		0: control	1: unused	2: value
// 	// NWAY STEREO:			0: left		1: right	2: value
// 
// 	int					szindex0;
// 	int					szindex1;
// 	int					szindex2;
// 
// 	inline const mstudioflexcontroller_t *pController( void ) const
// 	{
// 		return !stereo ? (mstudioflexcontroller_t *)( (char *)this + szindex0 ) : NULL;
// 	}
// 	inline char * const	pszControllerName( void ) const { return !stereo ? pController()->pszName() : NULL; }
// 	inline int			controllerIndex( const CStudioHdr &cStudioHdr ) const;
// 
// 	inline const mstudioflexcontroller_t *pLeftController( void ) const
// 	{
// 		return stereo ? (mstudioflexcontroller_t *)( (char *)this + szindex0 ) : NULL;
// 	}
// 	inline char * const	pszLeftName( void ) const { return stereo ? pLeftController()->pszName() : NULL; }
// 	inline int			leftIndex( const CStudioHdr &cStudioHdr ) const;
// 
// 	inline const mstudioflexcontroller_t *pRightController( void ) const
// 	{
// 		return stereo ? (mstudioflexcontroller_t *)( (char *)this + szindex1 ): NULL;
// 	}
// 	inline char * const	pszRightName( void ) const { return stereo ? pRightController()->pszName() : NULL; }
// 	inline int			rightIndex( const CStudioHdr &cStudioHdr ) const;
// 
// 	inline const mstudioflexcontroller_t *pNWayValueController( void ) const
// 	{
// 		return remaptype == FLEXCONTROLLER_REMAP_NWAY ? (mstudioflexcontroller_t *)( (char *)this + szindex2 ) : NULL;
// 	}
// 	inline char * const	pszNWayValueName( void ) const { return remaptype == FLEXCONTROLLER_REMAP_NWAY ? pNWayValueController()->pszName() : NULL; }
// 	inline int			nWayValueIndex( const CStudioHdr &cStudioHdr ) const;
// 
// 	// Number of controllers this ui description contains, 1, 2 or 3
// 	inline int			Count() const { return ( stereo ? 2 : 1 ) + ( remaptype == FLEXCONTROLLER_REMAP_NWAY ? 1 : 0 ); }
// 	inline const mstudioflexcontroller_t *pController( int index ) const;
// 
// 	unsigned char		remaptype;	// See the FlexControllerRemapType_t enum
// 	bool				stereo;		// Is this a stereo control?
// 	byte				unused[2];
// };


// this is the memory image of vertex anims (16-bit fixed point)
// struct mstudiovertanim_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	unsigned short		index;
// 	byte				speed;	// 255/max_length_in_flex
// 	byte				side;	// 255/left_right
// 
// protected:
// 	union
// 	{
// 		short			delta[3];
// 		float16			flDelta[3];
// 	};
// 
// 	union
// 	{
// 		short			ndelta[3];
// 		float16			flNDelta[3];
// 	};
// 
// public:
// 	inline void ConvertToFixed( float flVertAnimFixedPointScale )
// 	{
// 		delta[0] = ( short )( flDelta[0].GetFloat() / flVertAnimFixedPointScale );
// 		delta[1] = ( short )( flDelta[1].GetFloat() / flVertAnimFixedPointScale );
// 		delta[2] = ( short )( flDelta[2].GetFloat() / flVertAnimFixedPointScale );
// 		ndelta[0] = ( short )( flNDelta[0].GetFloat() / flVertAnimFixedPointScale );
// 		ndelta[1] = ( short )( flNDelta[1].GetFloat() / flVertAnimFixedPointScale );
// 		ndelta[2] = ( short )( flNDelta[2].GetFloat() / flVertAnimFixedPointScale );
// 	}
// 
// 	inline Vector GetDeltaFixed( float flVertAnimFixedPointScale )
// 	{
// 		return Vector( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale );
// 	}
// 	inline Vector GetNDeltaFixed( float flVertAnimFixedPointScale )
// 	{
// 		return Vector( ndelta[0] * flVertAnimFixedPointScale, ndelta[1] * flVertAnimFixedPointScale, ndelta[2] * flVertAnimFixedPointScale );
// 	}
// 	inline void GetDeltaFixed4DAligned( Vector4DAligned *vFillIn, float flVertAnimFixedPointScale )
// 	{
// 		vFillIn->Set( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale, 0.0f );
// 	}
// 	inline void GetNDeltaFixed4DAligned( Vector4DAligned *vFillIn, float flVertAnimFixedPointScale )
// 	{
// 		vFillIn->Set( ndelta[0] * flVertAnimFixedPointScale, ndelta[1] * flVertAnimFixedPointScale, ndelta[2] * flVertAnimFixedPointScale, 0.0f );
// 	}
// 	inline Vector GetDeltaFloat()
// 	{
// 		return Vector (flDelta[0].GetFloat(), flDelta[1].GetFloat(), flDelta[2].GetFloat());
// 	}
// 	inline Vector GetNDeltaFloat()
// 	{
// 		return Vector (flNDelta[0].GetFloat(), flNDelta[1].GetFloat(), flNDelta[2].GetFloat());
// 	}
// 	inline void SetDeltaFixed( const Vector& vInput, float flVertAnimFixedPointScale )
// 	{
// 		delta[0] = ( short )( vInput.x / flVertAnimFixedPointScale );
// 		delta[1] = ( short )( vInput.y / flVertAnimFixedPointScale );
// 		delta[2] = ( short )( vInput.z / flVertAnimFixedPointScale );
// 	}
// 	inline void SetNDeltaFixed( const Vector& vInputNormal, float flVertAnimFixedPointScale )
// 	{
// 		ndelta[0] = ( short )( vInputNormal.x / flVertAnimFixedPointScale );
// 		ndelta[1] = ( short )( vInputNormal.y / flVertAnimFixedPointScale );
// 		ndelta[2] = ( short )( vInputNormal.z / flVertAnimFixedPointScale );
// 	}
// 
// 	// Ick...can also force fp16 data into this structure for writing to file in legacy format...
// 	inline void SetDeltaFloat( const Vector& vInput )
// 	{
// 		flDelta[0].SetFloat( vInput.x );
// 		flDelta[1].SetFloat( vInput.y );
// 		flDelta[2].SetFloat( vInput.z );
// 	}
// 	inline void SetNDeltaFloat( const Vector& vInputNormal )
// 	{
// 		flNDelta[0].SetFloat( vInputNormal.x );
// 		flNDelta[1].SetFloat( vInputNormal.y );
// 		flNDelta[2].SetFloat( vInputNormal.z );
// 	}
// 
// 	class CSortByIndex
// 	{
// 	public:
// 		bool operator()(const mstudiovertanim_t &left, const mstudiovertanim_t & right)const
// 		{
// 			return left.index < right.index;
// 		}
// 	};
// 	friend class CSortByIndex;
// 
// 	mstudiovertanim_t(){}
// //private:
// // No copy constructors allowed, but it's needed for std::sort()
// //	mstudiovertanim_t(const mstudiovertanim_t& vOther);
// };


// this is the memory image of vertex anims (16-bit fixed point)
// struct mstudiovertanim_wrinkle_t : public mstudiovertanim_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	short	wrinkledelta;
// 
// 	inline void SetWrinkleFixed( float flWrinkle, float flVertAnimFixedPointScale )
// 	{
// 		int nWrinkleDeltaInt = ( int )( flWrinkle / flVertAnimFixedPointScale );
// 		wrinkledelta = clamp( nWrinkleDeltaInt, -32767, 32767 );
// 	}
// 
// 	inline Vector4D GetDeltaFixed( float flVertAnimFixedPointScale )
// 	{
// 		return Vector4D( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale, wrinkledelta * flVertAnimFixedPointScale );
// 	}
// 
// 	inline void GetDeltaFixed4DAligned( Vector4DAligned *vFillIn, float flVertAnimFixedPointScale )
// 	{
// 		vFillIn->Set( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale, wrinkledelta * flVertAnimFixedPointScale );
// 	}
// 
// 	inline float GetWrinkleDeltaFixed( float flVertAnimFixedPointScale )
// 	{
// 		return wrinkledelta * flVertAnimFixedPointScale;
// 	}
// };


// enum StudioVertAnimType_t
// {
// 	STUDIO_VERT_ANIM_NORMAL = 0,
// 	STUDIO_VERT_ANIM_WRINKLE,
// };


// struct mstudioflex_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					flexdesc;	// input value
// 
// 	float				target0;	// zero
// 	float				target1;	// one
// 	float				target2;	// one
// 	float				target3;	// zero
// 
// 	int					numverts;
// 	int					vertindex;
// 
// 	inline	mstudiovertanim_t *pVertanim( int i ) const { Assert( vertanimtype == STUDIO_VERT_ANIM_NORMAL ); return (mstudiovertanim_t *)(((byte *)this) + vertindex) + i; };
// 	inline	mstudiovertanim_wrinkle_t *pVertanimWrinkle( int i ) const { Assert( vertanimtype == STUDIO_VERT_ANIM_WRINKLE ); return  (mstudiovertanim_wrinkle_t *)(((byte *)this) + vertindex) + i; };
// 
// 	inline	byte *pBaseVertanim( ) const { return ((byte *)this) + vertindex; };
// 	inline	int	VertAnimSizeBytes() const { return ( vertanimtype == STUDIO_VERT_ANIM_NORMAL ) ? sizeof(mstudiovertanim_t) : sizeof(mstudiovertanim_wrinkle_t); }
// 
// 	int					flexpair;	// second flex desc
// 	unsigned char		vertanimtype;	// See StudioVertAnimType_t
// 	unsigned char		unusedchar[3];
// 	int					unused[6];
// 
// };


// struct mstudioflexop_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int		op;
// 	union 
// 	{
// 		int		index;
// 		float	value;
// 	} d;
// };

// struct mstudioflexrule_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					flex;
// 	int					numops;
// 	int					opindex;
// 	inline mstudioflexop_t *iFlexOp( int i ) const { return  (mstudioflexop_t *)(((byte *)this) + opindex) + i; };
// };

// 16 bytes
// struct mstudioboneweight_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	float	weight[MAX_NUM_BONES_PER_VERT];
// 	byte	bone[MAX_NUM_BONES_PER_VERT]; 
// 	byte	numbones;
// 
// //	byte	material;
// //	short	firstref;
// //	short	lastref;
// };

// NOTE: This is exactly 48 bytes
// struct mstudiovertex_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	mstudioboneweight_t	m_BoneWeights;
// 	Vector				m_vecPosition;
// 	Vector				m_vecNormal;
// 	Vector2D			m_vecTexCoord;
// 
// 	mstudiovertex_t() {}
// 
// private:
// 	// No copy constructors allowed
// 	mstudiovertex_t(const mstudiovertex_t& vOther);
// };

// skin info
// struct mstudiotexture_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int						sznameindex;
// 	inline char * const		pszName( void ) const { return ((char *)this) + sznameindex; }
// 	int						flags;
// 	int						used;
//     int						unused1;
// 	mutable IMaterial		*material;  // fixme: this needs to go away . .isn't used by the engine, but is used by studiomdl
// 	mutable void			*clientmaterial;	// gary, replace with client material pointer if used
// 	
// 	int						unused[10];
// };

// eyeball
// struct mstudioeyeball_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					sznameindex;
// 	inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
// 	int		bone;
// 	Vector	org;
// 	float	zoffset;
// 	float	radius;
// 	Vector	up;
// 	Vector	forward;
// 	int		texture;
// 
// 	int		unused1;
// 	float	iris_scale;
// 	int		unused2;
// 
// 	int		upperflexdesc[3];	// index of raiser, neutral, and lowerer flexdesc that is set by flex controllers
// 	int		lowerflexdesc[3];
// 	float	uppertarget[3];		// angle (radians) of raised, neutral, and lowered lid positions
// 	float	lowertarget[3];
// 
// 	int		upperlidflexdesc;	// index of flex desc that actual lid flexes look to
// 	int		lowerlidflexdesc;
// 	int		unused[4];			// These were used before, so not guaranteed to be 0
// 	bool	m_bNonFACS;			// Never used before version 44
// 	char	unused3[3];
// 	int		unused4[7];
// 
// 	mstudioeyeball_t(){}
// private:
// 	// No copy constructors allowed
// 	mstudioeyeball_t(const mstudioeyeball_t& vOther);
// };


// ikinfo
struct mstudioiklink_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	int		bone;
	Vector	kneeDir;	// ideal bending direction (per link, if applicable)
	Vector	unused0;	// unused

	mstudioiklink_t_PS3(){}
private:
	// No copy constructors allowed
	mstudioiklink_t_PS3(const mstudioiklink_t_PS3& vOther);
};

struct mstudioikchain_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	int				sznameindex;
//	inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
	int				linktype;
	int				numlinks;
	int				linkindex;
	inline mstudioiklink_t_PS3 *pLink( void *pEA_this, int i ) const { return (mstudioiklink_t_PS3 *)(((byte *)pEA_this) + linkindex) + i; };
	// FIXME: add unused entries
};

// struct mstudioiface_t
// {
// 	mstudioiface_t()
// 	{
// 		a = b = c = d = 0xFFFF;
// 	}
// 
// 	unsigned short a, b, c, d;		// Indices to vertices (If d is 0xFFFF, this is a triangle, else it's a quad)
// };

struct mstudiomodel_t_PS3;

// struct mstudio_modelvertexdata_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	Vector				*Position( int i ) const;
// 	Vector				*Normal( int i ) const;
// 	Vector4D			*TangentS( int i ) const;
// 	Vector2D			*Texcoord( int i ) const;
// 	mstudioboneweight_t	*BoneWeights( int i ) const;
// 	mstudiovertex_t		*Vertex( int i ) const;
// 	bool				HasTangentData( void ) const;
// 	int					GetGlobalVertexIndex( int i ) const;
// 	int					GetGlobalTangentIndex( int i ) const;
// 
// 	// base of external vertex data stores
// 	const void			*pVertexData;
// 	const void			*pTangentData;
// };

// struct mstudio_meshvertexdata_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	Vector				*Position( int i ) const;
// 	Vector				*Normal( int i ) const;
// 	Vector4D			*TangentS( int i ) const;
// 	Vector2D			*Texcoord( int i ) const;
// 	mstudioboneweight_t *BoneWeights( int i ) const;
// 	mstudiovertex_t		*Vertex( int i ) const;
// 	bool				HasTangentData( void ) const;
// 	int					GetModelVertexIndex( int i ) const;
// 	int					GetGlobalVertexIndex( int i ) const;
// 
// 	// indirection to this mesh's model's vertex data
// 	const mstudio_modelvertexdata_t	*modelvertexdata;
// 
// 	// used for fixup calcs when culling top level lods
// 	// expected number of mesh verts at desired lod
// 	int					numLODVertexes[MAX_NUM_LODS];
// };

// struct mstudiomesh_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					material;
// 
// 	int					modelindex;
// 	mstudiomodel_t *pModel() const; 
// 
// 	int					numvertices;		// number of unique vertices/normals/texcoords
// 	int					vertexoffset;		// vertex mstudiovertex_t
// 
// 	// Access thin/fat mesh vertex data (only one will return a non-NULL result)
// 	const mstudio_meshvertexdata_t	*GetVertexData(		void *pModelData = NULL );
// 	const thinModelVertices_t		*GetThinVertexData(	void *pModelData = NULL );
// 	
// 	int					numflexes;			// vertex animation
// 	int					flexindex;
// 	inline mstudioflex_t *pFlex( int i ) const { return (mstudioflex_t *)(((byte *)this) + flexindex) + i; };
// 
// 	// special codes for material operations
// 	int					materialtype;
// 	int					materialparam;
// 
// 	// a unique ordinal for this mesh
// 	int					meshid;
// 
// 	Vector				center;
// 
// 	mstudio_meshvertexdata_t vertexdata;
// 
// 	int					unused[8]; // remove as appropriate
// 
// 	mstudiomesh_t(){}
// private:
// 	// No copy constructors allowed
// 	mstudiomesh_t(const mstudiomesh_t& vOther);
// };

// studio models
// struct mstudiomodel_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	inline const char * pszName( void ) const { return name; }
// 	char				name[64];
// 
// 	int					type;
// 
// 	float				boundingradius;
// 
// 	int					nummeshes;	
// 	int					meshindex;
// 	inline mstudiomesh_t *pMesh( int i ) const { return (mstudiomesh_t *)(((byte *)this) + meshindex) + i; };
// 
// 	// cache purposes
// 	int					numvertices;		// number of unique vertices/normals/texcoords
// 	int					vertexindex;		// vertex Vector
// 	int					tangentsindex;		// tangents Vector
// 
// 	// These functions are defined in application-specific code:
// 	const vertexFileHeader_t			*CacheVertexData(			void *pModelData );
// 
// 	// Access thin/fat mesh vertex data (only one will return a non-NULL result)
// 	const mstudio_modelvertexdata_t		*GetVertexData(		void *pModelData = NULL );
// 	const thinModelVertices_t			*GetThinVertexData(	void *pModelData = NULL );
// 
// 	int					numattachments;
// 	int					attachmentindex;
// 
// 	int					numeyeballs;
// 	int					eyeballindex;
// 	inline  mstudioeyeball_t *pEyeball( int i ) { return (mstudioeyeball_t *)(((byte *)this) + eyeballindex) + i; };
// 
// 	mstudio_modelvertexdata_t vertexdata;
// 
// 	int					unused[8];		// remove as appropriate
// };

// inline bool mstudio_modelvertexdata_t::HasTangentData( void ) const 
// {
// 	return (pTangentData != NULL);
// }

// inline int mstudio_modelvertexdata_t::GetGlobalVertexIndex( int i ) const
// {
// 	mstudiomodel_t *modelptr = (mstudiomodel_t *)((byte *)this - offsetof(mstudiomodel_t, vertexdata));
// 	Assert( ( modelptr->vertexindex % sizeof( mstudiovertex_t ) ) == 0 );
// 	return ( i + ( modelptr->vertexindex / sizeof( mstudiovertex_t ) ) );
// }
// 
// inline int mstudio_modelvertexdata_t::GetGlobalTangentIndex( int i ) const
// {
// 	mstudiomodel_t *modelptr = (mstudiomodel_t *)((byte *)this - offsetof(mstudiomodel_t, vertexdata));
// 	Assert( ( modelptr->tangentsindex % sizeof( Vector4D ) ) == 0 );
// 	return ( i + ( modelptr->tangentsindex / sizeof( Vector4D ) ) );
// }
// 
// inline mstudiovertex_t *mstudio_modelvertexdata_t::Vertex( int i ) const 
// {
// 	return (mstudiovertex_t *)pVertexData + GetGlobalVertexIndex( i );
// }
// 
// inline Vector *mstudio_modelvertexdata_t::Position( int i ) const 
// {
// 	return &Vertex(i)->m_vecPosition;
// }
// 
// inline Vector *mstudio_modelvertexdata_t::Normal( int i ) const 
// { 
// 	return &Vertex(i)->m_vecNormal;
// }
// 
// inline Vector4D *mstudio_modelvertexdata_t::TangentS( int i ) const 
// {
// 	// NOTE: The tangents vector is 16-bytes in a separate array
// 	// because it only exists on the high end, and if I leave it out
// 	// of the mstudiovertex_t, the vertex is 64-bytes (good for low end)
// 	return (Vector4D *)pTangentData + GetGlobalTangentIndex( i );
// }
// 
// inline Vector2D *mstudio_modelvertexdata_t::Texcoord( int i ) const 
// { 
// 	return &Vertex(i)->m_vecTexCoord;
// }
// 
// inline mstudioboneweight_t *mstudio_modelvertexdata_t::BoneWeights( int i ) const 
// {
// 	return &Vertex(i)->m_BoneWeights;
// }
// 
// inline mstudiomodel_t *mstudiomesh_t::pModel() const 
// { 
// 	return (mstudiomodel_t *)(((byte *)this) + modelindex); 
// }
// 
// inline bool mstudio_meshvertexdata_t::HasTangentData( void ) const
// {
// 	return modelvertexdata->HasTangentData();
// }
// 
// inline const mstudio_meshvertexdata_t *mstudiomesh_t::GetVertexData( void *pModelData )
// {
// 	// get this mesh's model's vertex data (allow for mstudiomodel_t::GetVertexData
// 	// returning NULL if the data has been converted to 'thin' vertices)
// 	this->pModel()->GetVertexData( pModelData );
// 	vertexdata.modelvertexdata = &( this->pModel()->vertexdata );
// 
// 	if ( !vertexdata.modelvertexdata->pVertexData )
// 		return NULL;
// 
// 	return &vertexdata;
// }
// 
// inline const thinModelVertices_t * mstudiomesh_t::GetThinVertexData( void *pModelData )
// {
// 	// get this mesh's model's thin vertex data
// 	return this->pModel()->GetThinVertexData( pModelData );
// }
// 
// inline int mstudio_meshvertexdata_t::GetModelVertexIndex( int i ) const
// {
// 	mstudiomesh_t *meshptr = (mstudiomesh_t *)((byte *)this - offsetof(mstudiomesh_t,vertexdata)); 
// 	return meshptr->vertexoffset + i;
// }
// 
// inline int mstudio_meshvertexdata_t::GetGlobalVertexIndex( int i ) const
// {
// 	return modelvertexdata->GetGlobalVertexIndex( GetModelVertexIndex( i ) );
// }
// 
// inline Vector *mstudio_meshvertexdata_t::Position( int i ) const 
// {
// 	return modelvertexdata->Position( GetModelVertexIndex( i ) ); 
// };
// 
// inline Vector *mstudio_meshvertexdata_t::Normal( int i ) const 
// {
// 	return modelvertexdata->Normal( GetModelVertexIndex( i ) ); 
// };
// 
// inline Vector4D *mstudio_meshvertexdata_t::TangentS( int i ) const
// {
// 	return modelvertexdata->TangentS( GetModelVertexIndex( i ) );
// }
// 
// inline Vector2D *mstudio_meshvertexdata_t::Texcoord( int i ) const 
// {
// 	return modelvertexdata->Texcoord( GetModelVertexIndex( i ) ); 
// };
// 
// inline mstudioboneweight_t *mstudio_meshvertexdata_t::BoneWeights( int i ) const 
// {
// 	return modelvertexdata->BoneWeights( GetModelVertexIndex( i ) ); 
// };
// 
// inline mstudiovertex_t *mstudio_meshvertexdata_t::Vertex( int i ) const
// {
// 	return modelvertexdata->Vertex( GetModelVertexIndex( i ) );
// }
// 
// // a group of studio model data
// enum studiomeshgroupflags_t
// {
// 	MESHGROUP_IS_HWSKINNED		= 0x2,
// 	MESHGROUP_IS_DELTA_FLEXED	= 0x4
// };


// ----------------------------------------------------------
// Runtime stuff
// ----------------------------------------------------------

// struct studiomeshgroup_t
// {
// 	IMesh			*m_pMesh;
// 	int				m_NumStrips;
// 	int				m_Flags;			// see studiomeshgroupflags_t
// 	OptimizedModel::StripHeader_t	*m_pStripData;
// 	unsigned short	*m_pGroupIndexToMeshIndex;
// 	int				m_NumVertices;
// 	int				*m_pUniqueFaces;	// for performance measurements
// 	unsigned short	*m_pIndices;
// 	unsigned short	*m_pTopologyIndices;
// 	bool			m_MeshNeedsRestore;
// 	short			m_ColorMeshID;
// 	IMorph			*m_pMorph;
// 
// 	inline unsigned short MeshIndex( int i ) const { return m_pGroupIndexToMeshIndex[m_pIndices[i]]; }
// };


// studio model data
// struct studiomeshdata_t
// {
// 	int					m_NumGroup;
// 	studiomeshgroup_t*	m_pMeshGroup;
// };
// 
// struct studioloddata_t
// {
// 	// not needed - this is really the same as studiohwdata_t.m_NumStudioMeshes
// 	//int					m_NumMeshes; 
// 	studiomeshdata_t	*m_pMeshData; // there are studiohwdata_t.m_NumStudioMeshes of these.
// 	float				m_SwitchPoint;
// 	// one of these for each lod since we can switch to simpler materials on lower lods.
// 	int					numMaterials; 
// 	IMaterial			**ppMaterials; /* will have studiohdr_t.numtextures elements allocated */
// 	// hack - this needs to go away.
// 	int					*pMaterialFlags; /* will have studiohdr_t.numtextures elements allocated */
// #ifndef _CERT
// 	int					m_NumFaces;	/* Total face count for this LOD */
// #endif // !_CERT
// 
// 	// For decals on hardware morphing, we must actually do hardware skinning
// 	// For this to work, we have to hope that the total # of bones used by
// 	// hw flexed verts is < than the max possible for the dx level we're running under
// 	int					*m_pHWMorphDecalBoneRemap;
// 	int					m_nDecalBoneCount;
// };
// 
// struct studiohwdata_t
// {
// 	int					m_RootLOD;	// calced and clamped, nonzero for lod culling
// 	int					m_NumLODs;
// 	studioloddata_t		*m_pLODs;
// 	int					m_NumStudioMeshes;
// 
// 	inline float LODMetric( float unitSphereSize ) const { return ( unitSphereSize != 0.0f ) ? (100.0f / unitSphereSize) : 0.0f; }
// 	inline int GetLODForMetric( float lodMetric ) const
// 	{
// 		static ConVarRef r_lod_switch_scale( "r_lod_switch_scale" );
// 
// 		if ( !m_NumLODs )
// 			return 0;
// 
// 		// On low GPU levels, we pull in the LOD transitions with a scale factor
// 		float flSwitchPointModifier = r_lod_switch_scale.IsValid() ? r_lod_switch_scale.GetFloat() : 1.0f;
// 
// 		// shadow lod is specified on the last lod with a negative switch
// 		// never consider shadow lod as viable candidate
// 		int numLODs = (m_pLODs[m_NumLODs-1].m_SwitchPoint < 0.0f) ? m_NumLODs-1 : m_NumLODs;
// 
// 		for ( int i = m_RootLOD; i < numLODs-1; i++ )
// 		{
// 			if ( ( m_pLODs[i+1].m_SwitchPoint * flSwitchPointModifier ) > lodMetric )
// 				return i;
// 		}
// 
// 		return numLODs-1;
// 	}
// 
// #ifndef _CERT
// 	// Each model counts how many rendered faces it accounts for each frame:
// 	inline void UpdateFacesRenderedCount( studiohdr_t *pStudioHdr, CUtlHash< studiohwdata_t * > &hwDataHash, int nLOD, int nInstances, int nFacesOverride = -1 )
// 	{
// 		if ( hwDataHash.Find( this ) == hwDataHash.InvalidHandle() )
// 		{
// 			m_NumFacesRenderedThisFrame = 0;
// 			m_NumTimesRenderedThisFrame = 0;
// 			m_pStudioHdr = pStudioHdr;
// 			hwDataHash.Insert( this );
// 		}
// 		Assert( m_pStudioHdr && ( m_pStudioHdr == pStudioHdr ) );
// 		if ( nFacesOverride == -1 ) 
// 		{
// 			nFacesOverride = ( nLOD < m_NumLODs ) ? m_pLODs[ nLOD ].m_NumFaces : 0;
// 		}
// 		m_NumFacesRenderedThisFrame += nInstances * nFacesOverride;
// 		m_NumTimesRenderedThisFrame ++;
// 	}
// 	int m_NumFacesRenderedThisFrame;
// 	int m_NumTimesRenderedThisFrame;
// 	studiohdr_t *m_pStudioHdr; // There is no way to map between these inside CStudioRender, so we have to store it.
// #endif // !_CERT
// };

// ----------------------------------------------------------
// ----------------------------------------------------------

// body part index
// struct mstudiobodyparts_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					sznameindex;
// 	inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
// 	int					nummodels;
// 	int					base;
// 	int					modelindex; // index into models array
// 	inline mstudiomodel_t *pModel( int i ) const { return (mstudiomodel_t *)(((byte *)this) + modelindex) + i; };
// };
// 
// 
// struct mstudiomouth_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					bone;
// 	Vector				forward;
// 	int					flexdesc;
// 
// 	mstudiomouth_t(){}
// private:
// 	// No copy constructors allowed
// 	mstudiomouth_t(const mstudiomouth_t& vOther);
// };
// 
// struct mstudiohitboxset_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					sznameindex;
// 	inline char * const	pszName( void ) const { return ((char *)this) + sznameindex; }
// 	int					numhitboxes;
// 	int					hitboxindex;
// 	inline mstudiobbox_t *pHitbox( int i ) const { return (mstudiobbox_t *)(((byte *)this) + hitboxindex) + i; };
// };


//-----------------------------------------------------------------------------
// Src bone transforms are transformations that will convert .dmx or .smd-based animations into .mdl-based animations
// NOTE: The operation you should apply is: pretransform * bone transform * posttransform
//-----------------------------------------------------------------------------
// struct mstudiosrcbonetransform_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 
// 	int			sznameindex;
// 	inline const char *pszName( void ) const { return ((char *)this) + sznameindex; }
// 	matrix3x4_t	pretransform;	
// 	matrix3x4_t	posttransform;	
// };


// ----------------------------------------------------------
// Purpose: Load time results on model compositing
// ----------------------------------------------------------

class virtualgroup_t_PS3
{
public:
	virtualgroup_t_PS3( void ) { cache = NULL; };
	// tool dependant.  In engine this is a model_t, in tool it's a direct pointer
	void *cache;
	// converts cache entry into a usable studiohdr_t *
// 	const studiohdr_t *GetStudioHdr( void ) const;

// 	CUtlVector< int > boneMap;				// maps global bone to local bone
// 	CUtlVector< int > masterBone;			// maps local bone to global bone
// 	CUtlVector< int > masterSeq;			// maps local sequence to master sequence
// 	CUtlVector< int > masterAnim;			// maps local animation to master animation
// 	CUtlVector< int > masterAttachment;	// maps local attachment to global
// 	CUtlVector< int > masterPose;			// maps local pose parameter to global
// 	CUtlVector< int > masterNode;			// maps local transition nodes to global
};

struct virtualsequence_t_PS3
{
	int	flags;
	int activity;
	int group;
	int index;
};

struct virtualgeneric_t_PS3
{
	int group;
	int index;
};


struct virtualmodel_t_PS3
{
// 	void AppendSequences( int group, const studiohdr_t_PS3 *pStudioHdr ); 
// 	void AppendAnimations( int group, const studiohdr_t_PS3 *pStudioHdr );
// 	void AppendAttachments( int ground, const studiohdr_t_PS3 *pStudioHdr );
// 	void AppendPoseParameters( int group, const studiohdr_t_PS3 *pStudioHdr );
// 	void AppendBonemap( int group, const studiohdr_t_PS3 *pStudioHdr );
// 	void AppendNodes( int group, const studiohdr_t_PS3 *pStudioHdr );
// 	void AppendTransitions( int group, const studiohdr_t_PS3 *pStudioHdr );
// 	void AppendIKLocks( int group, const studiohdr_t_PS3 *pStudioHdr );
// 	void AppendModels( int group, const studiohdr_t_PS3 *pStudioHdr );
// 	void UpdateAutoplaySequences( const studiohdr_t_PS3 *pStudioHdr );

// 	virtualgroup_t_PS3 *pAnimGroup( int animation ) { return &m_group[ m_anim[ animation ].group ]; }; // Note: user must manage mutex for this
// 	virtualgroup_t_PS3 *pSeqGroup( int sequence ) { return &m_group[ m_seq[ sequence ].group ]; }; // Note: user must manage mutex for this

    CThreadFastMutex_PS3 m_Lock;

// 	CUtlVector< virtualsequence_t_PS3 > m_seq;
// 	CUtlVector< virtualgeneric_t_PS3 > m_anim;
// 	CUtlVector< virtualgeneric_t_PS3 > m_attachment;
// 	CUtlVector< virtualgeneric_t_PS3 > m_pose;
// 	CUtlVector< virtualgroup_t_PS3 > m_group;
// 	CUtlVector< virtualgeneric_t_PS3 > m_node;
// 	CUtlVector< virtualgeneric_t_PS3 > m_iklock;
// 	CUtlVector< unsigned short > m_autoplaySequences;
};

// 'thin' vertex data, used to do model decals (see Studio_CreateThinVertexes())
// struct thinModelVertices_t
// {
// 	void Init( int numBoneInfluences, Vector *positions, unsigned short *normals, float *boneWeights, byte *boneIndices )
// 	{
// 		Assert( positions != NULL );
// 		Assert( normals   != NULL );
// 		Assert( ( numBoneInfluences >= 0 ) && ( numBoneInfluences <= 3 ) );
// 		Assert( numBoneInfluences > 0 ? !!boneIndices : !boneIndices );
// 		Assert( numBoneInfluences > 1 ? !!boneWeights : !boneWeights );
// 		m_numBoneInfluences	= numBoneInfluences;
// 		m_vecPositions		= positions;
// 		m_vecNormals		= normals;
// 		m_boneWeights		= boneWeights;
// 		m_boneIndices		= boneIndices;
// 	}
// 
// 	void SetPosition( int vertIndex, const Vector & position )
// 	{
// 		Assert( m_vecPositions );
// 		m_vecPositions[ vertIndex ] = position;
// 	}
// 
// 	void SetNormal( int vertIndex, const Vector & normal )
// 	{
// 		Assert( m_vecNormals );
// 		unsigned int packedNormal;
// 		PackNormal_UBYTE4( normal.x, normal.y, normal.z, &packedNormal );
// 		m_vecNormals[ vertIndex ] = (unsigned short)( 0x0000FFFF & packedNormal );
// 	}
// 
// 	void SetBoneWeights( int vertIndex, const mstudioboneweight_t & boneWeights )
// 	{
// 		Assert( ( m_numBoneInfluences  >= 1 ) && ( m_numBoneInfluences  <= 3 ) );
// 		Assert( ( boneWeights.numbones >= 1 ) && ( boneWeights.numbones <= m_numBoneInfluences ) );
// 		int    numStoredWeights = MAX( 0, ( m_numBoneInfluences - 1 ) );
// 		float *pBaseWeight	= m_boneWeights + vertIndex*numStoredWeights;
// 		byte  *pBaseIndex	= m_boneIndices + vertIndex*m_numBoneInfluences;
// 		for ( int i = 0; i < m_numBoneInfluences; i++ )
// 		{
// 			pBaseIndex[i] = boneWeights.bone[i];
// 		}
// 		for ( int i = 0; i < numStoredWeights; i++ )
// 		{
// 			pBaseWeight[i] = boneWeights.weight[i];
// 		}
// 	}
// 
// 	void GetMeshPosition( mstudiomesh_t *pMesh, int meshIndex, Vector *pPosition ) const
// 	{
// 		Assert( pMesh );
// 		GetPosition( pMesh->vertexdata.GetGlobalVertexIndex( meshIndex ), pPosition );
// 	}
// 
// 	void GetMeshNormal( mstudiomesh_t *pMesh, int meshIndex, Vector *pNormal ) const
// 	{
// 		Assert( pMesh );
// 		GetNormal( pMesh->vertexdata.GetGlobalVertexIndex( meshIndex ), pNormal );
// 	}
// 
// 	void GetMeshBoneWeights( mstudiomesh_t *pMesh, int meshIndex, mstudioboneweight_t *pBoneWeights ) const
// 	{
// 		Assert( pMesh );
// 		GetBoneWeights( pMesh->vertexdata.GetGlobalVertexIndex( meshIndex ), pBoneWeights );
// 	}
// 
// 	void GetModelPosition( mstudiomodel_t *pModel, int modelIndex, Vector *pPosition ) const
// 	{
// 		Assert( pModel );
// 		GetPosition( pModel->vertexdata.GetGlobalVertexIndex( modelIndex ), pPosition );
// 	}
// 
// 	void GetModelNormal( mstudiomodel_t *pModel, int modelIndex, Vector *pNormal ) const
// 	{
// 		Assert( pModel );
// 		GetNormal( pModel->vertexdata.GetGlobalVertexIndex( modelIndex ), pNormal );
// 	}
// 
// 	void GetModelBoneWeights( mstudiomodel_t *pModel, int modelIndex, mstudioboneweight_t *pBoneWeights ) const
// 	{
// 		Assert( pModel );
// 		GetBoneWeights( pModel->vertexdata.GetGlobalVertexIndex( modelIndex ), pBoneWeights );
// 	}
// 
// private:
// 	void GetPosition( int vertIndex, Vector *pPosition ) const
// 	{
// 		Assert( pPosition );
// 		Assert( m_vecPositions );
// 		*pPosition = m_vecPositions[ vertIndex ];
// 	}
// 
// 	void GetNormal( int vertIndex, Vector *pNormal ) const
// 	{
// 		Assert( pNormal );
// 		Assert( m_vecNormals );
// 		unsigned int packedNormal = 0x0000FFFF & m_vecNormals[ vertIndex ];
// 		UnpackNormal_UBYTE4( &packedNormal, pNormal->Base() );
// 	}
// 
// 	void GetBoneWeights( int vertIndex, mstudioboneweight_t * RESTRICT pBoneWeights ) const
// 	{
// 		Assert( pBoneWeights );
// 		Assert( ( m_numBoneInfluences <= 1 ) || ( m_boneWeights != NULL ) );
// 		Assert( ( m_numBoneInfluences <= 0 ) || ( m_boneIndices != NULL ) );
// 		int    numStoredWeights = MAX( 0, ( m_numBoneInfluences - 1 ) );
// 		float * RESTRICT pBaseWeight	= m_boneWeights + vertIndex*numStoredWeights;
// 		byte  * RESTRICT pBaseIndex	= m_boneIndices + vertIndex*m_numBoneInfluences;
// 		float  sum			= 0.0f;
// 		// TODO: unroll this loop? It's only three. We could use a switch
// 		// and code it explicitly for the various possible m_numBoneInfluences
// 		// which would improve scheduling but bloat code.
// 		for (int i = 0;i < MAX_NUM_BONES_PER_VERT;i++)
// 		{
// 			float weight;
// 			if ( i < ( m_numBoneInfluences - 1 ) )
// 			{
// 				weight = pBaseWeight[i];
// 				sum += weight;
// 			}
// 			else
// 			{
// 				weight = 1.0f - sum;
// 				sum = 1.0f;
// 			}
// 
// 			pBoneWeights->weight[i] = weight;
// 			pBoneWeights->bone[i] = ( i < m_numBoneInfluences ) ? pBaseIndex[i] : 0;
// 
// 			/*
// 			if ( i < ( m_numBoneInfluences - 1 ) )
// 				pBoneWeights->weight[i] = pBaseWeight[i];
// 			else
// 				pBoneWeights->weight[i] = 1.0f - sum;
// 			sum += pBoneWeights->weight[i];
// 
// 			pBoneWeights->bone[i] = ( i < m_numBoneInfluences ) ? pBaseIndex[i] : 0;
// 			*/
// 		}
// 
// 		// Treat 'zero weights' as '100% binding to bone zero':
// 		pBoneWeights->numbones = m_numBoneInfluences ? m_numBoneInfluences : 1;
// 	}
// 
// 	int				m_numBoneInfluences;// Number of bone influences per vertex, N
// 	float			*m_boneWeights;		// This array stores (N-1) weights per vertex (unless N is zero)
// 	byte			*m_boneIndices;		// This array stores N indices per vertex
// 	Vector			*m_vecPositions;
// 	unsigned short	*m_vecNormals;		// Normals are compressed into 16 bits apiece (see PackNormal_UBYTE4() )
// };


// ----------------------------------------------------------
// Studio Model Stream Data File
// ----------------------------------------------------------

// little-endian "IDSS"
// #define MODEL_STREAM_FILE_ID		(('S'<<24)+('S'<<16)+('D'<<8)+'I')
// #define MODEL_STREAM_FILE_VERSION	1

// struct vertexStreamFileHeader_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int		id;								// MODEL_STREAM_FILE_ID
// 	int		version;						// MODEL_STREAM_FILE_VERSION
// 	long	checksum;						// same as studiohdr_t, ensures sync
// 	long	flags;							// flags
// 	int		numVerts;						// number of vertices
// 	int		uv2StreamStart;					// offset from base to uv2 stream
// 	int		uv2ElementSize;					// size of each uv2 element
// 	int		pad;							// pad
// 
// public:
// 
// 	// Accessor to uv2 stream
// 	const void *GetStreamUv2() const
// 	{
// 		if ( ( id == MODEL_STREAM_FILE_ID ) && ( uv2StreamStart != 0 ) )
// 			return ( void * ) ( uv2StreamStart + (byte *)this );
// 		else
// 			return NULL;
// 	}
// };



// ----------------------------------------------------------
// Studio Model Vertex Data File
// Position independent flat data for cache manager
// ----------------------------------------------------------

// little-endian "IDSV"
// #define MODEL_VERTEX_FILE_ID		(('V'<<24)+('S'<<16)+('D'<<8)+'I')
// #define MODEL_VERTEX_FILE_VERSION	4
// // this id (IDCV) is used once the vertex data has been compressed (see CMDLCache::CreateThinVertexes)
// #define MODEL_VERTEX_FILE_THIN_ID	(('V'<<24)+('C'<<16)+('D'<<8)+'I')
// // this id (IDDV) is used once the vertex data has been discarded (see CMDLCache::CreateNullVertexes)
// #define MODEL_VERTEX_FILE_NULL_ID	(('V'<<24)+('D'<<16)+('D'<<8)+'I')

// struct vertexFileHeader_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int		id;								// MODEL_VERTEX_FILE_ID
// 	int		version;						// MODEL_VERTEX_FILE_VERSION
// 	long	checksum;						// same as studiohdr_t, ensures sync
// 	int		numLODs;						// num of valid lods
// 	int		numLODVertexes[MAX_NUM_LODS];	// num verts for desired root lod
// 	int		numFixups;						// num of vertexFileFixup_t
// #ifdef _PS3
// 	union
// 	{
// 		int fixupTableStart;
// 		uint32 ps3edgeDmaInputDesc;			// [PS3: offset of the EDGE DMA INPUT stream]
// 	};
// 	uint32 inline GetPs3EdgeDmaInputStart() const { return ( ps3edgeDmaInputDesc & 0xFFFF ) << 8; }
// 	uint32 inline GetPs3EdgeDmaInputLength() const { return ( ( ps3edgeDmaInputDesc >> 16 ) & 0x7FFF ) << 8; }
// #else
// 	int		fixupTableStart;				// offset from base to fixup table
// #endif
// 	int		vertexDataStart;				// offset from base to vertex block
// 	int		tangentDataStart;				// offset from base to tangent block
// 
// public:
// 
// 	// Accessor to fat vertex data
// 	const mstudiovertex_t *GetVertexData() const
// 	{
// 		if ( ( id == MODEL_VERTEX_FILE_ID ) && ( vertexDataStart != 0 ) )
// 			return ( mstudiovertex_t * ) ( vertexDataStart + (byte *)this );
// 		else
// 			return NULL;
// 	}
// 	// Accessor to (fat) tangent vertex data (tangents aren't stored in compressed data)
// 	const Vector4D *GetTangentData() const
// 	{
// 		if ( ( id == MODEL_VERTEX_FILE_ID ) && ( tangentDataStart != 0 ) )
// 			return ( Vector4D * ) ( tangentDataStart + (byte *)this );
// 		else
// 			return NULL;
// 	}
// 	// Accessor to thin vertex data
// 	const  thinModelVertices_t *GetThinVertexData() const
// 	{
// 		if ( ( id == MODEL_VERTEX_FILE_THIN_ID ) && ( vertexDataStart != 0 ) )
// 			return ( thinModelVertices_t * ) ( vertexDataStart + (byte *)this );
// 		else
// 			return NULL;
// 	}
// 
// #ifdef _PS3
// 	// Accessor to EDGE DMA INPUT format
// 	const byte *GetPs3EdgeDmaInput() const
// 	{
// 		if ( ( id == MODEL_VERTEX_FILE_ID ) && ( ps3edgeDmaInputDesc & 0x80000000 ) )
// 			return ( byte * ) ( GetPs3EdgeDmaInputStart() + (byte *)this );
// 		else
// 			return NULL;
// 	}
// #endif
// };
// 
// // model vertex data accessor (defined here so vertexFileHeader_t can be used)
// inline const mstudio_modelvertexdata_t * mstudiomodel_t::GetVertexData( void *pModelData )
// {
// 	const vertexFileHeader_t * pVertexHdr = CacheVertexData( pModelData );
// 	if ( !pVertexHdr )
// 		return NULL;
// 
// 	vertexdata.pVertexData  = pVertexHdr->GetVertexData();
// 	vertexdata.pTangentData = pVertexHdr->GetTangentData();
// 
// 	if ( !vertexdata.pVertexData )
// 		return NULL;
// 
// 	return &vertexdata;
// }
// 
// // model thin vertex data accessor (defined here so vertexFileHeader_t can be used)
// inline const thinModelVertices_t * mstudiomodel_t::GetThinVertexData( void *pModelData )
// {
// 	const vertexFileHeader_t * pVertexHdr = CacheVertexData( pModelData );
// 	if ( !pVertexHdr )
// 		return NULL;
// 
// 	return pVertexHdr->GetThinVertexData();
// }
// 
// // apply sequentially to lod sorted vertex and tangent pools to re-establish mesh order
// struct vertexFileFixup_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int		lod;				// used to skip culled root lod
// 	int		sourceVertexID;		// absolute index from start of vertex/tangent blocks
// 	int		numVertexes;
// };
// 
// // This flag is set if no hitbox information was specified
// #define STUDIOHDR_FLAGS_AUTOGENERATED_HITBOX	( 1 << 0 )
// 
// // NOTE:  This flag is set at loadtime, not mdl build time so that we don't have to rebuild
// // models when we change materials.
// #define STUDIOHDR_FLAGS_USES_ENV_CUBEMAP		( 1 << 1 )
// 
// // Use this when there are translucent parts to the model but we're not going to sort it 
// #define STUDIOHDR_FLAGS_FORCE_OPAQUE			( 1 << 2 )
// 
// // Use this when we want to render the opaque parts during the opaque pass
// // and the translucent parts during the translucent pass
// #define STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS		( 1 << 3 )
// 
// // This is set any time the .qc files has $staticprop in it
// // Means there's no bones and no transforms
// #define STUDIOHDR_FLAGS_STATIC_PROP				( 1 << 4 )
// 
// // NOTE:  This flag is set at loadtime, not mdl build time so that we don't have to rebuild
// // models when we change materials.
// #define STUDIOHDR_FLAGS_USES_FB_TEXTURE		    ( 1 << 5 )
// 
// // This flag is set by studiomdl.exe if a separate "$shadowlod" entry was present
// //  for the .mdl (the shadow lod is the last entry in the lod list if present)
// #define STUDIOHDR_FLAGS_HASSHADOWLOD			( 1 << 6 )
// 
// // NOTE:  This flag is set at loadtime, not mdl build time so that we don't have to rebuild
// // models when we change materials.
// #define STUDIOHDR_FLAGS_USES_BUMPMAPPING		( 1 << 7 )
// 
// // NOTE:  This flag is set when we should use the actual materials on the shadow LOD
// // instead of overriding them with the default one (necessary for translucent shadows)
// #define STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS	( 1 << 8 )
// 
// // NOTE:  This flag is set when we should use the actual materials on the shadow LOD
// // instead of overriding them with the default one (necessary for translucent shadows)
// #define STUDIOHDR_FLAGS_OBSOLETE				( 1 << 9 )
// 
// #define STUDIOHDR_FLAGS_UNUSED					( 1 << 10 )
// 
// // NOTE:  This flag is set at mdl build time
// #define STUDIOHDR_FLAGS_NO_FORCED_FADE			( 1 << 11 )
// 
// // NOTE:  The npc will lengthen the viseme check to always include two phonemes
// #define STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE	( 1 << 12 )
// 
// // This flag is set when the .qc has $constantdirectionallight in it
// // If set, we use constantdirectionallightdot to calculate light intensity
// // rather than the normal directional dot product
// // only valid if STUDIOHDR_FLAGS_STATIC_PROP is also set
// #define STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT ( 1 << 13 )
// 
// // Flag to mark delta flexes as already converted from disk format to memory format
// #define STUDIOHDR_FLAGS_FLEXES_CONVERTED		( 1 << 14 )
// 
// // Indicates the studiomdl was built in preview mode
// #define STUDIOHDR_FLAGS_BUILT_IN_PREVIEW_MODE	( 1 << 15 )
// 
// // Ambient boost (runtime flag)
// #define STUDIOHDR_FLAGS_AMBIENT_BOOST			( 1 << 16 )
// 
// // Don't cast shadows from this model (useful on first-person models)
// #define STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS		( 1 << 17 )
// 
// // alpha textures should cast shadows in vrad on this model (ONLY prop_static!)
// #define STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS	( 1 << 18 )
// 
// // Model has a quad-only Catmull-Clark SubD cage
// #define STUDIOHDR_FLAGS_SUBDIVISION_SURFACE		( 1 << 19 )
// 
// // flagged on load to indicate no animation events on this model
// #define STUDIOHDR_FLAGS_NO_ANIM_EVENTS			( 1 << 20 )
// 
// // If flag is set then studiohdr_t.flVertAnimFixedPointScale contains the
// // scale value for fixed point vert anim data, if not set then the
// // scale value is the default of 1.0 / 4096.0.  Regardless use
// // studiohdr_t::VertAnimFixedPointScale() to always retrieve the scale value
// #define STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE	( 1 << 21 )
// 
// // If flag is set then model data is processed for EDGE
// // the flag is set at tool time when producing PS3-format assets
// #define STUDIOHDR_FLAGS_PS3_EDGE_FORMAT			( 1 << 22 )

// NOTE! Next time we up the .mdl file format, remove studiohdr2_t
// and insert all fields in this structure into studiohdr_t.
struct studiohdr2_t_PS3
{
	// NOTE: For forward compat, make sure any methods in this struct
	// are also available in studiohdr_t so no leaf code ever directly references
	// a studiohdr2_t structure
//	DECLARE_BYTESWAP_DATADESC();
	int numsrcbonetransform;
	int srcbonetransformindex;

	int	illumpositionattachmentindex;
//	inline int			IllumPositionAttachmentIndex() const { return illumpositionattachmentindex; }

	float flMaxEyeDeflection;
//	inline float		MaxEyeDeflection() const { return flMaxEyeDeflection != 0.0f ? flMaxEyeDeflection : 0.866f; } // default to cos(30) if not set

	int linearboneindex;
//	inline mstudiolinearbone_t *pLinearBones() const { return (linearboneindex) ? (mstudiolinearbone_t *)(((byte *)this) + linearboneindex) : NULL; }

	int sznameindex;
//	inline char *pszName() { return (sznameindex) ? (char *)(((byte *)this) + sznameindex ) : NULL; }

	int m_nBoneFlexDriverCount;
	int m_nBoneFlexDriverIndex;
//	inline mstudioboneflexdriver_t *pBoneFlexDriver( int i ) const { Assert( i >= 0 && i < m_nBoneFlexDriverCount ); return (mstudioboneflexdriver_t *)(((byte *)this) + m_nBoneFlexDriverIndex) + i; }

	int reserved[56];
};

struct studiohdr_t_PS3
{
//	DECLARE_BYTESWAP_DATADESC();
	int					id;
	int					version;

	long				checksum;		// this has to be the same in the phy and vtx files to load!
	
//	inline const char *	pszName( void ) const { if (studiohdr2index && pStudioHdr2()->pszName()) return pStudioHdr2()->pszName(); else return name; }
	char				name[64];

	int					length;

	Vector				eyeposition;	// ideal eye position

	Vector				illumposition;	// illumination center
	
	Vector				hull_min;		// ideal movement hull size
	Vector				hull_max;			

	Vector				view_bbmin;		// clipping bounding box
	Vector				view_bbmax;		

	int					flags;

	int					numbones;			// bones
	int					boneindex;
// 	inline mstudiobone_t *pBone( int i ) const { Assert( i >= 0 && i < numbones); return (mstudiobone_t *)(((byte *)this) + boneindex) + i; };
// 	int					RemapSeqBone( int iSequence, int iLocalBone ) const;	// maps local sequence bone to global bone
// 	int					RemapAnimBone( int iAnim, int iLocalBone ) const;		// maps local animations bone to global bone

	int					numbonecontrollers;		// bone controllers
	int					bonecontrollerindex;
// 	inline mstudiobonecontroller_t *pBonecontroller( int i ) const { Assert( i >= 0 && i < numbonecontrollers); return (mstudiobonecontroller_t *)(((byte *)this) + bonecontrollerindex) + i; };

	int					numhitboxsets;
	int					hitboxsetindex;

	// Look up hitbox set by index
// 	mstudiohitboxset_t	*pHitboxSet( int i ) const 
// 	{ 
// 		Assert( i >= 0 && i < numhitboxsets); 
// 		return (mstudiohitboxset_t *)(((byte *)this) + hitboxsetindex ) + i; 
// 	};

	// Calls through to hitbox to determine size of specified set
// 	inline mstudiobbox_t *pHitbox( int i, int set ) const 
// 	{ 
// 		mstudiohitboxset_t const *s = pHitboxSet( set );
// 		if ( !s )
// 			return NULL;
// 
// 		return s->pHitbox( i );
// 	};

	// Calls through to set to get hitbox count for set
// 	inline int			iHitboxCount( int set ) const
// 	{
// 		mstudiohitboxset_t const *s = pHitboxSet( set );
// 		if ( !s )
// 			return 0;
// 
// 		return s->numhitboxes;
// 	};

	// file local animations? and sequences
//private:
	int					numlocalanim;			// animations/poses
	int					localanimindex;		// animation descriptions
 // 	inline mstudioanimdesc_t *pLocalAnimdesc( int i ) const { if (i < 0 || i >= numlocalanim) i = 0; return (mstudioanimdesc_t *)(((byte *)this) + localanimindex) + i; };

	int					numlocalseq;				// sequences
	int					localseqindex;
// 	inline mstudioseqdesc_t_PS3 *pLocalSeqdesc_PS3( int i ) const { if (i < 0 || i >= numlocalseq) i = 0; return (mstudioseqdesc_t_PS3 *)(((byte *)this) + localseqindex) + i; };

//public:
// 	bool				SequencesAvailable() const;
// 	int					GetNumSeq_Internal() const;
// 	inline int			GetNumSeq() const
// 	{
// 		if (numincludemodels == 0)
// 		{
// 			return numlocalseq;
// 		}
// 		return GetNumSeq_Internal();
// 
// 	}
// 	mstudioanimdesc_t	&pAnimdesc_Internal( int i ) const;
// 	inline mstudioanimdesc_t &pAnimdesc( int i ) const
// 	{
// 		if (numincludemodels == 0)
// 		{
// 			return *pLocalAnimdesc( i );
// 		}
// 
// 		return pAnimdesc_Internal( i );
// 	}
// 
// 	mstudioseqdesc_t	&pSeqdesc_Internal( int i ) const;
// 	inline mstudioseqdesc_t &pSeqdesc( int i ) const
// 	{
// 		if (numincludemodels == 0)
// 		{
// 			return *pLocalSeqdesc( i );
// 		}
// 		return pSeqdesc_Internal( i );
// 	}
// 	int			iRelativeAnim_Internal( int baseseq, int relanim ) const;	// maps seq local anim reference to global anim index
// 	inline int			iRelativeAnim( int baseseq, int relanim ) const
// 	{
// 		if ( numincludemodels == 0 )
// 			return relanim;
// 		return iRelativeAnim_Internal( baseseq, relanim );
// 	}
// 	int					iRelativeSeq_Internal( int baseseq, int relseq ) const;		// maps seq local seq reference to global seq index
// 	inline int			iRelativeSeq( int baseseq, int relseq ) const
// 	{
// 		if (numincludemodels == 0)
// 		{
// 			return relseq;
// 		}
// 		return iRelativeSeq_Internal( baseseq, relseq );
// 	}

//private:
	mutable int			activitylistversion;	// initialization flag - have the sequences been indexed?
	mutable int			eventsindexed;
//public:
// 	int					GetSequenceActivity( int iSequence );
// 	void				SetSequenceActivity( int iSequence, int iActivity );
// 	int					GetActivityListVersion( void );
// 	void				SetActivityListVersion( int version ) const;
// 	int					GetEventListVersion( void );
// 	void				SetEventListVersion( int version );
	
	// raw textures
	int					numtextures;
	int					textureindex;
// 	inline mstudiotexture_t *pTexture( int i ) const { Assert( i >= 0 && i < numtextures ); return (mstudiotexture_t *)(((byte *)this) + textureindex) + i; }; 


	// raw textures search paths
	int					numcdtextures;
	int					cdtextureindex;
// 	inline char			*pCdtexture( int i ) const { return (((char *)this) + *((int *)(((byte *)this) + cdtextureindex) + i)); };

	// replaceable textures tables
	int					numskinref;
	int					numskinfamilies;
	int					skinindex;
// 	inline short		*pSkinref( int i ) const { return (short *)(((byte *)this) + skinindex) + i; };

	int					numbodyparts;		
	int					bodypartindex;
// 	inline mstudiobodyparts_t	*pBodypart( int i ) const { return (mstudiobodyparts_t *)(((byte *)this) + bodypartindex) + i; };

	// queryable attachable points
//private:
	int					numlocalattachments;
	int					localattachmentindex;
// 	inline mstudioattachment_t	*pLocalAttachment( int i ) const { Assert( i >= 0 && i < numlocalattachments); return (mstudioattachment_t *)(((byte *)this) + localattachmentindex) + i; };
////public:
// 	int					GetNumAttachments( void ) const;
// 	const mstudioattachment_t &pAttachment( int i ) const;
// 	int					GetAttachmentBone( int i );
	// used on my tools in hlmv, not persistant
// 	void				SetAttachmentBone( int iAttachment, int iBone );

	// animation node to animation node transition graph
//private:
	int					numlocalnodes;
	int					localnodeindex;
	int					localnodenameindex;
// 	inline char			*pszLocalNodeName( int iNode ) const { Assert( iNode >= 0 && iNode < numlocalnodes); return (((char *)this) + *((int *)(((byte *)this) + localnodenameindex) + iNode)); }
// 	inline byte			*pLocalTransition( int i ) const { Assert( i >= 0 && i < (numlocalnodes * numlocalnodes)); return (byte *)(((byte *)this) + localnodeindex) + i; };

//// public:
// 	int					EntryNode( int iSequence );
// 	int					ExitNode( int iSequence );
// 	char				*pszNodeName( int iNode );
// 	int					GetTransition( int iFrom, int iTo ) const;

	int					numflexdesc;
	int					flexdescindex;
//	inline mstudioflexdesc_t *pFlexdesc( int i ) const { Assert( i >= 0 && i < numflexdesc); return (mstudioflexdesc_t *)(((byte *)this) + flexdescindex) + i; };

	int					numflexcontrollers;
	int					flexcontrollerindex;
//	inline mstudioflexcontroller_t *pFlexcontroller( LocalFlexController_t i ) const { Assert( i >= 0 && i < numflexcontrollers); return (mstudioflexcontroller_t *)(((byte *)this) + flexcontrollerindex) + i; };

	int					numflexrules;
	int					flexruleindex;
//	inline mstudioflexrule_t *pFlexRule( int i ) const { Assert( i >= 0 && i < numflexrules); return (mstudioflexrule_t *)(((byte *)this) + flexruleindex) + i; };

	int					numikchains;
	int					ikchainindex;
//	inline mstudioikchain_t *pIKChain( int i ) const { Assert( i >= 0 && i < numikchains); return (mstudioikchain_t *)(((byte *)this) + ikchainindex) + i; };

	int					nummouths;
	int					mouthindex;
//	inline mstudiomouth_t *pMouth( int i ) const { Assert( i >= 0 && i < nummouths); return (mstudiomouth_t *)(((byte *)this) + mouthindex) + i; };

//private:
	int					numlocalposeparameters;
	int					localposeparamindex;
//	inline mstudioposeparamdesc_t *pLocalPoseParameter( int i ) const { Assert( i >= 0 && i < numlocalposeparameters); return (mstudioposeparamdesc_t *)(((byte *)this) + localposeparamindex) + i; };
////public:
// 	int					GetNumPoseParameters( void ) const;
// 	const mstudioposeparamdesc_t &pPoseParameter( int i );
// 	int					GetSharedPoseParameter( int iSequence, int iLocalPose ) const;

	int					surfacepropindex;
// 	inline char * const pszSurfaceProp( void ) const { return ((char *)this) + surfacepropindex; }
// 	inline int			GetSurfaceProp() const { return surfacepropLookup; }

	// Key values
	int					keyvalueindex;
	int					keyvaluesize;
// 	inline const char * KeyValueText( void ) const { return keyvaluesize != 0 ? ((char *)this) + keyvalueindex : NULL; }

	int					numlocalikautoplaylocks;
	int					localikautoplaylockindex;
// 	inline mstudioiklock_t *pLocalIKAutoplayLock( int i ) const { Assert( i >= 0 && i < numlocalikautoplaylocks); return (mstudioiklock_t *)(((byte *)this) + localikautoplaylockindex) + i; };

// 	int					GetNumIKAutoplayLocks( void ) const;
// 	const mstudioiklock_t &pIKAutoplayLock( int i );
// 	int					CountAutoplaySequences() const;
// 	int					CopyAutoplaySequences( unsigned short *pOut, int outCount ) const;
// 	int					GetAutoplayList( unsigned short **pOut ) const;

	// The collision model mass that jay wanted
	float				mass;
	int					contents;

	// external animations, models, etc.
	int					numincludemodels;
	int					includemodelindex;
// 	inline mstudiomodelgroup_t *pModelGroup( int i ) const { Assert( i >= 0 && i < numincludemodels); return (mstudiomodelgroup_t *)(((byte *)this) + includemodelindex) + i; };
	// implementation specific call to get a named model
// 	const studiohdr_t	*FindModel( void **cache, char const *modelname ) const;

	// implementation specific back pointer to virtual data
	mutable void		*virtualModel;
// 	virtualmodel_t		*GetVirtualModel( void ) const;

	// for demand loaded animation blocks
	int					szanimblocknameindex;	
// 	inline char * const pszAnimBlockName( void ) const { return ((char *)this) + szanimblocknameindex; }
	int					numanimblocks;
	int					animblockindex;
// 	inline mstudioanimblock_t *pAnimBlock( int i ) const { Assert( i > 0 && i < numanimblocks); return (mstudioanimblock_t *)(((byte *)this) + animblockindex) + i; };
	mutable void		*animblockModel;
// 	byte *				GetAnimBlock( int i, bool preloadIfMissing = true ) const;
// 	bool				hasAnimBlockBeenPreloaded( int i ) const;

	int					bonetablebynameindex;
// 	inline const byte	*GetBoneTableSortedByName() const { return (byte *)this + bonetablebynameindex; }

	// used by tools only that don't cache, but persist mdl's peer data
	// engine uses virtualModel to back link to cache pointers
	void				*pVertexBase;
	void				*pIndexBase;

	// if STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT is set,
	// this value is used to calculate directional components of lighting 
	// on static props
	byte				constdirectionallightdot;

	// set during load of mdl data to track *desired* lod configuration (not actual)
	// the *actual* clamped root lod is found in studiohwdata
	// this is stored here as a global store to ensure the staged loading matches the rendering
	byte				rootLOD;
	
	// set in the mdl data to specify that lod configuration should only allow first numAllowRootLODs
	// to be set as root LOD:
	//	numAllowedRootLODs = 0	means no restriction, any lod can be set as root lod.
	//	numAllowedRootLODs = N	means that lod0 - lod(N-1) can be set as root lod, but not lodN or lower.
	byte				numAllowedRootLODs;

	byte				unused[1];

	int					unused4; // zero out if version < 47

	int					numflexcontrollerui;
	int					flexcontrolleruiindex;
// 	mstudioflexcontrollerui_t *pFlexControllerUI( int i ) const { Assert( i >= 0 && i < numflexcontrollerui); return (mstudioflexcontrollerui_t *)(((byte *)this) + flexcontrolleruiindex) + i; }

	float				flVertAnimFixedPointScale;
// 	inline float		VertAnimFixedPointScale() const { return ( flags & STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE ) ? flVertAnimFixedPointScale : 1.0f / 4096.0f; }

	mutable int			surfacepropLookup;	// this index must be cached by the loader, not saved in the file

	// FIXME: Remove when we up the model version. Move all fields of studiohdr2_t into studiohdr_t.
	int					studiohdr2index;
// 	studiohdr2_t*		pStudioHdr2() const { return (studiohdr2_t *)( ( (byte *)this ) + studiohdr2index ); }

	// Src bone transforms are transformations that will convert .dmx or .smd-based animations into .mdl-based animations
//	int					NumSrcBoneTransforms() const { return studiohdr2index ? pStudioHdr2()->numsrcbonetransform : 0; }
// 	const mstudiosrcbonetransform_t* SrcBoneTransform( int i ) const { Assert( i >= 0 && i < NumSrcBoneTransforms()); return (mstudiosrcbonetransform_t *)(((byte *)this) + pStudioHdr2()->srcbonetransformindex) + i; }

// 	inline int			IllumPositionAttachmentIndex() const { return studiohdr2index ? pStudioHdr2()->IllumPositionAttachmentIndex() : 0; }

// 	inline float		MaxEyeDeflection() const { return studiohdr2index ? pStudioHdr2()->MaxEyeDeflection() : 0.866f; } // default to cos(30) if not set

// 	inline mstudiolinearbone_t *pLinearBones() const { return studiohdr2index ? pStudioHdr2()->pLinearBones() : NULL; }

// 	inline int			BoneFlexDriverCount() const { return studiohdr2index ? pStudioHdr2()->m_nBoneFlexDriverCount : 0; }
// 	inline const mstudioboneflexdriver_t* BoneFlexDriver( int i ) const { Assert( i >= 0 && i < BoneFlexDriverCount() ); return studiohdr2index ? pStudioHdr2()->pBoneFlexDriver( i ) : NULL; }

	// NOTE: No room to add stuff? Up the .mdl file format version 
	// [and move all fields in studiohdr2_t into studiohdr_t and kill studiohdr2_t],
	// or add your stuff to studiohdr2_t. See NumSrcBoneTransforms/SrcBoneTransform for the pattern to use.
	int					unused2[1];

	studiohdr_t_PS3() {}

private:
	// No copy constructors allowed
	studiohdr_t_PS3(const studiohdr_t_PS3& vOther);

	friend struct virtualmodel_t_PS3;
};


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------

//class IDataCache_PS3;
//class IMDLCache_PS3;

// class CStudioHdr_PS3
// {
// public:
// 	CStudioHdr_PS3( void );
// 	CStudioHdr_PS3( const studiohdr_t_PS3 *pStudioHdr, IMDLCache_PS3 *mdlcache = NULL );
// 	~CStudioHdr_PS3() { /*Term();*/ 	m_pActivityToSequence = NULL; }

// 	void Init( const studiohdr_t_PS3 *pStudioHdr, IMDLCache_PS3 *mdlcache = NULL );
// 	void Term();

// public:
// 	inline bool IsVirtual( void ) { return (m_pVModel != NULL); };
// 	inline bool IsValid( void ) { return (m_pStudioHdr != NULL); };
// 	inline bool IsReadyForAccess( void ) const { return (m_pStudioHdr != NULL); };
// 	inline virtualmodel_t		*GetVirtualModel( void ) const { return m_pVModel; };
// 	inline const studiohdr_t	*GetRenderHdr( void ) const { return m_pStudioHdr; };
// 	const studiohdr_t *pSeqStudioHdr( int sequence );
// 	const studiohdr_t *pAnimStudioHdr( int animation );
 
// private:
// 	mutable const studiohdr_t		*m_pStudioHdr;
// 	mutable virtualmodel_t	*m_pVModel;

// 	const virtualmodel_t * ResetVModel( const virtualmodel_t *pVModel ) const;
// 	const studiohdr_t *GroupStudioHdr( int group );
// 	mutable CUtlVector< const studiohdr_t_PS3 * > m_pStudioHdrCache;
// 
// 	mutable int			m_nFrameUnlockCounter;
// 	int	*				m_pFrameUnlockCounter;
// 	CThreadFastMutex_PS3	m_FrameUnlockCounterMutex;

// public:
// 	inline int			numbones( void ) const { return m_pStudioHdr->numbones; };
// 	inline mstudiobone_t *pBone( int i ) const { return m_pStudioHdr->pBone( i ); };
// 	int					RemapAnimBone( int iAnim, int iLocalBone ) const;		// maps local animations bone to global bone
// 	int					RemapSeqBone( int iSequence, int iLocalBone ) const;	// maps local sequence bone to global bone

// 	bool				SequencesAvailable() const;
// 	int					GetNumSeq_Internal( void ) const;
// 	inline int			GetNumSeq( void ) const
// 	{
// 		if ( !m_pVModel )
// 			return m_pStudioHdr->numlocalseq;
// 		return GetNumSeq_Internal();
// 	}

// 	mstudioanimdesc_t	&pAnimdesc_Internal( int i );
// 	inline mstudioanimdesc_t &pAnimdesc( int i )
// 	{
// 		if  ( !m_pVModel )
// 			return *m_pStudioHdr->pLocalAnimdesc( i );
// 		return pAnimdesc_Internal( i );
// 	}
//  	mstudioseqdesc_t_PS3	&pSeqdesc_Internal_PS3( int iSequence );
//  	inline mstudioseqdesc_t_PS3 &pSeqdesc_PS3( int iSequence )
//  	{
//  		if ( !m_pVModel )
//  			return *m_pStudioHdr->pLocalSeqdesc_PS3( iSequence );
//  
//  		return pSeqdesc_Internal_PS3( iSequence );
//  	}

// 	int					iRelativeAnim_Internal( int baseseq, int relanim ) const;	// maps seq local anim reference to global anim index
// 	inline int			iRelativeAnim( int baseseq, int relanim ) const
// 	{
// 		if ( !m_pVModel )
// 			return relanim;
// 		return iRelativeAnim_Internal( baseseq, relanim );
// 	}

// 	int					iRelativeSeq( int baseseq, int relseq ) const;		// maps seq local seq reference to global seq index

// 	int					GetSequenceActivity( int iSequence );
// 	void				SetSequenceActivity( int iSequence, int iActivity );
// 	int					GetActivityListVersion( void );
// 	void				SetActivityListVersion( int version );
// 	int					GetEventListVersion( void );
// 	void				SetEventListVersion( int version );
 
// 	int					GetNumAttachments( void ) const;
// 	const mstudioattachment_t &pAttachment( int i );
// 	int					GetAttachmentBone( int i );
	// used on my tools in hlmv, not persistant
// 	void				SetAttachmentBone( int iAttachment, int iBone );

// 	int					EntryNode( int iSequence );
// 	int					ExitNode( int iSequence );
// 	char				*pszNodeName( int iNode );
	// FIXME: where should this one be?
// 	int					GetTransition( int iFrom, int iTo ) const;

// 	int					GetNumPoseParameters( void ) const;
// 	const mstudioposeparamdesc_t &pPoseParameter( int i );
// 	int					GetSharedPoseParameter( int iSequence, int iLocalPose ) const;

// 	int					GetNumIKAutoplayLocks( void ) const;
// 	const mstudioiklock_t &pIKAutoplayLock( int i );

// 	inline int			CountAutoplaySequences() const { return m_pStudioHdr->CountAutoplaySequences(); };
// 	inline int			CopyAutoplaySequences( unsigned short *pOut, int outCount ) const { return m_pStudioHdr->CopyAutoplaySequences( pOut, outCount ); };
// 	inline int			GetAutoplayList( unsigned short **pOut ) const { return m_pStudioHdr->GetAutoplayList( pOut ); };

// 	inline int			GetNumBoneControllers( void ) const { return m_pStudioHdr->numbonecontrollers; };
// 	inline mstudiobonecontroller_t *pBonecontroller( int i ) const { return m_pStudioHdr->pBonecontroller( i ); };

// 	inline int			numikchains() const { return m_pStudioHdr->numikchains; };
// 	inline int			GetNumIKChains( void ) const { return m_pStudioHdr->numikchains; };
// 	inline mstudioikchain_t	*pIKChain( int i ) const { return m_pStudioHdr->pIKChain( i ); };

// 	inline int			numflexrules() const { return m_pStudioHdr->numflexrules; };
// 	inline mstudioflexrule_t *pFlexRule( int i ) const { return m_pStudioHdr->pFlexRule( i ); };

// 	inline int			numflexdesc() const{ return m_pStudioHdr->numflexdesc; };
// 	inline mstudioflexdesc_t *pFlexdesc( int i ) const { return m_pStudioHdr->pFlexdesc( i ); };

// 	inline LocalFlexController_t			numflexcontrollers() const{ return (LocalFlexController_t)m_pStudioHdr->numflexcontrollers; };
// 	inline mstudioflexcontroller_t *pFlexcontroller( LocalFlexController_t i ) const { return m_pStudioHdr->pFlexcontroller( i ); };

// 	inline int			numflexcontrollerui() const{ return m_pStudioHdr->numflexcontrollerui; };
// 	inline mstudioflexcontrollerui_t *pFlexcontrollerUI( int i ) const { return m_pStudioHdr->pFlexControllerUI( i ); };

// 	inline const char	*name() const { return m_pStudioHdr->pszName(); }; // deprecated -- remove after full xbox merge
// 	inline const char	*pszName() const { return m_pStudioHdr->pszName(); };

// 	inline int			numbonecontrollers() const { return m_pStudioHdr->numbonecontrollers; };

// 	inline int			numhitboxsets() const { return m_pStudioHdr->numhitboxsets; };
// 	inline mstudiohitboxset_t	*pHitboxSet( int i ) const { return m_pStudioHdr->pHitboxSet( i ); };

// 	inline mstudiobbox_t *pHitbox( int i, int set ) const { return m_pStudioHdr->pHitbox( i, set ); }; 
// 	inline int			iHitboxCount( int set ) const { return m_pStudioHdr->iHitboxCount( set ); };

// 	inline int			numbodyparts() const { return m_pStudioHdr->numbodyparts; };		
// 	inline mstudiobodyparts_t	*pBodypart( int i ) const { return m_pStudioHdr->pBodypart( i ); };

// 	inline int			numskinfamilies() const { return m_pStudioHdr->numskinfamilies; }

// 	inline Vector		eyeposition() const { return m_pStudioHdr->eyeposition; };

// 	inline int			flags() const { return m_pStudioHdr->flags; };

// 	inline char			*const pszSurfaceProp( void ) const { return m_pStudioHdr->pszSurfaceProp(); };
// 	inline int			GetSurfaceProp()const { return m_pStudioHdr->surfacepropLookup; }

// 	inline float		mass() const { return m_pStudioHdr->mass; };
// 	inline int			contents() const { return m_pStudioHdr->contents; }

// 	inline const byte	*GetBoneTableSortedByName() const { return m_pStudioHdr->GetBoneTableSortedByName(); };

// 	inline Vector		illumposition() const { return m_pStudioHdr->illumposition; };
	
// 	inline Vector		hull_min() const { return m_pStudioHdr->hull_min; };		// ideal movement hull size
// 	inline Vector		hull_max() const { return m_pStudioHdr->hull_max; };			

// 	inline Vector		view_bbmin() const { return m_pStudioHdr->view_bbmin; };		// clipping bounding box
// 	inline Vector		view_bbmax() const { return m_pStudioHdr->view_bbmax; };		

// 	inline int			numtextures() const { return m_pStudioHdr->numtextures; };

// 	inline int			IllumPositionAttachmentIndex() const { return m_pStudioHdr->IllumPositionAttachmentIndex(); }

// 	inline float		MaxEyeDeflection() const { return m_pStudioHdr->MaxEyeDeflection(); }

// 	inline mstudiolinearbone_t *pLinearBones() const { return m_pStudioHdr->pLinearBones(); }

// 	inline int			BoneFlexDriverCount() const { return m_pStudioHdr->BoneFlexDriverCount(); }
// 	inline const mstudioboneflexdriver_t *BoneFlexDriver( int i ) const { return m_pStudioHdr->BoneFlexDriver( i ); }

// public:
// 	int IsSequenceLooping( int iSequence );
// 	float GetSequenceCycleRate( int iSequence );

// 	void				RunFlexRules( const float *src, float *dest );
// 	void				RunFlexRulesOld( const float *src, float *dest );
// 	void				RunFlexRulesNew( const float *src, float *dest );


// public:
// 	inline int boneFlags( int iBone ) const { return m_boneFlags[ iBone ]; }
// 	inline void setBoneFlags( int iBone, int flags ) { m_boneFlags[ iBone ] |= flags; }
// 	inline void clearBoneFlags( int iBone, int flags ) { m_boneFlags[ iBone ] &= ~flags; }
// 	inline int boneParent( int iBone ) const { return m_boneParent[ iBone ]; }

// private:
// 	CUtlVector< int >  m_boneFlags;
// 	CUtlVector< int >  m_boneParent;

// public:

	// This class maps an activity to sequences allowed for that activity, accelerating the resolution
	// of SelectWeightedSequence(), especially on PowerPC. Iterating through every sequence
	// attached to a model turned out to be a very destructive cache access pattern on 360.
	// 
	// I've encapsulated this behavior inside a nested class for organizational reasons; there is
	// no particular programmatic or efficiency benefit to it. It just makes clearer what particular
	// code in the otherwise very complicated StudioHdr class has to do with this particular
	// optimization, and it lets you collapse the whole definition down to a single line in Visual
	// Studio.
// 	class CActivityToSequenceMapping_PS3 /* final */ 
// 	{
// 	public:
// 		// A tuple of a sequence and its corresponding weight. Lists of these correspond to activities.
// 		struct SequenceTuple_PS3
// 		{
// 			short		seqnum;
// 			short		weight; // the absolute value of the weight from the sequence header
// 			CUtlSymbol	*pActivityModifiers;		// list of activity modifier symbols
// 			int			iNumActivityModifiers;
// 		};

		// The type of the hash's stored data, a composite of both key and value
		// (because that's how CUtlHash works):
		// key: an int, the activity #
		// values: an index into the m_pSequenceTuples array, a count of the
		// total sequences present for an activity, and the sum of their
		// weights.
		// Note this struct is 128-bits wide, exactly coincident to a PowerPC 
		// cache line and VMX register. Please consider very carefully the
		// performance implications before adding any additional fields to this.
		// You could probably do away with totalWeight if you really had to.
// 		struct HashValueType_PS3
// 		{
// 			// KEY (hashed)
// 			int activityIdx; 
// 
// 			// VALUE (not hashed)
// 			int startingIdx;
// 			int count;
// 			int totalWeight;

// 			HashValueType(int _actIdx, int _stIdx, int _ct, int _tW) : 
// 			activityIdx(_actIdx), startingIdx(_stIdx), count(_ct), totalWeight(_tW) {}

			// default constructor (ought not to be actually used)
// 			HashValueType() : activityIdx(-1), startingIdx(-1), count(-1), totalWeight(-1) 
// 				{ AssertMsg(false, "Don't use default HashValueType()!"); }


// 			class HashFuncs_PS3
// 			{
// 			public:
// 				// dummy constructor (gndn)
// 				HashFuncs_PS3( int ) {}

				// COMPARE
				// compare two entries for uniqueness. We should never have two different
				// entries for the same activity, so we only compare the activity index;
				// this allows us to use the utlhash as a dict by constructing dummy entries
				// as hash lookup keys.
// 				bool operator()( const HashValueType &lhs, const HashValueType &rhs ) const
// 				{
// 					return lhs.activityIdx == rhs.activityIdx;
// 				}

				// HASH
				// We only hash on the activity index; everything else is data.
// 				unsigned int operator()( const HashValueType &item ) const
// 				{
// 					return HashInt( item.activityIdx );
// 				}
// 			};
// 		};

//  		typedef CUtlHash<HashValueType_PS3, HashValueType_PS3::HashFuncs_PS3, HashValueType_PS3::HashFuncs_PS3> ActivityToValueIdxHash_PS3;

		// These must be here because IFM does not compile/link studio.cpp (?!?)

		// ctor
// 		CActivityToSequenceMapping( void ) 
// 			: m_pSequenceTuples(NULL), m_iSequenceTuplesCount(0), m_ActToSeqHash(8,0,0), m_expectedVModel(NULL), m_pStudioHdr(NULL)
// 		{};

		// dtor -- not virtual because this class has no inheritors
// 		~CActivityToSequenceMapping()
// 		{	
// 			if ( m_pSequenceTuples != NULL )
// 			{
// 				if ( m_pSequenceTuples->pActivityModifiers != NULL )
// 				{
// 					delete[] m_pSequenceTuples->pActivityModifiers;
// 				}
// 				delete[] m_pSequenceTuples;
// 			}
// 		}

		/// Get the list of sequences for an activity. Returns the pointer to the
		/// first sequence tuple. Output parameters are a count of sequences present,
		/// and the total weight of all the sequences. (it would be more LHS-friendly
		/// to return these on registers, if only C++ offered more than one return 
		/// value....)
// 		const SequenceTuple *GetSequences( int forActivity, int *outSequenceCount, int *outTotalWeight );

		/// The number of sequences available for an activity.
// 		int NumSequencesForActivity( int forActivity );

// 		static CActivityToSequenceMapping *FindMapping( const CStudioHdr *pstudiohdr );
// 		static void ReleaseMapping( CActivityToSequenceMapping *pMap );
// 		static void ResetMappings();

// 	private:

		/// Allocate my internal array. (It is freed in the destructor.) Also,
		/// build the hash of activities to sequences and populate m_pSequenceTuples.
// 		void Initialize( const CStudioHdr *pstudiohdr );

		/// Force Initialize() to occur again, even if it has already occured.
// 		void Reinitialize( CStudioHdr *pstudiohdr );

		/// A more efficient version of the old SelectWeightedSequence() function in animation.cpp. 
// 		int SelectWeightedSequence( CStudioHdr *pstudiohdr, int activity, int curSequence );

		// selects the sequence with the most matching modifiers
// 		int SelectWeightedSequenceFromModifiers( CStudioHdr *pstudiohdr, int activity, CUtlSymbol *pActivityModifiers, int iModifierCount );

		// Actually a big array, into which the hash values index.
// 		SequenceTuple_PS3 *m_pSequenceTuples;
// 		unsigned int m_iSequenceTuplesCount; // (size of the whole array)

		// we don't store an outer pointer because we can't initialize it at construction time
		// (warning c4355) -- there are ways around this but it's easier to just pass in a 
		// pointer to the CStudioHdr when we need it, since this class isn't supposed to 
		// export its interface outside the studio header anyway.
		// CStudioHdr * const m_pOuter;

// 		ActivityToValueIdxHash_PS3 m_ActToSeqHash;
// 
// 		const studiohdr_t_PS3 *m_pStudioHdr;

		// we store these so we can know if the contents of the studiohdr have changed
		// from underneath our feet (this is an emergency data integrity check)
// 		const void *m_expectedVModel;


		// double-check that the data I point to hasn't changed
// 		bool ValidateAgainst( const CStudioHdr * RESTRICT pstudiohdr ) RESTRICT;
// 		void SetValidation( const CStudioHdr *RESTRICT pstudiohdr ) RESTRICT;

// 		friend class CStudioHdr_PS3;
// 	};

// 	CActivityToSequenceMapping_PS3 *m_pActivityToSequence;

// 	void InitActivityToSequence()
// 	{
// 		if ( !m_pActivityToSequence )
// 		{
// 			m_pActivityToSequence = CActivityToSequenceMapping::FindMapping( this );
// 		}
// 	}

	/// A more efficient version of the old SelectWeightedSequence() function in animation.cpp. 
	/// Returns -1 on failure to find a sequence
// 	inline int SelectWeightedSequence( int activity, int curSequence )
// 	{
// 		InitActivityToSequence();
// 		return m_pActivityToSequence->SelectWeightedSequence( this, activity, curSequence );
// 	}

// 	inline int SelectWeightedSequenceFromModifiers( int activity, CUtlSymbol *pActivityModifiers, int iModifierCount )
// 	{
// 		InitActivityToSequence();
// 		return m_pActivityToSequence->SelectWeightedSequenceFromModifiers( this, activity, pActivityModifiers, iModifierCount );
// 	}

	/// True iff there is at least one sequence for the given activity.
// 	inline bool HaveSequenceForActivity( int activity )	
// 	{
// 		InitActivityToSequence();
// 		return (m_pActivityToSequence->NumSequencesForActivity( activity ) > 0);
// 	}

	// Force this CStudioHdr's activity-to-sequence mapping to be reinitialized
// 	inline void ReinitializeSequenceMapping(void)
// 	{
// 		if ( m_pActivityToSequence )
// 		{
// 			CActivityToSequenceMapping::ReleaseMapping( m_pActivityToSequence );
// 			m_pActivityToSequence = NULL;
// 		}
// 		m_pActivityToSequence = CActivityToSequenceMapping::FindMapping( this );
// 	}

// #ifdef STUDIO_ENABLE_PERF_COUNTERS
// public:
// 	inline void			ClearPerfCounters_PS3( void )
// 	{
// 		m_nPerfAnimatedBones = 0;
// 		m_nPerfUsedBones = 0;
// 		m_nPerfAnimationLayers = 0;
// 	};
// 
// 	// timing info
// 	mutable	int			m_nPerfAnimatedBones;
// 	mutable	int			m_nPerfUsedBones;
// 	mutable	int			m_nPerfAnimationLayers;
// #endif


// };

/*
class CModelAccess
{
public:
	CModelAccess(CStudioHdr *pSemaphore)
	 : m_pStudioHdr(pSemaphore)
	{
		m_pStudioHdr->IncrementAccess();
	}
	
	~CModelAccess()
	{
		m_pStudioHdr->DecrementAccess();
	}
	
private:
	CStudioHdr *m_pStudioHdr;
};

#define ENABLE_MODEL_ACCESS( a ) \
	CModelAccess ModelAccess##__LINE__( a->m_pStudioHdr )
*/

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------

// struct flexweight_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					key;
// 	float				weight;
// 	float				influence;
// };
// 
// struct flexsetting_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					nameindex;
// 
// 	inline char *pszName( void ) const
// 	{ 
// 		return (char *)(((byte *)this) + nameindex); 
// 	}
// 
// 	// Leaving this for legacy support
// 	int					obsolete1;
// 
// 	// Number of flex settings
// 	int					numsettings;
// 	int					index;
// 	
// 	// OBSOLETE:  
// 	int					obsolete2;
// 
// 	// Index of start of contiguous array of flexweight_t structures
// 	int					settingindex;
// 
// 	//-----------------------------------------------------------------------------
// 	// Purpose: Retrieves a pointer to the flexweight_t, including resolving
// 	//  any markov chain hierarchy.  Because of this possibility, we return
// 	//  the number of settings in the weights array returned.  We'll generally
// 	//  call this function with i == 0
// 	// Input  : *base - 
// 	//			i - 
// 	//			**weights - 
// 	// Output : int
// 	//-----------------------------------------------------------------------------
// 	inline int psetting( byte *base, int i, flexweight_t **weights ) const;
// };
// 
// 
// struct flexsettinghdr_t
// {
// 	DECLARE_BYTESWAP_DATADESC();
// 	int					id;
// 	int					version;
// 
// 	inline const char * pszName( void ) const { return name; }
// 	char				name[64];
// 	int					length;
// 
// 	int					numflexsettings;
// 	int					flexsettingindex;
// 	inline flexsetting_t *pSetting( int i ) const { return (flexsetting_t *)(((byte *)this) + flexsettingindex) + i; };
// 	int					nameindex;
// 
// 	// look up flex settings by "index"
// 	int					numindexes;
// 	int					indexindex;
// 
// 	inline flexsetting_t *pIndexedSetting( int index ) const 
// 	{ 
// 		if ( index < 0 || index >= numindexes )
// 		{
// 			return NULL;
// 		}
// 
// 		int i = *((int *)(((byte *)this) + indexindex) + index);
// 		
// 		if (i == -1) 
// 		{
// 			return NULL;
// 		}
// 
// 		return pSetting( i );
// 	}
// 
// 	// index names of "flexcontrollers"
// 	int					numkeys;
// 	int					keynameindex;
// 	inline char			*pLocalName( int i ) const { return (char *)(((byte *)this) + *((int *)(((byte *)this) + keynameindex) + i)); };
// 
// 	int					keymappingindex;
// 	inline int			*pLocalToGlobal( int i ) const { return (int *)(((byte *)this) + keymappingindex) + i; };
// 	inline int			LocalToGlobal( int i ) const { return *pLocalToGlobal( i ); };
// };

//-----------------------------------------------------------------------------
// Purpose: Retrieves a pointer to the flexweight_t.  
// Input  : *base - flexsettinghdr_t * pointer
//			i - index of flex setting to retrieve
//			**weights - destination for weights array starting at index i.
// Output : int
//-----------------------------------------------------------------------------
// inline int flexsetting_t::psetting( byte *base, int i, flexweight_t **weights ) const
// { 
// 	// Grab array pointer
// 	*weights = (flexweight_t *)(((byte *)this) + settingindex) + i;
// 	// Return true number of settings
// 	return numsettings;
// };


//-----------------------------------------------------------------------------
// For a given flex controller ui struct, these return the index of the
// studiohdr_t flex controller that correspond to the the left and right
// flex controllers if the ui controller is a stereo control.
// nWayValueIndex returns the index of the flex controller that is the value
// flex controller for an NWAY combination
// If these functions are called and the ui controller isn't of the type
// specified then -1 is returned
//-----------------------------------------------------------------------------
// inline int mstudioflexcontrollerui_t::controllerIndex( const CStudioHdr &cStudioHdr ) const
// {
// 	return !stereo ? pController() - cStudioHdr.pFlexcontroller( (LocalFlexController_t)0 ) : -1;
// }
// 
// 
// inline int mstudioflexcontrollerui_t::rightIndex( const CStudioHdr &cStudioHdr ) const
// {
// 	return stereo ? pRightController() - cStudioHdr.pFlexcontroller( (LocalFlexController_t)0 ) : -1;
// }
// 
// 
// inline int mstudioflexcontrollerui_t::leftIndex( const CStudioHdr &cStudioHdr ) const
// {
// 	return stereo ? pLeftController() - cStudioHdr.pFlexcontroller((LocalFlexController_t) 0 ) : -1;
// }
// 
// 
// inline int mstudioflexcontrollerui_t::nWayValueIndex( const CStudioHdr &cStudioHdr ) const
// {
// 	return remaptype == FLEXCONTROLLER_REMAP_NWAY ? pNWayValueController() - cStudioHdr.pFlexcontroller( (LocalFlexController_t)0 ) : -1;
// }


// inline const mstudioflexcontroller_t *mstudioflexcontrollerui_t::pController( int index ) const
// {
// 	if ( index < 0 || index > Count() )
// 		return NULL;
// 
// 	if ( remaptype == FLEXCONTROLLER_REMAP_NWAY )
// 	{
// 		if ( stereo )
// 			return (mstudioflexcontroller_t *)( ( char * ) this ) + *( &szindex0 + index );
// 
// 		if ( index == 0 )
// 			return pController();
// 
// 		if ( index == 1 )
// 			return pNWayValueController();
// 
// 		return NULL;
// 	}
// 
// 	if ( index > 1 )
// 		return NULL;
// 
// 	if ( stereo )
// 		return (mstudioflexcontroller_t *)( ( char * ) this ) + *( &szindex0 + index );
// 
// 	if ( index > 0 )
// 		return NULL;
// 
// 	return pController();
// }


// #define STUDIO_CONST	1	// get float
// #define STUDIO_FETCH1	2	// get Flexcontroller value
// #define STUDIO_FETCH2	3	// get flex weight
// #define STUDIO_ADD		4
// #define STUDIO_SUB		5
// #define STUDIO_MUL		6
// #define STUDIO_DIV		7
// #define STUDIO_NEG		8	// not implemented
// #define STUDIO_EXP		9	// not implemented
// #define STUDIO_OPEN		10	// only used in token parsing
// #define STUDIO_CLOSE	11
// #define STUDIO_COMMA	12	// only used in token parsing
// #define STUDIO_MAX		13
// #define STUDIO_MIN		14
// #define STUDIO_2WAY_0	15	// Fetch a value from a 2 Way slider for the 1st value RemapVal( 0.0, 0.5, 0.0, 1.0 )
// #define STUDIO_2WAY_1	16	// Fetch a value from a 2 Way slider for the 2nd value RemapVal( 0.5, 1.0, 0.0, 1.0 )
// #define STUDIO_NWAY		17	// Fetch a value from a 2 Way slider for the 2nd value RemapVal( 0.5, 1.0, 0.0, 1.0 )
// #define STUDIO_COMBO	18	// Perform a combo operation (essentially multiply the last N values on the stack)
// #define STUDIO_DOMINATE	19	// Performs a combination domination operation
// #define STUDIO_DME_LOWER_EYELID 20	// 
// #define STUDIO_DME_UPPER_EYELID 21	// 
// 
// // motion flags
// #define STUDIO_X		0x00000001
// #define STUDIO_Y		0x00000002	
// #define STUDIO_Z		0x00000004
// #define STUDIO_XR		0x00000008
// #define STUDIO_YR		0x00000010
// #define STUDIO_ZR		0x00000020
// 
// #define STUDIO_LX		0x00000040
// #define STUDIO_LY		0x00000080
// #define STUDIO_LZ		0x00000100
// #define STUDIO_LXR		0x00000200
// #define STUDIO_LYR		0x00000400
// #define STUDIO_LZR		0x00000800
// 
// #define STUDIO_LINEAR	0x00001000
// 
// #define STUDIO_TYPES	0x0003FFFF
// #define STUDIO_RLOOP	0x00040000	// controller that wraps shortest distance
// 
// // sequence and autolayer flags
// #define STUDIO_LOOPING	0x0001		// ending frame should be the same as the starting frame
// #define STUDIO_SNAP		0x0002		// do not interpolate between previous animation and this one
// #define STUDIO_DELTA	0x0004		// this sequence "adds" to the base sequences, not slerp blends
// #define STUDIO_AUTOPLAY	0x0008		// temporary flag that forces the sequence to always play
// #define STUDIO_POST		0x0010		// 
// #define STUDIO_ALLZEROS	0x0020		// this animation/sequence has no real animation data
// #define STUDIO_FRAMEANIM 0x0040		// animation is encoded as by frame x bone instead of RLE bone x frame
// #define STUDIO_CYCLEPOSE 0x0080		// cycle index is taken from a pose parameter index
// #define STUDIO_REALTIME	0x0100		// cycle index is taken from a real-time clock, not the animations cycle index

// #define STUDIO_LOCAL	0x0200		// sequence has a local context sequence

// #define STUDIO_HIDDEN	0x0400		// don't show in default selection views
// #define STUDIO_OVERRIDE	0x0800		// a forward declared sequence (empty)
// #define STUDIO_ACTIVITY	0x1000		// Has been updated at runtime to activity index
// #define STUDIO_EVENT	0x2000		// Has been updated at runtime to event index on server
// #define STUDIO_WORLD	0x4000		// sequence blends in worldspace
// #define STUDIO_NOFORCELOOP 0x8000	// do not force the animation loop
// #define STUDIO_EVENT_CLIENT 0x10000	// Has been updated at runtime to event index on client

// autolayer flags
//							0x0001
//							0x0002
//							0x0004
//							0x0008
//#define STUDIO_AL_POST		0x0010		// 
//							0x0020
//#define STUDIO_AL_SPLINE	0x0040		// convert layer ramp in/out curve is a spline instead of linear
//#define STUDIO_AL_XFADE		0x0080		// pre-bias the ramp curve to compense for a non-1 weight, assuming a second layer is also going to accumulate
//							0x0100
//#define STUDIO_AL_NOBLEND	0x0200		// animation always blends at 1.0 (ignores weight)
//							0x0400
//							0x0800
//#define STUDIO_AL_LOCAL		0x1000		// layer is a local context sequence
//							0x2000
//#define STUDIO_AL_POSE		0x4000		// layer blends using a pose parameter instead of parent cycle


// Insert this code anywhere that you need to allow for conversion from an old STUDIO_VERSION to a new one.
// If we only support the current version, this function should be empty.
// inline bool Studio_ConvertStudioHdrToNewVersion( studiohdr_t *pStudioHdr )
// {
// 	COMPILE_TIME_ASSERT( STUDIO_VERSION == 49 ); //  put this to make sure this code is updated upon changing version.
// 
// 	int version = pStudioHdr->version;
// 	if ( version == STUDIO_VERSION )
// 		return true;
// 
// 	bool bResult = true;
// 	if (version < 46)
// 	{
// 		// some of the anim index data is incompatible
// 		for (int i = 0; i < pStudioHdr->numlocalanim; i++)
// 		{
// 			mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i );
// 
// 			// old ANI files that used sections (v45 only) are not compatible
// 			if ( pAnim->sectionframes != 0 )
// 			{
// 				// zero most everything out
// 				memset( &(pAnim->numframes), 0, (byte *)(pAnim + 1) - (byte *)&(pAnim->numframes) );
// 
// 				pAnim->numframes = 1;
// 				pAnim->animblock = -1; // disable animation fetching
// 				bResult = false;
// 			}
// 		}
// 	}
// 
// 	if (version < 47)
// 	{
// 		// now used to contain zeroframe cache data, make sure it's empty
// 		if (pStudioHdr->unused4 != 0)
// 		{
// 			pStudioHdr->unused4 = 0;
// 			bResult = false;
// 		}
// 		for (int i = 0; i < pStudioHdr->numlocalanim; i++)
// 		{
// 			mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i );
// 			pAnim->zeroframeindex = 0;
// 			pAnim->zeroframespan = 0;
// 		}
// 	} 
// 	else if (version == 47)
// 	{
// 		// clear out stale version of zeroframe cache data
// 		for (int i = 0; i < pStudioHdr->numlocalanim; i++)
// 		{
// 			mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i );
// 			if (pAnim->zeroframeindex != 0)
// 			{
// 				pAnim->zeroframeindex = 0;
// 				pAnim->zeroframespan = 0;
// 				bResult = false;
// 			}
// 		}
// 	}
// 
// 	if (version < 49)
// 	{
// 		// remove any frameanim flag settings that might be stale
// 		for (int i = 0; i < pStudioHdr->numlocalanim; i++)
// 		{
// 			mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i );
// 			if (pAnim->flags & STUDIO_FRAMEANIM)
// 			{
// 				pAnim->flags &= ~STUDIO_FRAMEANIM;
// 				bResult = false;
// 			}
// 		}
// 	}
// 	// for now, just slam the version number since they're compatible
// 	pStudioHdr->version = STUDIO_VERSION;
// 
// 	return bResult;
// }

// must be run to fixup with specified rootLOD
// inline void Studio_SetRootLOD( studiohdr_t *pStudioHdr, int rootLOD )
// {
// 	// honor studiohdr restriction of root lod in case requested root lod exceeds restriction.
// 	if ( pStudioHdr->numAllowedRootLODs > 0 &&
// 		 rootLOD >= pStudioHdr->numAllowedRootLODs )
// 	{
// 		rootLOD = pStudioHdr->numAllowedRootLODs - 1;
// 	}
// 
// 	// run the lod fixups that culls higher detail lods
// 	// vertexes are external, fixups ensure relative offsets and counts are cognizant of shrinking data
// 	// indexes are built in lodN..lod0 order so higher detail lod data can be truncated at load
// 	// the fixup lookup arrays are filled (or replicated) to ensure all slots valid
// 	int vertexindex  = 0;
// 	int tangentsindex = 0;
// 	int bodyPartID;
// 	for ( bodyPartID = 0; bodyPartID < pStudioHdr->numbodyparts; bodyPartID++ )
// 	{
// 		mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyPartID );
// 		int modelID;
// 		for ( modelID = 0; modelID < pBodyPart->nummodels; modelID++ )
// 		{
// 			mstudiomodel_t *pModel = pBodyPart->pModel( modelID );
// 			int totalMeshVertexes = 0;
// 			int meshID;
// 			for ( meshID = 0; meshID < pModel->nummeshes; meshID++ )
// 			{
// 				mstudiomesh_t *pMesh = pModel->pMesh( meshID );
// 
// 				// get the fixup, vertexes are reduced
// 				pMesh->numvertices  = pMesh->vertexdata.numLODVertexes[rootLOD];
// 				pMesh->vertexoffset = totalMeshVertexes;
// 				totalMeshVertexes += pMesh->numvertices;
// 			}
// 
// 			// stay in sync
// 			pModel->numvertices   = totalMeshVertexes;
// 			pModel->vertexindex   = vertexindex;
// 			pModel->tangentsindex = tangentsindex;
// 
// 			vertexindex   += totalMeshVertexes*sizeof(mstudiovertex_t);
// 			tangentsindex += totalMeshVertexes*sizeof(Vector4D);
// 		}
// 	}
// 
// 	// track the set desired configuration
// 	pStudioHdr->rootLOD = rootLOD;
// }

// Determines allocation requirements for vertexes
// inline int Studio_VertexDataSize( const vertexFileHeader_t *pVvdHdr, int rootLOD, bool bNeedsTangentS )
// {
// 	// the quantity of vertexes necessary for root lod and all lower detail lods
// 	// add one extra vertex to each section
// 	// the extra vertex allows prefetch hints to read ahead 1 vertex without faulting
// 	int numVertexes = pVvdHdr->numLODVertexes[rootLOD] + 1;
// 	int dataLength  = pVvdHdr->vertexDataStart + numVertexes*sizeof(mstudiovertex_t);
// 	if (bNeedsTangentS)
// 	{
// 		dataLength += numVertexes*sizeof(Vector4D);
// #ifdef _PS3
// 		if ( !pVvdHdr->numFixups && ( pVvdHdr->ps3edgeDmaInputDesc & 0x80000000 ) )
// 		{
// 			// PS3 does not support root lod, so all vertexes will be used
// 			dataLength = pVvdHdr->GetPs3EdgeDmaInputStart() + pVvdHdr->GetPs3EdgeDmaInputLength();
// 		}
// #endif
// 	}
// 
// 	// allocate this much
// 	return dataLength;
// }

// Load the minimum quantity of verts and run fixups
// inline int Studio_LoadVertexes( const vertexFileHeader_t *pTempVvdHdr, vertexFileHeader_t *pNewVvdHdr, int rootLOD, bool bNeedsTangentS )
// {
// 	int					i;
// 	int					target;
// 	int					numVertexes;
// 	vertexFileFixup_t	*pFixupTable;
// 
// 	numVertexes = pTempVvdHdr->numLODVertexes[rootLOD];
// 
// 	// copy all data up to start of vertexes
// 	memcpy((void*)pNewVvdHdr, (void*)pTempVvdHdr, pTempVvdHdr->vertexDataStart);
// 
// 	for ( i = 0; i < rootLOD; i++)
// 	{
// 		pNewVvdHdr->numLODVertexes[i] = pNewVvdHdr->numLODVertexes[rootLOD];
// 	}
// 
// 	// fixup data starts
// 	if (bNeedsTangentS)
// 	{
// 		// tangent data follows possibly reduced vertex data
// 		pNewVvdHdr->tangentDataStart = pNewVvdHdr->vertexDataStart + numVertexes*sizeof(mstudiovertex_t);
// 
// #ifdef _PS3
// 		// PS3 does not support root LOD, so all vertices will be available
// 		if ( !pTempVvdHdr->numFixups && ( pTempVvdHdr->ps3edgeDmaInputDesc & 0x80000000 ) )
// 		{
// 			pNewVvdHdr->ps3edgeDmaInputDesc = pTempVvdHdr->ps3edgeDmaInputDesc;
// 		}
// 		else
// 		{
// 			pNewVvdHdr->ps3edgeDmaInputDesc = 0;
// 		}
// #endif
// 	}
// 	else
// 	{
// 		// no tangent data will be available, mark for identification
// 		pNewVvdHdr->tangentDataStart = 0;
// 
// #ifdef _PS3
// 		pNewVvdHdr->ps3edgeDmaInputDesc = 0;
// #endif
// 	}
// 
// 	if (!pNewVvdHdr->numFixups)
// 	{		
// 		// fixups not required
// 		// transfer vertex data
// 		memcpy(
// 			(byte *)pNewVvdHdr+pNewVvdHdr->vertexDataStart, 
// 			(byte *)pTempVvdHdr+pTempVvdHdr->vertexDataStart,
// 			numVertexes*sizeof(mstudiovertex_t) );
// 
// 		if (bNeedsTangentS)
// 		{
// 			// transfer tangent data to cache memory
// 			memcpy(
// 				(byte *)pNewVvdHdr+pNewVvdHdr->tangentDataStart, 
// 				(byte *)pTempVvdHdr+pTempVvdHdr->tangentDataStart,
// 				numVertexes*sizeof(Vector4D) );
// 
// #ifdef _PS3
// 			if ( pNewVvdHdr->ps3edgeDmaInputDesc )
// 			{
// 				// transfer EDGE DMA INPUT to cache memory
// 				memcpy(
// 					const_cast< byte* >( pNewVvdHdr->GetPs3EdgeDmaInput() ),
// 					pTempVvdHdr->GetPs3EdgeDmaInput(),
// 					pTempVvdHdr->GetPs3EdgeDmaInputLength()
// 					);
// 			}
// #endif
// 		}
// 
// 		return numVertexes;
// 	}
// 
// 	// fixups required
// 	// re-establish mesh ordered vertexes into cache memory, according to table
// 	target      = 0;
// 	pFixupTable = (vertexFileFixup_t *)((byte *)pTempVvdHdr + pTempVvdHdr->fixupTableStart);
// 	for (i=0; i<pTempVvdHdr->numFixups; i++)
// 	{
// 		if (pFixupTable[i].lod < rootLOD)
// 		{
// 			// working bottom up, skip over copying higher detail lods
// 			continue;
// 		}
// 
// 		// copy vertexes
// 		memcpy(
// 			(mstudiovertex_t *)((byte *)pNewVvdHdr+pNewVvdHdr->vertexDataStart) + target,
// 			(mstudiovertex_t *)((byte *)pTempVvdHdr+pTempVvdHdr->vertexDataStart) + pFixupTable[i].sourceVertexID,
// 			pFixupTable[i].numVertexes*sizeof(mstudiovertex_t) );
// 
// 		if (bNeedsTangentS)
// 		{
// 			// copy tangents
// 			memcpy(
// 				(Vector4D *)((byte *)pNewVvdHdr+pNewVvdHdr->tangentDataStart) + target,
// 				(Vector4D *)((byte *)pTempVvdHdr+pTempVvdHdr->tangentDataStart) + pFixupTable[i].sourceVertexID,
// 				pFixupTable[i].numVertexes*sizeof(Vector4D) );
// 		}
// 
// 		// data is placed consecutively
// 		target += pFixupTable[i].numVertexes;
// 	}
// 
// 	pNewVvdHdr->numFixups = 0;
// 	pNewVvdHdr->fixupTableStart = 0; // otherwise PS3 will be confusing it for EDGE DMA INPUT
// 
// 	return target;
// }


#endif // STUDIO__PS3_H