#ifndef STUDIO_H
#define STUDIO_H

#ifdef _WIN32
#pragma once
#endif

#include "basetypes.h"
#include "vector2d.h"
#include "vector.h"
#include "vector4d.h"
#include "compressed_vector.h"
#include "dbg.h"
#include "threadtools.h"
#include "mathlib.h"
#include "utlvector.h"
#include "utlhash.h"
#include "datamap.h"
#include "generichash.h"
#include "localflexcontroller.h"
#include "utlsymbol.h"

#define STUDIO_ENABLE_PERF_COUNTERS

#define STUDIO_SEQUENCE_ACTIVITY_LOOKUPS_ARE_SLOW 0 
#define STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE 1

class IMaterial;
class IMesh;
class IMorph;
struct virtualmodel_t;
struct vertexFileHeader_t;
struct thinModelVertices_t;

namespace OptimizedModel
{
	struct StripHeader_t;
}


#define STUDIO_VERSION		48

#ifndef _XBOX
#define MAXSTUDIOTRIANGLES	65536	   
#define MAXSTUDIOVERTS		65536	   
#define	MAXSTUDIOFLEXVERTS	10000	              
#else
#define MAXSTUDIOTRIANGLES	25000
#define MAXSTUDIOVERTS		10000
#define	MAXSTUDIOFLEXVERTS	1000
#endif
#define MAXSTUDIOSKINS		32		  
#define MAXSTUDIOBONES		128		    
#define MAXSTUDIOFLEXDESC	1024	         
#define MAXSTUDIOFLEXCTRL	96		      
#define MAXSTUDIOPOSEPARAM	24
#define MAXSTUDIOBONECTRLS	4
#define MAXSTUDIOANIMBLOCKS 256

#define MAXSTUDIOBONEBITS	7		    

#define MAX_NUM_BONES_PER_VERT 3

#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

struct mstudioaxisinterpbone_t
{
	DECLARE_BYTESWAP_DATADESC();
	int				control;           
	int				axis;	   
	Vector			pos[6];	      
	Quaternion		quat[6];      

	mstudioaxisinterpbone_t() {}
private:
	mstudioaxisinterpbone_t(const mstudioaxisinterpbone_t& vOther);
};


struct mstudioquatinterpinfo_t
{
	DECLARE_BYTESWAP_DATADESC();
	float			inv_tolerance;	       
	Quaternion		trigger;	   
	Vector			pos;		  
	Quaternion		quat;		  

	mstudioquatinterpinfo_t() {}
private:
	mstudioquatinterpinfo_t(const mstudioquatinterpinfo_t& vOther);
};

struct mstudioquatinterpbone_t
{
	DECLARE_BYTESWAP_DATADESC();
	int				control;    
	int				numtriggers;
	int				triggerindex;
	inline mstudioquatinterpinfo_t* pTrigger(int i) const { return  (mstudioquatinterpinfo_t*)(((byte*)this) + triggerindex) + i; };

	mstudioquatinterpbone_t() {}
private:
	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
#define JIGGLE_IS_BOING					0x80		      

struct mstudiojigglebone_t
{
	DECLARE_BYTESWAP_DATADESC();

	int				flags;

	float			length;					         
	float			tipMass;

	float			yawStiffness;
	float			yawDamping;
	float			pitchStiffness;
	float			pitchDamping;
	float			alongStiffness;
	float			alongDamping;

	float			angleLimit;				      

	float			minYaw;					  
	float			maxYaw;					  
	float			yawFriction;
	float			yawBounce;

	float			minPitch;				  
	float			maxPitch;				  
	float			pitchFriction;
	float			pitchBounce;

	float			baseMass;
	float			baseStiffness;
	float			baseDamping;
	float			baseMinLeft;
	float			baseMaxLeft;
	float			baseLeftFriction;
	float			baseMinUp;
	float			baseMaxUp;
	float			baseUpFriction;
	float			baseMinForward;
	float			baseMaxForward;
	float			baseForwardFriction;

	float			boingImpactSpeed;
	float			boingImpactAngle;
	float			boingDampingRate;
	float			boingFrequency;
	float			boingAmplitude;

private:
};

struct mstudioaimatbone_t
{
	DECLARE_BYTESWAP_DATADESC();

	int				parent;
	int				aim;		     
	Vector			aimvector;
	Vector			upvector;
	Vector			basepos;

	mstudioaimatbone_t() {}
private:
	mstudioaimatbone_t(const mstudioaimatbone_t& vOther);
};

struct mstudiobone_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					sznameindex;
	inline char* const pszName(void) const { return ((char*)this) + sznameindex; }
	int		 			parent;		  
	int					bonecontroller[6];	      

	Vector				pos;
	Quaternion			quat;
	RadianEuler			rot;
	Vector				posscale;
	Vector				rotscale;

	matrix3x4_t			poseToBone;
	Quaternion			qAlignment;
	int					flags;
	int					proctype;
	int					procindex;		  
	mutable int			physicsbone;	     
	inline void* pProcedure() const { if (procindex == 0) return NULL; else return  (void*)(((byte*)this) + procindex); };
	int					surfacepropidx;	      
	inline char* const pszSurfaceProp(void) const { return ((char*)this) + surfacepropidx; }
	int					contents;		      

	int					unused[8];		   

	mstudiobone_t() {}
private:
	mstudiobone_t(const mstudiobone_t& vOther);
};

struct mstudiolinearbone_t
{
	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 Vector pos(int i) const { Assert(i >= 0 && i < numbones); return *((Vector*)(((byte*)this) + posindex) + i); };

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

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

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

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

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

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

	int unused[6];

	mstudiolinearbone_t() {}
private:
	mstudiolinearbone_t(const mstudiolinearbone_t& vOther);
};


enum StudioBoneFlexComponent_t
{
	STUDIO_BONE_FLEX_INVALID = -1,	 
	STUDIO_BONE_FLEX_TX = 0,		  
	STUDIO_BONE_FLEX_TY = 1,		  
	STUDIO_BONE_FLEX_TZ = 2			  
};


struct mstudioboneflexdrivercontrol_t
{
	DECLARE_BYTESWAP_DATADESC();

