// #pragma pattern_limit 0x20000 #pragma array_limit 0x20000 #include #include #include #define MAX_NUM_LODS 8 using byte = u8; using uchar = u8; using int = s32; using uint = u32; using short = s16; using ushort = u16; using uint32 = u32; fn Print_pszName(auto structure) { str retVal = std::format("{}", structure.pszName); return retVal; }; struct Vector { float x, y, z; } [[format("FormatVector")]]; fn FormatVector(auto vec) { str retVal = std::format("{:.3f} {:.3f} {:.3f}", vec.x, vec.y, vec.z); return retVal; }; struct Quaternion { float x, y, z, w; }[[format("FormatQuat")]]; fn FormatQuat(Quaternion vec) { str retVal = std::format("{:.3f} {:.3f} {:.3f} {:.3f}", vec.x, vec.y, vec.z, vec.w); return retVal; }; using RadianEuler = Vector; using QAngle = Vector; struct matrix3x4_t{ float m00; float m01; float m02; float m03; float m10; float m11; float m12; float m13; float m20; float m21; float m22; float m23; }; bitfield STUDIOMDL_FLAGS { AUTOGENERATED_HITBOX : 1; USES_ENV_CUBEMAP : 1; FORCE_OPAQUE : 1; TRANSLUCENT_TWOPASS : 1; STATIC_PROP : 1; USES_FB_TEXTURE : 1; HASSHADOWLOD : 1; USES_BUMPMAPPING : 1; USE_SHADOWLOD_MATERIALS : 1; OBSOLETE : 1; UNUSED : 1; NO_FORCED_FADE : 1; FORCE_PHONEME_CROSSFADE : 1; CONSTANT_DIRECTIONAL_LIGHT_DOT : 1; FLEXES_CONVERTED : 1; BUILT_IN_PREVIEW_MODE : 1; AMBIENT_BOOST : 1; DO_NOT_CAST_SHADOWS : 1; CAST_TEXTURE_SHADOWS : 1; padding : 1; VERT_ANIM_FIXED_POINT_SCALE : 1; padding : 11; } [[right_to_left]]; struct mstudiobone_t { int sznameindex; char pszName[] @ addressof(this) + sznameindex; int parentbone; // parent bone int bonecontroller[6]; // bone controller index, -1 == none Vector pos; Quaternion quat; RadianEuler rot; Vector posscale; Vector rotscale; matrix3x4_t poseToBone; Quaternion qAlignment; int flags; int proctype; int procindex; // procedural rule int physicsbone; // index into physically simulated bone int surfacepropidx; // index into string tablefor property name char pszSurfaceProp[] @ addressof(this) + surfacepropidx; 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 }; struct mstudiobonecontroller_t { 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]; }; struct mstudiobbox_t { int bone; int group; // intersection group Vector bbmin; // bounding box Vector bbmax; int szhitboxnameindex; // offset to the name of the hitbox. QAngle angOffsetOrientation; float flCapsuleRadius; int unused[4]; char pszHitboxName[] @ addressof(this) + szhitboxnameindex; }; struct mstudiohitboxset_t { int sznameindex; char pszName[] @ addressof(this) + sznameindex; int numhitboxes; int hitboxindex; mstudiobbox_t pHitbox[numhitboxes] @ addressof(this) + hitboxindex; }; using float16 = u16; struct mstudioikrulezeroframe_t { 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 mstudiomovement_t { 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??? }; struct mstudioanimblock_t { int datastart; int dataend; }; struct mstudioanimdesc_t { int baseptr; int sznameindex; char pszName[] @ addressof(this) + sznameindex; float fps; // frames per second int flags; // looping/non-looping flags int numframes; int nummovements; int movementindex; mstudiomovement_t pMovement[nummovements] @ addressof(this) + movementindex; int ikrulezeroframeindex; mstudioikrulezeroframe_t pIKRuleZeroFrame[ikrulezeroframeindex != 0] @ addressof(this) + ikrulezeroframeindex; int unused1[5]; int animblock; int animindex; // non-zero when anim data isn't in sections int numikrules; int ikruleindex; // non-zero when IK data is stored in the mdl int animblockikruleindex; // non-zero when IK data is stored in animblock file int numlocalhierarchy; int localhierarchyindex; int sectionindex; int sectionframes; // number of frames used in each fast lookup section, zero if not used short zeroframespan; // frames per span short zeroframecount; // number of spans int zeroframeindex; float zeroframestalltime [[hidden]]; // saved during read stalls } [[format("Print_pszName")]]; struct mstudioevent_t { float cycle; int event; int type; char options[64]; int szeventindex; char pszEventName[] @ addressof(this) + szeventindex; }; struct mstudioautolayer_t { short iSequence; short iPose; 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 mstudioseqdesc_t { int baseptr; int szlabelindex; char pszLabel[] @ addressof(this) + szlabelindex; int szactivitynameindex; char pszActivityName[] @ addressof(this) + szactivitynameindex; int flags; // looping/non-looping flags int activity; // initialized at loadtime to game DLL values int actweight; int numevents; int eventindex; mstudioevent_t pEvent[numevents] @ addressof(this) + eventindex; 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( int x, int y ) 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; mstudioautolayer_t pAutolayer[numautolayers] @ addressof(this) + autolayerindex; int weightlistindex; float pBoneweight[parent.numbones] @ addressof(this) + weightlistindex; //inline float *pBoneweight( int i ) const { return ((float *)(((byte *)this) + weightlistindex) + i); }; //inline float weight( int i ) const { return *(pBoneweight( 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 *pIKLock( int i ) const { Assert( i >= 0 && i < numiklocks); return (mstudioiklock_t *)(((byte *)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 animtagindex; int numanimtags; //inline mstudioanimtag_t *pAnimTag( int i ) const { Assert( i >= 0 && i < numanimtags); return (mstudioanimtag_t *)(((byte *)this) + animtagindex) + i; }; int rootDriverIndex; int unused[2]; // remove/add as appropriate (grow back to 8 ints on version change!) }; struct mstudiotexture_t { int sznameindex; char pszName[] @ addressof(this) + sznameindex; int flags; int used; int unused1; int material; // fixme: this needs to go away . .isn't used by the engine, but is used by studiomdl int clientmaterial; // gary, replace with client material pointer if used int unused[10]; }; struct mstudio_meshvertexdata_t { int unused_modelvertexdata; // 64b - Moved to follow num_LOD_Vertexes. int numLODVertexes[MAX_NUM_LODS]; //int modelvertexdata; }; struct mstudioflex_t { 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 uchar vertanimtype; // See StudioVertAnimType_t uchar unusedchar[3]; int unused[6]; }; struct mstudiomesh_t { int material; int modelindex; int numvertices; // number of unique vertices/normals/texcoords int vertexoffset; // vertex mstudiovertex_t int numflexes; // vertex animation int flexindex; mstudioflex_t pFlex[numflexes] @ addressof(this) + flexindex; // 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 }; struct mstudioeyeball_t { int sznameindex; char pszName[] @ addressof(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]; }; struct mstudio_modelvertexdata_t { int pVertexData; int pTangentData; int pExtraData; }; struct mstudiomodel_t { char name[64]; int type; float boundingradius; int nummeshes; int meshindex; mstudiomesh_t pMesh[nummeshes] @ addressof(this) + meshindex; int numvertices; // number of unique vertices/normals/texcoords int vertexindex; // vertex Vector int tangentsindex; // tangents Vector int numattachments; int attachmentindex; int numeyeballs; int eyeballindex; mstudioeyeball_t pEyeball[numeyeballs] @ addressof(this) + eyeballindex; mstudio_modelvertexdata_t vertexdata; int unused[7]; // remove as appropriate }; struct mstudiobodyparts_t { int sznameindex; char pszName[] @ addressof(this) + sznameindex; int nummodels; int base; int modelindex; // index into models array mstudiomodel_t pModel[nummodels] @ addressof(this) + modelindex; }; struct mstudioattachment_t { int sznameindex; char pszName[] @ addressof(this) + sznameindex; uint flags; int localbone; matrix3x4_t local; // attachment point int unused[8]; }; struct mstudioflexdesc_t { int szFACSindex; char pszFACS[] @ addressof(this) + szFACSindex; }; struct mstudioflexcontroller_t { int sztypeindex; char pszType[] @ addressof(this) + sztypeindex; int sznameindex; char pszName[] @ addressof(this) + sznameindex; int localToGlobal; // remapped at load time to master list float min; float max; }; union flexd { int index; float value; }; struct mstudioflexop_t { int op; flexd d; }; struct mstudioflexrule_t { int flex; int numops; int opindex; mstudioflexop_t iFlexOp[numops] @ addressof(this) + opindex; }; struct mstudioiklink_t { int bone; Vector kneeDir; // ideal bending direction (per link, if applicable) Vector unused0; // unused }; struct mstudioikchain_t { int sznameindex; char pszName[] @ addressof(this) + sznameindex; int linktype; int numlinks; int linkindex; mstudioiklink_t pLink[numlinks] @ addressof(this) + linkindex; }; struct mstudiomouth_t { int bone; Vector forward; int flexdesc; }; struct mstudioposeparamdesc_t { int sznameindex; char pszName[] @ addressof(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 mstudioiklock_t { int chain; float flPosWeight; float flLocalQWeight; int flags; int unused[4]; }; struct mstudiomodelgroup_t { int szlabelindex; // textual name char pszLabel[] @ addressof(this) + szlabelindex; int sznameindex; // file name char pszName[] @ addressof(this) + sznameindex; }; enum RemapType : uchar //FlexControllerRemapType_t enum { PASSTHRU = 0, TWOWAY, // Control 0 -> ramps from 1-0 from 0->0.5. Control 1 -> ramps from 0-1 from 0.5->1 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 EYELID }; struct mstudioflexcontrollerui_t { int sznameindex; char pszName[] @ addressof(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; //TODO inlines RemapType remaptype; // See the FlexControllerRemapType_t enum bool stereo; // Is this a stereo control? byte unused[2]; }; struct mstudiosrcbonetransform_t { int sznameindex; char pszName[] @ addressof(this) + sznameindex; matrix3x4_t pretransform; matrix3x4_t posttransform; }; struct mstudiolinearbone_t { int numbones; int flagsindex; int flags[numbones] @ addressof(this) + flagsindex; int parentindex; int prnt[numbones] @ addressof(this) + parentindex; int posindex; Vector pos[numbones] @ addressof(this) + posindex; int quatindex; Quaternion quat[numbones] @ addressof(this) + quatindex; int rotindex; RadianEuler rot[numbones] @ addressof(this) + rotindex; int posetoboneindex; matrix3x4_t poseToBone[numbones] @ addressof(this) + posetoboneindex; int posscaleindex; Vector posscale[numbones] @ addressof(this) + posscaleindex; int rotscaleindex; Vector rotscale[numbones] @ addressof(this) + rotscaleindex; int qalignmentindex; Quaternion qalignment[numbones] @ addressof(this) + qalignmentindex; int unused[6]; }; struct mstudiobodygrouppreset_t { int sznameindex; char pszName[] @ addressof(this) + sznameindex; int iValue; int iMask; }; enum StudioBoneFlexComponent_t : int { 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 }; struct mstudioboneflexdrivercontrol_t { StudioBoneFlexComponent_t 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 }; struct mstudioboneflexdriver_t { 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) mstudioboneflexdrivercontrol_t pBoneFlexDriverControl[m_nControlCount] @ addressof(this) + m_nControlIndex; int unused[3]; }; struct cdtextures_t { int idx; char cdtexture[] @ std::mem::read_signed($-4, 4); }; struct studiohdr2_t { int numsrcbonetransform; int srcbonetransformindex; int illumpositionattachmentindex; float flMaxEyeDeflection; int linearboneindex; mstudiolinearbone_t pLinearBones @ addressof(this) + linearboneindex; int sznameindex; char StudioHdr2_pszName[] @ addressof(this) + sznameindex; int m_nBoneFlexDriverCount; int m_nBoneFlexDriverIndex; mstudioboneflexdriver_t pBoneFlexDriver[m_nBoneFlexDriverCount] @ addressof(this) + m_nBoneFlexDriverIndex; int m_pFeModel_index; int m_nBodyGroupPresetCount; int m_nBodyGroupPresetIndex; mstudiobodygrouppreset_t pBodyGroupPreset[m_nBodyGroupPresetCount] @ addressof(this) + m_nBodyGroupPresetIndex; int reserved[53]; }; struct studiohdr_t { int id; int version; int checksum; 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; STUDIOMDL_FLAGS flags; int numbones; // bones int boneindex; mstudiobone_t pBone[numbones] @ addressof(this) + boneindex; int numbonecontrollers; // bone controllers int bonecontrollerindex; mstudiobonecontroller_t pBonecontroller[numbonecontrollers] @ addressof(this) + boneindex; int numhitboxsets; int hitboxsetindex; mstudiohitboxset_t pHitboxSet[numhitboxsets] @ addressof(this) + hitboxsetindex; int numlocalanim; // animations/poses int localanimindex; // animation descriptions mstudioanimdesc_t pLocalAnimdesc[numlocalanim] @ addressof(this) + localanimindex; int numlocalseq; // sequences int localseqindex; mstudioseqdesc_t pLocalSeqdesc[numlocalseq] @ addressof(this) + localseqindex; int activitylistversion; // initialization flag - have the sequences been indexed? int eventsindexed; int numtextures; int textureindex; mstudiotexture_t pTexture[numtextures] @ addressof(this) + textureindex; int numcdtextures; int cdtextureindex; cdtextures_t CdTextures[numcdtextures] @ cdtextureindex; int numskinref; int numskinfamilies; int skinindex; short pSkinref[numskinfamilies*numskinref] @ addressof(this) + skinindex; int numbodyparts; int bodypartindex; mstudiobodyparts_t pBodypart[numbodyparts] @ addressof(this) + bodypartindex; int numlocalattachments; int localattachmentindex; mstudioattachment_t pLocalAttachment[numlocalattachments] @ addressof(this) + localattachmentindex; int numlocalnodes; int localnodeindex; int localnodenameindex; int numflexdesc; int flexdescindex; mstudioflexdesc_t pFlexdesc[numflexdesc] @ addressof(this) + flexdescindex; int numflexcontrollers; int flexcontrollerindex; mstudioflexcontroller_t pFlexcontroller[numflexcontrollers] @ addressof(this) + flexcontrollerindex; int numflexrules; int flexruleindex; mstudioflexrule_t pFlexRule[numflexrules] @ addressof(this) + flexruleindex; int numikchains; int ikchainindex; mstudioikchain_t pIKChain[numikchains] @ addressof(this) + ikchainindex; int nummouths; int mouthindex; mstudiomouth_t pMouth[nummouths] @ addressof(this) + mouthindex; int numlocalposeparameters; int localposeparamindex; mstudioposeparamdesc_t pLocalPoseParameter[numlocalposeparameters] @ addressof(this) + localposeparamindex; int surfacepropindex; char pszSurfaceProp[] @ addressof(this) + surfacepropindex; int keyvalueindex; int keyvaluesize; char KeyValueText[keyvaluesize] @ addressof(this) + keyvalueindex; int numlocalikautoplaylocks; int localikautoplaylockindex; mstudioiklock_t pLocalIKAutoplayLock[numlocalikautoplaylocks] @ addressof(this) + localikautoplaylockindex; float mass; int contents; int numincludemodels; int includemodelindex; mstudiomodelgroup_t pModelGroup[numincludemodels] @ addressof(this) + includemodelindex; int unused_virtualModel; int szanimblocknameindex; char pszAnimBlockName[] @ addressof(this) + szanimblocknameindex; int numanimblocks; int animblockindex; mstudioanimblock_t pAnimBlock[numanimblocks] @ addressof(this) + animblockindex; int unused_animblockModel; int bonetablebynameindex; int unused_pVertexBase; int unused_pIndexBase; byte constdirectionallightdot; byte rootLOD; byte numAllowedRootLODs; byte unused[1]; int unused4; int numflexcontrollerui; int flexcontrolleruiindex; mstudioflexcontrollerui_t pFlexControllerUI[numflexcontrollerui] @ addressof(this) + flexcontrolleruiindex; float flVertAnimFixedPointScale; int surfacepropLookup; int studiohdr2index; studiohdr2_t pStudioHdr2 @ addressof(this) + studiohdr2index; mstudiosrcbonetransform_t SrcBoneTransform[pStudioHdr2.numsrcbonetransform] @ addressof(this) + pStudioHdr2.srcbonetransformindex; int unused2[1]; }; studiohdr_t pStudioHdr @ 0x00;