	int m_nBoneComponent;		      
	int m_nFlexControllerIndex;	    
	float m_flMin;				           
	float m_flMax;				           

	mstudioboneflexdrivercontrol_t() {}
private:
	mstudioboneflexdrivercontrol_t(const mstudioboneflexdrivercontrol_t& vOther);
};


struct mstudioboneflexdriver_t
{
	DECLARE_BYTESWAP_DATADESC();

	int m_nBoneIndex;			     
	int m_nControlCount;		      
	int m_nControlIndex;		         

	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:
	mstudioboneflexdriver_t(const mstudioboneflexdriver_t& vOther);
};


#define BONE_CALCULATE_MASK			0x1F
#define BONE_PHYSICALLY_SIMULATED	0x01	        
#define BONE_PHYSICS_PROCEDURAL		0x02	     
#define BONE_ALWAYS_PROCEDURAL		0x04	     
#define BONE_SCREEN_ALIGN_SPHERE	0x08	         
#define BONE_SCREEN_ALIGN_CYLINDER	0x10	          

#define BONE_USED_MASK				0x0007FF00
#define BONE_USED_BY_ANYTHING		0x0007FF00
#define BONE_USED_BY_HITBOX			0x00000100	         
#define BONE_USED_BY_ATTACHMENT		0x00000200	         
#define BONE_USED_BY_VERTEX_MASK	0x0003FC00
#define BONE_USED_BY_VERTEX_LOD0	0x00000400	            
#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	          

#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	             

#define BONE_HAS_SAVEFRAME_POS		0x00200000	 
#define BONE_HAS_SAVEFRAME_ROT		0x00400000	 

struct mstudiobonecontroller_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					bone;	   
	int					type;	       
	float				start;
	float				end;
	int					rest;	     
	int					inputfield;	      
	int					unused[8];
};

struct mstudiobbox_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					bone;
	int					group;				  
	Vector				bbmin;				  
	Vector				bbmax;
	int					szhitboxnameindex;	       
	int					unused[8];

	const char* pszHitboxName()
	{
		if (szhitboxnameindex == 0)
			return "";

		return ((const char*)this) + szhitboxnameindex;
	}

	mstudiobbox_t() {}

private:
	mstudiobbox_t(const mstudiobbox_t& vOther);
};

struct mstudiomodelgroup_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					szlabelindex;	  
	inline char* const pszLabel(void) const { return ((char*)this) + szlabelindex; }
	int					sznameindex;	  
	inline char* const pszName(void) const { return ((char*)this) + sznameindex; }
};

struct mstudiomodelgrouplookup_t
{
	int					modelgroup;
	int					indexwithingroup;
};

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

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

#define IK_SELF 1
#define IK_WORLD 2
#define IK_GROUND 3
#define IK_RELEASE 4
#define IK_ATTACHMENT 5
#define IK_UNLATCH 6

struct mstudioikerror_t
{
	DECLARE_BYTESWAP_DATADESC();
	Vector		pos;
	Quaternion	q;

	mstudioikerror_t() {}

private:
	mstudioikerror_t(const mstudioikerror_t& vOther);
};

union mstudioanimvalue_t;

struct mstudiocompressedikerror_t
{
	DECLARE_BYTESWAP_DATADESC();
	float	scale[6];
	short	offset[6];
	inline mstudioanimvalue_t* pAnimvalue(int i) const { if (offset[i] > 0) return  (mstudioanimvalue_t*)(((byte*)this) + offset[i]); else return NULL; };
	mstudiocompressedikerror_t() {}

private:
	mstudiocompressedikerror_t(const mstudiocompressedikerror_t& vOther);
};

struct mstudioikrule_t
{
	DECLARE_BYTESWAP_DATADESC();
	int			index;

	int			type;
	int			chain;

	int			bone;

	int			slot;	       
	float		height;
	float		radius;
	float		floor;
	Vector		pos;
	Quaternion	q;

	int			compressedikerrorindex;
	inline mstudiocompressedikerror_t* pCompressedError() const { return (mstudiocompressedikerror_t*)(((byte*)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; };

	float		start;	   
	float		peak;	    
	float		tail;	    
	float		end;	    

	float		unused3;	 
	float		contact;	     
	float		drop;		           
	float		top;		     

	int			unused6;
	int			unused7;
	int			unused8;

	int			szattachmentindex;		    
	inline char* const pszAttachment(void) const { return ((char*)this) + szattachmentindex; }

	int			unused[7];

	mstudioikrule_t() {}

private:
	mstudioikrule_t(const mstudioikrule_t& vOther);
};


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

	int			unused[4];
};


struct mstudiolocalhierarchy_t
{
	DECLARE_BYTESWAP_DATADESC();
	int			iBone;			   
	int			iNewParent;		    

	float		start;			   
	float		peak;			    
	float		tail;			    
	float		end;			    

	int			iStart;			   

	int			localanimindex;
	inline mstudiocompressedikerror_t* pLocalAnim() const { return (mstudiocompressedikerror_t*)(((byte*)this) + localanimindex); };

	int			unused[4];
};



union mstudioanimvalue_t
{
	struct
	{
		byte	valid;
		byte	total;
	} num;
	short		value;
};

struct mstudioanim_valueptr_t
{
	DECLARE_BYTESWAP_DATADESC();
	short	offset[3];
	inline mstudioanimvalue_t* pAnimvalue(int i) const { if (offset[i] > 0) return  (mstudioanimvalue_t*)(((byte*)this) + offset[i]); else return NULL; };
};

#define STUDIO_ANIM_RAWPOS	0x01  
#define STUDIO_ANIM_RAWROT	0x02  
#define STUDIO_ANIM_ANIMPOS	0x04  
#define STUDIO_ANIM_ANIMROT	0x08  
#define STUDIO_ANIM_DELTA	0x10
#define STUDIO_ANIM_RAWROT2	0x20  


struct mstudioanim_t
{
	DECLARE_BYTESWAP_DATADESC();
	byte				bone;
	byte				flags;		  

	inline byte* pData(void) const { return (((byte*)this) + sizeof(struct mstudioanim_t)); };
	inline mstudioanim_valueptr_t* pRotV(void) const { return (mstudioanim_valueptr_t*)(pData()); };
	inline mstudioanim_valueptr_t* pPosV(void) const { return (mstudioanim_valueptr_t*)(pData()) + ((flags & STUDIO_ANIM_ANIMROT) != 0); };

	inline Quaternion48* pQuat48(void) const { return (Quaternion48*)(pData()); };
	inline Quaternion64* pQuat64(void) const { return (Quaternion64*)(pData()); };
	inline Vector48* pPos(void) const { return (Vector48*)(pData() + ((flags & STUDIO_ANIM_RAWROT) != 0) * sizeof(*pQuat48()) + ((flags & STUDIO_ANIM_RAWROT2) != 0) * sizeof(*pQuat64())); };

	short				nextoffset;
	inline mstudioanim_t* pNext(void) const { if (nextoffset != 0) return  (mstudioanim_t*)(((byte*)this) + nextoffset); else return NULL; };
};

struct mstudiomovement_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					endframe;
	int					motionflags;
	float				v0;			     
	float				v1;			     
	float				angle;		        
	Vector				vector;		        
	Vector				position;	     

	mstudiomovement_t() {}
private:
	mstudiomovement_t(const mstudiomovement_t& vOther);
};

struct studiohdr_t;

struct mstudioanimblock_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					datastart;
	int					dataend;
};

struct mstudioanimsections_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					animblock;
	int					animindex;
};

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

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

	float				fps;		   	
	int					flags;		  

	int					numframes;

	int					nummovements;
	int					movementindex;
	inline mstudiomovement_t* const pMovement(int i) const { return (mstudiomovement_t*)(((byte*)this) + movementindex) + i; };

	int					unused1[6];			         	

	int					animblock;
	int					animindex;	        
	mstudioanim_t* pAnimBlock(int block, int index) const;           
	mstudioanim_t* pAnim(int* piFrame, float& flStall) const;         
	mstudioanim_t* pAnim(int* piFrame) const;         

	int					numikrules;
	int					ikruleindex;	         
	int					animblockikruleindex;          
	mstudioikrule_t* pIKRule(int i) const;

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

	int					sectionindex;
	int					sectionframes;              
	inline mstudioanimsections_t* const pSection(int i) const { return (mstudioanimsections_t*)(((byte*)this) + sectionindex) + i; }

	short				zeroframespan;	   
	short				zeroframecount;    
	int					zeroframeindex;
	byte* pZeroFrameData() const { if (zeroframeindex) return (((byte*)this) + zeroframeindex); else return NULL; };
	mutable float		zeroframestalltime;		    

	mstudioanimdesc_t() {}
private:
	mstudioanimdesc_t(const mstudioanimdesc_t& vOther);
};

struct mstudioikrule_t;

struct mstudioautolayer_t
{
	DECLARE_BYTESWAP_DATADESC();
	short				iSequence;
	short				iPose;
	int					flags;
	float				start;	   
	float				peak;	    
	float				tail;	    
	float				end;	    
};

struct mstudioactivitymodifier_t
{
	DECLARE_BYTESWAP_DATADESC();

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

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

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

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

	int					flags;		  

	int					activity;	       
	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; };

	Vector				bbmin;		    
	Vector				bbmax;

	int					numblends;

	int					animindexindex;

	inline int			anim(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*)this) + animindexindex);
		int value = (int)blends[offset];
		return value;
	}

	int					movementindex;	      
	int					groupsize[2];
	int					paramindex[2];	      
	float				paramstart[2];	    
	float				paramend[2];	    
	int					paramparent;

	float				fadeintime;		       
	float				fadeouttime;	       

	int					localentrynode;		    
	int					localexitnode;		    
	int					nodeflags;		  

	float				entryphase;		     
	float				exitphase;		     

	float				lastframe;		     

	int					nextseq;		   
	int					pose;			        

	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; };

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

	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* pIKLock(int i) const { Assert(i >= 0 && i < numiklocks); return (mstudioiklock_t*)(((byte*)this) + iklockindex) + i; };

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

	int					cycleposeindex;		         

	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];		           

	mstudioseqdesc_t() {}
private:
	mstudioseqdesc_t(const mstudioseqdesc_t& vOther);
};


struct mstudioposeparamdesc_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					sznameindex;
	inline char* const pszName(void) const { return ((char*)this) + sznameindex; }
	int					flags;	 
	float				start;	  
	float				end;	  
	float				loop;	          
};

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;	       
	float				min;
	float				max;
};


enum FlexControllerRemapType_t
{
	FLEXCONTROLLER_REMAP_PASSTHRU = 0,
	FLEXCONTROLLER_REMAP_2WAY,	                
	FLEXCONTROLLER_REMAP_NWAY,	                             
	FLEXCONTROLLER_REMAP_EYELID
};


class CStudioHdr;
struct mstudioflexcontrollerui_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					sznameindex;
	inline char* const pszName(void) const { return ((char*)this) + sznameindex; }

	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;

	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;	    
	bool				stereo;		     
	byte				unused[2];
};


struct mstudiovertanim_t
{
	DECLARE_BYTESWAP_DATADESC();
	unsigned short		index;
	byte				speed;	 
	byte				side;	 

protected:
	union
	{
		short			delta[3];
		float16			flDelta[3];
	};

	union
	{
		short			ndelta[3];
		float16			flNDelta[3];
	};

public:
	inline void ConvertToFixed(float flVertAnimFixedPointScale)
	{
		delta[0] = flDelta[0].GetFloat() / flVertAnimFixedPointScale;
		delta[1] = flDelta[1].GetFloat() / flVertAnimFixedPointScale;
		delta[2] = flDelta[2].GetFloat() / flVertAnimFixedPointScale;
		ndelta[0] = flNDelta[0].GetFloat() / flVertAnimFixedPointScale;
		ndelta[1] = flNDelta[1].GetFloat() / flVertAnimFixedPointScale;
		ndelta[2] = 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] = vInput.x / flVertAnimFixedPointScale;
		delta[1] = vInput.y / flVertAnimFixedPointScale;
		delta[2] = vInput.z / flVertAnimFixedPointScale;
	}
	inline void SetNDeltaFixed(const Vector& vInputNormal, float flVertAnimFixedPointScale)
	{
		ndelta[0] = vInputNormal.x / flVertAnimFixedPointScale;
		ndelta[1] = vInputNormal.y / flVertAnimFixedPointScale;
		ndelta[2] = vInputNormal.z / flVertAnimFixedPointScale;
	}

	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() {}
};


struct mstudiovertanim_wrinkle_t : public mstudiovertanim_t
{
	DECLARE_BYTESWAP_DATADESC();

	short	wrinkledelta;

	inline void SetWrinkleFixed(float flWrinkle, float flVertAnimFixedPointScale)
	{
		int nWrinkleDeltaInt = 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;	  

	float				target0;	 
	float				target1;	 
	float				target2;	 
	float				target3;	 

	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;	   
	unsigned char		vertanimtype;	  
	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; };
};

struct mstudioboneweight_t
{
	DECLARE_BYTESWAP_DATADESC();
	float	weight[MAX_NUM_BONES_PER_VERT];
	char	bone[MAX_NUM_BONES_PER_VERT];
	byte	numbones;

};

struct mstudiovertex_t
{
	DECLARE_BYTESWAP_DATADESC();
	mstudioboneweight_t	m_BoneWeights;
	Vector				m_vecPosition;
	Vector				m_vecNormal;
	Vector2D			m_vecTexCoord;

	mstudiovertex_t() {}

private:
	mstudiovertex_t(const mstudiovertex_t& vOther);
};

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;                   
	mutable void* clientmaterial;	        

	int						unused[10];
};

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];	             
	int		lowerflexdesc[3];
	float	uppertarget[3];		         
	float	lowertarget[3];

	int		upperlidflexdesc;	          
	int		lowerlidflexdesc;
	int		unused[4];			          
	bool	m_bNonFACS;			     
	char	unused3[3];
	int		unused4[7];

	mstudioeyeball_t() {}
private:
	mstudioeyeball_t(const mstudioeyeball_t& vOther);
};


struct mstudioiklink_t
{
	DECLARE_BYTESWAP_DATADESC();
	int		bone;
	Vector	kneeDir;	       
	Vector	unused0;	 

	mstudioiklink_t() {}
private:
	mstudioiklink_t(const mstudioiklink_t& vOther);
};

struct mstudioikchain_t
{
	DECLARE_BYTESWAP_DATADESC();
	int				sznameindex;
	inline char* const pszName(void) const { return ((char*)this) + sznameindex; }
	int				linktype;
	int				numlinks;
	int				linkindex;
	inline mstudioiklink_t* pLink(int i) const { return (mstudioiklink_t*)(((byte*)this) + linkindex) + i; };
};


struct mstudioiface_t
{
	unsigned short a, b, c;		   
};


struct mstudiomodel_t;

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;

	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;

	const mstudio_modelvertexdata_t* modelvertexdata;

	int					numLODVertexes[MAX_NUM_LODS];
};

struct mstudiomesh_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					material;

	int					modelindex;
	mstudiomodel_t* pModel() const;

	int					numvertices;		    
	int					vertexoffset;		  

	const mstudio_meshvertexdata_t* GetVertexData(void* pModelData = NULL);
	const thinModelVertices_t* GetThinVertexData(void* pModelData = NULL);

	int					numflexes;			  
	int					flexindex;
	inline mstudioflex_t* pFlex(int i) const { return (mstudioflex_t*)(((byte*)this) + flexindex) + i; };

	int					materialtype;
	int					materialparam;

	int					meshid;

	Vector				center;

	mstudio_meshvertexdata_t vertexdata;

	int					unused[8];    

	mstudiomesh_t() {}
private:
	mstudiomesh_t(const mstudiomesh_t& vOther);
};

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; };

	int					numvertices;		    
	int					vertexindex;		  
	int					tangentsindex;		  

	const vertexFileHeader_t* CacheVertexData(void* pModelData);

	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];		   
};

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
{
	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)
{
	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)
{
	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));
}

enum studiomeshgroupflags_t
{
	MESHGROUP_IS_FLEXED = 0x1,
	MESHGROUP_IS_HWSKINNED = 0x2,
	MESHGROUP_IS_DELTA_FLEXED = 0x4
};


struct studiomeshgroup_t
{
	IMesh* m_pMesh;
	int				m_NumStrips;
	int				m_Flags;		  
	OptimizedModel::StripHeader_t* m_pStripData;
	unsigned short* m_pGroupIndexToMeshIndex;
	int				m_NumVertices;
	int* m_pUniqueTris;	   
	unsigned short* m_pIndices;
	bool			m_MeshNeedsRestore;
	short			m_ColorMeshID;
	IMorph* m_pMorph;

	inline unsigned short MeshIndex(int i) const { return m_pGroupIndexToMeshIndex[m_pIndices[i]]; }
};


struct studiomeshdata_t
{
	int					m_NumGroup;
	studiomeshgroup_t* m_pMeshGroup;
};

struct studioloddata_t
{
	studiomeshdata_t* m_pMeshData;      
	float				m_SwitchPoint;
	int					numMaterials;
	IMaterial** ppMaterials;       
	int* pMaterialFlags;       

	int* m_pHWMorphDecalBoneRemap;
	int					m_nDecalBoneCount;
};

struct studiohwdata_t
{
	int					m_RootLOD;	       
	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
	{
		if (!m_NumLODs)
			return 0;

		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 > lodMetric)
				return i;
		}

		return numLODs - 1;
	}
};

struct mstudiobodyparts_t
{
	DECLARE_BYTESWAP_DATADESC();
	int					sznameindex;
	inline char* const pszName(void) const { return ((char*)this) + sznameindex; }
	int					nummodels;
	int					base;
	int					modelindex;     
	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:
	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; };
};


struct mstudiosrcbonetransform_t
{
	DECLARE_BYTESWAP_DATADESC();

	int			sznameindex;
	inline const char* pszName(void) const { return ((char*)this) + sznameindex; }
	matrix3x4_t	pretransform;
	matrix3x4_t	posttransform;
};


class virtualgroup_t
{
public:
	virtualgroup_t(void) { cache = NULL; };
	void* cache;
	const studiohdr_t* GetStudioHdr(void) const;

	CUtlVector< int > boneMap;				      
	CUtlVector< int > masterBone;			      
	CUtlVector< int > masterSeq;			      
	CUtlVector< int > masterAnim;			      
	CUtlVector< int > masterAttachment;	     
	CUtlVector< int > masterPose;			      
	CUtlVector< int > masterNode;			      
};

struct virtualsequence_t
{
#ifdef _XBOX
	short flags;
	short activity;
	short group;
	short index;
#else
	int	flags;
	int activity;
	int group;
	int index;
#endif
};

struct virtualgeneric_t
{
#ifdef _XBOX
	short group;
	short index;
#else
	int group;
	int index;
#endif
};


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

	virtualgroup_t* pAnimGroup(int animation) { return &m_group[m_anim[animation].group]; }        
	virtualgroup_t* pSeqGroup(int sequence)
	{
		if ((unsigned)sequence >= (unsigned)m_seq.Count())
		{
			Assert(0);
			return 0;
		}
		return &m_group[m_seq[sequence].group];
	}        

	CThreadFastMutex m_Lock;

	CUtlVector< virtualsequence_t > m_seq;
	CUtlVector< virtualgeneric_t > m_anim;
	CUtlVector< virtualgeneric_t > m_attachment;
	CUtlVector< virtualgeneric_t > m_pose;
	CUtlVector< virtualgroup_t > m_group;
	CUtlVector< virtualgeneric_t > m_node;
	CUtlVector< virtualgeneric_t > m_iklock;
	CUtlVector< unsigned short > m_autoplaySequences;
};

struct thinModelVertices_t
{
	void Init(int numBoneInfluences, Vector* positions, unsigned short* normals, float* boneWeights, char* 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;
		char* 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* 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* pBaseWeight = m_boneWeights + vertIndex * numStoredWeights;
		char* pBaseIndex = m_boneIndices + vertIndex * m_numBoneInfluences;
		float  sum = 0.0f;
		for (int i = 0; i < MAX_NUM_BONES_PER_VERT; i++)
		{
			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;
		}

		pBoneWeights->numbones = m_numBoneInfluences ? m_numBoneInfluences : 1;
	}

	int				m_numBoneInfluences;       
	float* m_boneWeights;		           
	char* m_boneIndices;		       
	Vector* m_vecPositions;
	unsigned short* m_vecNormals;		          
};

#define MODEL_VERTEX_FILE_ID		(('V'<<24)+('S'<<16)+('D'<<8)+'I')
#define MODEL_VERTEX_FILE_VERSION	4
#define MODEL_VERTEX_FILE_THIN_ID	(('V'<<24)+('C'<<16)+('D'<<8)+'I')

struct vertexFileHeader_t
{
	DECLARE_BYTESWAP_DATADESC();
	int		id;								 
	int		version;						 
	int		checksum;						     
	int		numLODs;						    
	int		numLODVertexes[MAX_NUM_LODS];	      
	int		numFixups;						   
	int		fixupTableStart;				      
	int		vertexDataStart;				      
	int		tangentDataStart;				      

public:

	const mstudiovertex_t* GetVertexData() const
	{
		if ((id == MODEL_VERTEX_FILE_ID) && (vertexDataStart != 0))
			return (mstudiovertex_t*)(vertexDataStart + (byte*)this);
		else
			return NULL;
	}
	const Vector4D* GetTangentData() const
	{
		if ((id == MODEL_VERTEX_FILE_ID) && (tangentDataStart != 0))
			return (Vector4D*)(tangentDataStart + (byte*)this);
		else
			return NULL;
	}
	const  thinModelVertices_t* GetThinVertexData() const
	{
		if ((id == MODEL_VERTEX_FILE_THIN_ID) && (vertexDataStart != 0))
			return (thinModelVertices_t*)(vertexDataStart + (byte*)this);
		else
			return NULL;
	}
};

inline const mstudio_modelvertexdata_t* mstudiomodel_t::GetVertexData(void* pModelData)
{
	const vertexFileHeader_t* pVertexHdr = CacheVertexData(pModelData);
	if (!pVertexHdr)
	{
		vertexdata.pVertexData = NULL;
		vertexdata.pTangentData = NULL;
		return NULL;
	}

	vertexdata.pVertexData = pVertexHdr->GetVertexData();
	vertexdata.pTangentData = pVertexHdr->GetTangentData();

	if (!vertexdata.pVertexData)
		return NULL;

	return &vertexdata;
}

inline const thinModelVertices_t* mstudiomodel_t::GetThinVertexData(void* pModelData)
{
	const vertexFileHeader_t* pVertexHdr = CacheVertexData(pModelData);
	if (!pVertexHdr)
		return NULL;

	return pVertexHdr->GetThinVertexData();
}

struct vertexFileFixup_t
{
	DECLARE_BYTESWAP_DATADESC();
	int		lod;				      
	int		sourceVertexID;		       
	int		numVertexes;
};

#define STUDIOHDR_FLAGS_AUTOGENERATED_HITBOX				0x00000001

#define STUDIOHDR_FLAGS_USES_ENV_CUBEMAP					0x00000002

#define STUDIOHDR_FLAGS_FORCE_OPAQUE						0x00000004

#define STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS					0x00000008

#define STUDIOHDR_FLAGS_STATIC_PROP							0x00000010

#define STUDIOHDR_FLAGS_USES_FB_TEXTURE						0x00000020

#define STUDIOHDR_FLAGS_HASSHADOWLOD						0x00000040

#define STUDIOHDR_FLAGS_USES_BUMPMAPPING					0x00000080

#define STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS				0x00000100

#define STUDIOHDR_FLAGS_OBSOLETE							0x00000200

#define STUDIOHDR_FLAGS_UNUSED								0x00000400

#define STUDIOHDR_FLAGS_NO_FORCED_FADE						0x00000800

#define STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE				0x00001000

#define STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT		0x00002000

#define STUDIOHDR_FLAGS_FLEXES_CONVERTED					0x00004000

#define STUDIOHDR_FLAGS_BUILT_IN_PREVIEW_MODE				0x00008000

#define STUDIOHDR_FLAGS_AMBIENT_BOOST						0x00010000

#define STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS					0x00020000

#define STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS				0x00040000


#define STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE			0x00200000

struct studiohdr2_t
{
	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; }       

	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
{
	DECLARE_BYTESWAP_DATADESC();
	int					id;
	int					version;
	int					checksum;		              
	inline const char* pszName(void) const { if (studiohdr2index && pStudioHdr2()->pszName()) return pStudioHdr2()->pszName(); else return name; }
	char				name[64];
	int					length;
	Vector				eyeposition;	   
	Vector				illumposition;	  
	Vector				hull_min;		    
	Vector				hull_max;
	Vector				view_bbmin;		   
	Vector				view_bbmax;
	int					flags;
	int					numbones;			 
	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;	       
	int					RemapAnimBone(int iAnim, int iLocalBone) const;		       
	int					numbonecontrollers;		  
	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;

	mstudiohitboxset_t* pHitboxSet(int i) const
	{
		Assert(i >= 0 && i < numhitboxsets);
		return (mstudiohitboxset_t*)(((byte*)this) + hitboxsetindex) + i;
	};

	inline mstudiobbox_t* pHitbox(int i, int set) const
	{
		mstudiohitboxset_t const* s = pHitboxSet(set);
		if (!s)
			return NULL;

		return s->pHitbox(i);
	};

	inline int			iHitboxCount(int set) const
	{
		mstudiohitboxset_t const* s = pHitboxSet(set);
		if (!s)
			return 0;

		return s->numhitboxes;
	};


	int					numlocalanim;			 
	int					localanimindex;		  
	inline mstudioanimdesc_t* pLocalAnimdesc(int i) const { if (i < 0 || i >= numlocalanim) i = 0; return (mstudioanimdesc_t*)(((byte*)this) + localanimindex) + i; };

	int					numlocalseq;				 
	int					localseqindex;
	inline mstudioseqdesc_t* pLocalSeqdesc(int i) const { if (i < 0 || i >= numlocalseq) i = 0; return (mstudioseqdesc_t*)(((byte*)this) + localseqindex) + i; };

	bool				SequencesAvailable() const;
	int					GetNumSeq() const;
	mstudioanimdesc_t& pAnimdesc(int i) const;
	mstudioseqdesc_t& pSeqdesc(int i) const;
	int					iRelativeAnim(int baseseq, int relanim) const;	         
	int					iRelativeSeq(int baseseq, int relseq) const;		         

	mutable int			activitylistversion;	        
	mutable int			eventsindexed;
	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);

	int					numtextures;
	int					textureindex;
	inline mstudiotexture_t* pTexture(int i) const { Assert(i >= 0 && i < numtextures); return (mstudiotexture_t*)(((byte*)this) + textureindex) + i; };


	int					numcdtextures;
	int					cdtextureindex;
	inline char* pCdtexture(int i) const { return (((char*)this) + *((int*)(((byte*)this) + cdtextureindex) + i)); };

	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; };

	int					numlocalattachments;
	int					localattachmentindex;
	inline mstudioattachment_t* pLocalAttachment(int i) const { Assert(i >= 0 && i < numlocalattachments); return (mstudioattachment_t*)(((byte*)this) + localattachmentindex) + i; };
	int					GetNumAttachments(void) const;
	const mstudioattachment_t& pAttachment(int i) const;
	int					GetAttachmentBone(int i);
	void				SetAttachmentBone(int iAttachment, int iBone);

	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; };

	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(numflexcontrollers == 0 || (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; };

	int					numlocalposeparameters;
	int					localposeparamindex;
	inline mstudioposeparamdesc_t* pLocalPoseParameter(int i) const { Assert(i >= 0 && i < numlocalposeparameters); return (mstudioposeparamdesc_t*)(((byte*)this) + localposeparamindex) + i; };
	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; }

	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;

	float				mass;
	int					contents;

	int					numincludemodels;
	int					includemodelindex;
	inline mstudiomodelgroup_t* pModelGroup(int i) const { Assert(i >= 0 && i < numincludemodels); return (mstudiomodelgroup_t*)(((byte*)this) + includemodelindex) + i; };
	const studiohdr_t* FindModel(void** cache, char const* modelname) const;

	mutable void* virtualModel;
	virtualmodel_t* GetVirtualModel(void) const;

	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) const;

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

	void* pVertexBase;
	void* pIndexBase;

	byte				constdirectionallightdot;

	byte				rootLOD;

	byte				numAllowedRootLODs;

	byte				unused[1];

	int					unused4;       

	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; }

	int					unused3[1];

	int					studiohdr2index;
	studiohdr2_t* pStudioHdr2() const { return (studiohdr2_t*)(((byte*)this) + studiohdr2index); }

	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; }       

	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; }

	int					unused2[1];

	studiohdr_t() {}

private:
	studiohdr_t(const studiohdr_t& vOther);

	friend struct virtualmodel_t;
};



class IDataCache;
class IMDLCache;

class CStudioHdr
{
public:
	CStudioHdr(void);
	CStudioHdr(const studiohdr_t* pStudioHdr, IMDLCache* mdlcache = NULL);
	~CStudioHdr() { Term(); }

	void Init(const studiohdr_t* pStudioHdr, IMDLCache* 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* > m_pStudioHdrCache;

	mutable int			m_nFrameUnlockCounter;
	int* m_pFrameUnlockCounter;
	CThreadFastMutex	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;		       
	int					RemapSeqBone(int iSequence, int iLocalBone) const;	       

	bool				SequencesAvailable() const;
	int					GetNumSeq(void) const;
	mstudioanimdesc_t& pAnimdesc(int i);
	mstudioseqdesc_t& pSeqdesc(int iSequence);
	int					iRelativeAnim(int baseseq, int relanim) const;	         
	int					iRelativeSeq(int baseseq, int relseq) const;		         

	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);
	void				SetAttachmentBone(int iAttachment, int iBone);

	int					EntryNode(int iSequence);
	int					ExitNode(int iSequence);
	char* pszNodeName(int iNode);
	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* 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 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; };		    
	inline Vector		hull_max() const { return m_pStudioHdr->hull_max; };

	inline Vector		view_bbmin() const { return m_pStudioHdr->view_bbmin; };		   
	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); }

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

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

	void				RunFlexRules(const float* src, float* dest);


public:
	inline int boneFlags(int iBone) const { return m_boneFlags[iBone]; }
	inline int boneParent(int iBone) const { return m_boneParent[iBone]; }

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

public:

	class CActivityToSequenceMapping   
	{
	public:
		struct SequenceTuple
		{
			short		seqnum;
			short		weight;           
			CUtlSymbol* pActivityModifiers;		     
			int			iNumActivityModifiers;
		};

		struct HashValueType
		{
			int activityIdx;

			int startingIdx;
			int count;
			int totalWeight;

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

			HashValueType() : activityIdx(-1), startingIdx(-1), count(-1), totalWeight(-1)
			{
				AssertMsg(false, "Don't use default HashValueType()!");
			}


			class HashFuncs
			{
			public:
				HashFuncs(int) {}

				bool operator()(const HashValueType& lhs, const HashValueType& rhs) const
				{
					return lhs.activityIdx == rhs.activityIdx;
				}

				unsigned int operator()(const HashValueType& item) const
				{
					return HashInt(item.activityIdx);
				}
			};
		};

		typedef CUtlHash<HashValueType, HashValueType::HashFuncs, HashValueType::HashFuncs> ActivityToValueIdxHash;

		CActivityToSequenceMapping(void)
			: m_pSequenceTuples(NULL), m_iSequenceTuplesCount(0), m_ActToSeqHash(8, 0, 0), m_expectedPStudioHdr(NULL), m_expectedVModel(NULL)
#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE
			, m_bIsInitialized(false)
#endif
		{};

		~CActivityToSequenceMapping()
		{
			if (m_pSequenceTuples != NULL)
			{
				if (m_pSequenceTuples->pActivityModifiers != NULL)
				{
					delete[] m_pSequenceTuples->pActivityModifiers;
				}
				delete[] m_pSequenceTuples;
			}
		}

		const SequenceTuple* GetSequences(int forActivity, int* outSequenceCount, int* outTotalWeight);

		int NumSequencesForActivity(int forActivity);

#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE
		inline bool IsInitialized(void) { return m_bIsInitialized; }
#endif

	private:

		void Initialize(CStudioHdr* pstudiohdr);

		void Reinitialize(CStudioHdr* pstudiohdr);

		int SelectWeightedSequence(CStudioHdr* pstudiohdr, int activity, int curSequence);

		int SelectWeightedSequenceFromModifiers(CStudioHdr* pstudiohdr, int activity, CUtlSymbol* pActivityModifiers, int iModifierCount);

		SequenceTuple* m_pSequenceTuples;
		unsigned int m_iSequenceTuplesCount;      
#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE
		bool m_bIsInitialized;
#endif

		ActivityToValueIdxHash m_ActToSeqHash;

		const void* m_expectedPStudioHdr;
		const void* m_expectedVModel;

		bool ValidateAgainst(const CStudioHdr* RESTRICT pstudiohdr);
		void SetValidationPair(const CStudioHdr* RESTRICT pstudiohdr);

		friend class CStudioHdr;
	};

	CActivityToSequenceMapping m_ActivityToSequence;

	inline int SelectWeightedSequence(int activity, int curSequence)
	{
#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE
		if (!m_ActivityToSequence.IsInitialized())
		{
			m_ActivityToSequence.Initialize(this);
		}
#endif
		return m_ActivityToSequence.SelectWeightedSequence(this, activity, curSequence);
	}

	inline int SelectWeightedSequenceFromModifiers(int activity, CUtlSymbol* pActivityModifiers, int iModifierCount)
	{
#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE
		if (!m_ActivityToSequence.IsInitialized())
		{
			m_ActivityToSequence.Initialize(this);
		}
#endif
		return m_ActivityToSequence.SelectWeightedSequenceFromModifiers(this, activity, pActivityModifiers, iModifierCount);
	}

	inline bool HaveSequenceForActivity(int activity)
	{
#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE
		if (!m_ActivityToSequence.IsInitialized())
		{
			m_ActivityToSequence.Initialize(this);
		}
#endif
		return (m_ActivityToSequence.NumSequencesForActivity(activity) > 0);
	}

	inline void ReinitializeSequenceMapping(void)
	{
		m_ActivityToSequence.Reinitialize(this);
	}

#ifdef STUDIO_ENABLE_PERF_COUNTERS
public:
	inline void			ClearPerfCounters(void)
	{
		m_nPerfAnimatedBones = 0;
		m_nPerfUsedBones = 0;
		m_nPerfAnimationLayers = 0;
	};

	mutable	int			m_nPerfAnimatedBones;
	mutable	int			m_nPerfUsedBones;
	mutable	int			m_nPerfAnimationLayers;
#endif


};

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);
	}

	int					obsolete1;

	int					numsettings;
	int					index;

	int					obsolete2;

	int					settingindex;

	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;

	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);
	}

	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); };
};

inline int flexsetting_t::psetting(byte* base, int i, flexweight_t** weights) const
{
	*weights = (flexweight_t*)(((byte*)this) + settingindex) + i;
	return numsettings;
};


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	  
#define STUDIO_FETCH1	2	   
#define STUDIO_FETCH2	3	   
#define STUDIO_ADD		4
#define STUDIO_SUB		5
#define STUDIO_MUL		6
#define STUDIO_DIV		7
#define STUDIO_NEG		8	  
#define STUDIO_EXP		9	  
#define STUDIO_OPEN		10	     
#define STUDIO_CLOSE	11
#define STUDIO_COMMA	12	     
#define STUDIO_MAX		13
#define STUDIO_MIN		14
#define STUDIO_2WAY_0	15	                  
#define STUDIO_2WAY_1	16	                  
#define STUDIO_NWAY		17	                  
#define STUDIO_COMBO	18	             
#define STUDIO_DOMINATE	19	     
#define STUDIO_DME_LOWER_EYELID 20	 
#define STUDIO_DME_UPPER_EYELID 21	 

#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	     

#define STUDIO_LOOPING	0x0001		          
#define STUDIO_SNAP		0x0002		         
#define STUDIO_DELTA	0x0004		          
#define STUDIO_AUTOPLAY	0x0008		         
#define STUDIO_POST		0x0010		 
#define STUDIO_ALLZEROS	0x0020		       
#define STUDIO_CYCLEPOSE 0x0080		         
#define STUDIO_REALTIME	0x0100		             
#define STUDIO_LOCAL	0x0200		      
#define STUDIO_HIDDEN	0x0400		      
#define STUDIO_OVERRIDE	0x0800		     
#define STUDIO_ACTIVITY	0x1000		        
#define STUDIO_EVENT	0x2000		        
#define STUDIO_WORLD	0x4000		    
#define STUDIO_AL_POST		0x0010		 
#define STUDIO_AL_SPLINE	0x0040		           
#define STUDIO_AL_XFADE		0x0080		                   
#define STUDIO_AL_NOBLEND	0x0200		       
#define STUDIO_AL_LOCAL		0x1000		      
#define STUDIO_AL_POSE		0x4000		          


inline bool Studio_ConvertStudioHdrToNewVersion(studiohdr_t* pStudioHdr)
{
	COMPILE_TIME_ASSERT(STUDIO_VERSION == 48);              

	int version = pStudioHdr->version;
	if (version == STUDIO_VERSION)
		return true;

	bool bResult = true;
	if (version < 46)
	{
		for (int i = 0; i < pStudioHdr->numlocalanim; i++)
		{
			mstudioanimdesc_t* pAnim = (mstudioanimdesc_t*)pStudioHdr->pLocalAnimdesc(i);

			if (pAnim->sectionframes != 0)
			{
				memset(&(pAnim->numframes), 0, (byte*)(pAnim + 1) - (byte*)&(pAnim->numframes));

				pAnim->numframes = 1;
				pAnim->animblock = -1;    
				bResult = false;
			}
		}
	}

	if (version < 47)
	{
		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)
	{
		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;
			}
		}
	}

	pStudioHdr->version = STUDIO_VERSION;

	return bResult;
}

inline void Studio_SetRootLOD(studiohdr_t* pStudioHdr, int rootLOD)
{
	if (pStudioHdr->numAllowedRootLODs > 0 &&
		rootLOD >= pStudioHdr->numAllowedRootLODs)
	{
		rootLOD = pStudioHdr->numAllowedRootLODs - 1;
	}

	Assert(rootLOD >= 0 && rootLOD < MAX_NUM_LODS);
	Clamp(rootLOD, 0, MAX_NUM_LODS - 1);

	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);

				pMesh->numvertices = pMesh->vertexdata.numLODVertexes[rootLOD];
				pMesh->vertexoffset = totalMeshVertexes;
				totalMeshVertexes += pMesh->numvertices;
			}

			pModel->numvertices = totalMeshVertexes;
			pModel->vertexindex = vertexindex;
			pModel->tangentsindex = tangentsindex;

			vertexindex += totalMeshVertexes * sizeof(mstudiovertex_t);
			tangentsindex += totalMeshVertexes * sizeof(Vector4D);
		}
	}

	pStudioHdr->rootLOD = rootLOD;
}

inline int Studio_VertexDataSize(const vertexFileHeader_t* pVvdHdr, int rootLOD, bool bNeedsTangentS)
{
	int numVertexes = pVvdHdr->numLODVertexes[rootLOD] + 1;
	int dataLength = pVvdHdr->vertexDataStart + numVertexes * sizeof(mstudiovertex_t);
	if (bNeedsTangentS)
	{
		dataLength += numVertexes * sizeof(Vector4D);
	}

	return dataLength;
}

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];

	memcpy((void*)pNewVvdHdr, (void*)pTempVvdHdr, pTempVvdHdr->vertexDataStart);

	for (i = 0; i < rootLOD; i++)
	{
		pNewVvdHdr->numLODVertexes[i] = pNewVvdHdr->numLODVertexes[rootLOD];
	}

	if (bNeedsTangentS)
	{
		pNewVvdHdr->tangentDataStart = pNewVvdHdr->vertexDataStart + numVertexes * sizeof(mstudiovertex_t);
	}
	else
	{
		pNewVvdHdr->tangentDataStart = 0;
	}

	if (!pNewVvdHdr->numFixups)
	{
		memcpy(
			(byte*)pNewVvdHdr + pNewVvdHdr->vertexDataStart,
			(byte*)pTempVvdHdr + pTempVvdHdr->vertexDataStart,
			numVertexes * sizeof(mstudiovertex_t));

		if (bNeedsTangentS)
		{
			memcpy(
				(byte*)pNewVvdHdr + pNewVvdHdr->tangentDataStart,
				(byte*)pTempVvdHdr + pTempVvdHdr->tangentDataStart,
				numVertexes * sizeof(Vector4D));
		}

		return numVertexes;
	}

	target = 0;
	pFixupTable = (vertexFileFixup_t*)((byte*)pTempVvdHdr + pTempVvdHdr->fixupTableStart);
	for (i = 0; i < pTempVvdHdr->numFixups; i++)
	{
		if (pFixupTable[i].lod < rootLOD)
		{
			continue;
		}

		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)
		{
			memcpy(
				(Vector4D*)((byte*)pNewVvdHdr + pNewVvdHdr->tangentDataStart) + target,
				(Vector4D*)((byte*)pTempVvdHdr + pTempVvdHdr->tangentDataStart) + pFixupTable[i].sourceVertexID,
				pFixupTable[i].numVertexes * sizeof(Vector4D));
		}

		target += pFixupTable[i].numVertexes;
	}

	pNewVvdHdr->numFixups = 0;

	return target;
}

#endif  