Source: src/public/studio.h in ValveSoftware/source-sdk-2013, version 48, read on
13 November 2026. Sizes are the sums of the declared field types for the 32-bit layout, with “unused”
fields counted. Every …index in these records is a byte offset from the start of the record
that holds it. Records are little-endian; Vector is 3 floats (12 bytes),
Quaternion 4 floats (16), RadianEuler 3 floats (12) and
matrix3x4_t 12 floats (48).
Bones
mstudiobone_t, 216 bytes, at the header’s boneindex.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | int32 | sznameindex | Offset of the bone name |
| 4 | 4 | int32 | parent | Parent bone |
| 8 | 24 | int32[6] | bonecontroller | Bone controller index for each of the six motion axes, −1 for none |
| 32 | 12 | Vector | pos | Default position |
| 44 | 16 | Quaternion | quat | Default rotation |
| 60 | 12 | RadianEuler | rot | Default rotation as Euler angles |
| 72 | 12 | Vector | posscale | Compression scale of the position |
| 84 | 12 | Vector | rotscale | Compression scale of the rotation |
| 96 | 48 | matrix3x4_t | poseToBone | Pose to bone transform |
| 144 | 16 | Quaternion | qAlignment | Fixed alignment, used with BONE_FIXED_ALIGNMENT |
| 160 | 4 | int32 | flags | Bone flags, below |
| 164 | 4 | int32 | proctype | Procedural rule type, below |
| 168 | 4 | int32 | procindex | Offset of the procedural record, 0 for none |
| 172 | 4 | int32 | physicsbone | Index into the physically simulated bones (mutable) |
| 176 | 4 | int32 | surfacepropidx | Offset of the surface property name |
| 180 | 4 | int32 | contents | Contents flags (see BSPFlags.h) |
| 184 | 32 | int32[8] | unused | Unused |
Bone flags
| Value | Name | Meaning |
|---|---|---|
| 0x00000001 | BONE_PHYSICALLY_SIMULATED | Physically simulated when physics is active |
| 0x00000002 | BONE_PHYSICS_PROCEDURAL | Procedural when physics is active |
| 0x00000004 | BONE_ALWAYS_PROCEDURAL | Always procedurally animated |
| 0x00000008 | BONE_SCREEN_ALIGN_SPHERE | Aligns to the screen, not constrained in motion |
| 0x00000010 | BONE_SCREEN_ALIGN_CYLINDER | Aligns to the screen, constrained by its own axis |
| 0x0000001F | BONE_CALCULATE_MASK | Mask of the five flags above |
| 0x00000100 | BONE_USED_BY_HITBOX | Bone or a child is used by a hit box |
| 0x00000200 | BONE_USED_BY_ATTACHMENT | Bone or a child is used by an attachment point |
| 0x00000400 … 0x00020000 | BONE_USED_BY_VERTEX_LOD0 … LOD7 | Bone or a child is used by a skinned vertex at that level of detail; one bit per LOD, mask BONE_USED_BY_VERTEX_MASK 0x0003FC00 |
| 0x00040000 | BONE_USED_BY_BONE_MERGE | Available for bone merge to occur against it |
| 0x0007FF00 | BONE_USED_MASK | All the “used by” bits (BONE_USED_BY_ANYTHING) |
| 0x00100000 | BONE_FIXED_ALIGNMENT | Cannot spin 360 degrees; interpolation is normalised around a fixed orientation |
| 0x00200000 | BONE_HAS_SAVEFRAME_POS | Has a saved-frame position (Vector48) |
| 0x00400000 | BONE_HAS_SAVEFRAME_ROT | Has a saved-frame rotation (Quaternion64) |
| 0x00F00000 | BONE_TYPE_MASK | Mask of the bone type bits |
Procedural bones
When proctype is not 0, the record at procindex (from the start of the bone) is
one of these.
| proctype | Name | Record |
|---|---|---|
| 1 | STUDIO_PROC_AXISINTERP | mstudioaxisinterpbone_t, 176 bytes: control (int), axis (int), pos[6] (Vector), quat[6] (Quaternion); each array in the order X+, X−, Y+, Y−, Z+, Z− |
| 2 | STUDIO_PROC_QUATINTERP | mstudioquatinterpbone_t, 12 bytes: control, numtriggers, triggerindex. Each trigger, mstudioquatinterpinfo_t, is 48 bytes: inv_tolerance (float, 1 / radian angle of trigger influence), trigger (Quaternion, the angle to match), pos (Vector, new position), quat (Quaternion, new angle) |
| 3 | STUDIO_PROC_AIMATBONE | mstudioaimatbone_t, 44 bytes: parent, aim (a bone or an attachment), aimvector, upvector, basepos (Vectors) |
| 4 | STUDIO_PROC_AIMATATTACH | The same mstudioaimatbone_t, with aim an attachment |
| 5 | STUDIO_PROC_JIGGLE | mstudiojigglebone_t, 140 bytes: flags (int), then 34 floats, in this order: length, tipMass; yawStiffness, yawDamping, pitchStiffness, pitchDamping, alongStiffness, alongDamping; angleLimit (radians); minYaw, maxYaw, yawFriction, yawBounce; minPitch, maxPitch, pitchFriction, pitchBounce; baseMass, baseStiffness, baseDamping, baseMinLeft, baseMaxLeft, baseLeftFriction, baseMinUp, baseMaxUp, baseUpFriction, baseMinForward, baseMaxForward, baseForwardFriction; boingImpactSpeed, boingImpactAngle, boingDampingRate, boingFrequency, boingAmplitude |
Jiggle flags
| Value | Name | Meaning |
|---|---|---|
| 0x01 | JIGGLE_IS_FLEXIBLE | Flexible |
| 0x02 | JIGGLE_IS_RIGID | Rigid |
| 0x04 | JIGGLE_HAS_YAW_CONSTRAINT | Yaw constraint |
| 0x08 | JIGGLE_HAS_PITCH_CONSTRAINT | Pitch constraint |
| 0x10 | JIGGLE_HAS_ANGLE_CONSTRAINT | Angle constraint |
| 0x20 | JIGGLE_HAS_LENGTH_CONSTRAINT | Length constraint |
| 0x40 | JIGGLE_HAS_BASE_SPRING | Base spring |
| 0x80 | JIGGLE_IS_BOING | Simple squash and stretch sinusoid “boing” |
Bone controllers
mstudiobonecontroller_t, 56 bytes, at bonecontrollerindex.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | int32 | bone | Bone it drives; −1 for none (the header comment says “−1 == 0”) |
| 4 | 4 | int32 | type | Motion type flags, below |
| 8 | 4 | float | start | Start of the range |
| 12 | 4 | float | end | End of the range |
| 16 | 4 | int32 | rest | Byte index value at rest |
| 20 | 4 | int32 | inputfield | 0 to 3 user-set controller, 4 mouth |
| 24 | 32 | int32[8] | unused | Unused |
Motion types
Also used for the motionflags of a movement.
| Value | Name | Meaning |
|---|---|---|
| 0x00000001 | STUDIO_X | X translation |
| 0x00000002 | STUDIO_Y | Y translation |
| 0x00000004 | STUDIO_Z | Z translation |
| 0x00000008 | STUDIO_XR | X rotation |
| 0x00000010 | STUDIO_YR | Y rotation |
| 0x00000020 | STUDIO_ZR | Z rotation |
| 0x00000040 … 0x00000800 | STUDIO_LX, LY, LZ, LXR, LYR, LZR | 0x40 LX, 0x80 LY, 0x100 LZ, 0x200 LXR, 0x400 LYR, 0x800 LZR |
| 0x00001000 | STUDIO_LINEAR | Linear |
| 0x0003FFFF | STUDIO_TYPES | Mask of the types |
| 0x00040000 | STUDIO_RLOOP | Controller that wraps by the shortest distance |
Hit boxes
The header’s hitboxsetindex points to mstudiohitboxset_t records, 12 bytes each:
sznameindex, numhitboxes, hitboxindex (from the start of the set).
Each hit box, mstudiobbox_t, is 68 bytes.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | int32 | bone | Bone |
| 4 | 4 | int32 | group | Intersection group |
| 8 | 12 | Vector | bbmin | Bounding box, lower corner |
| 20 | 12 | Vector | bbmax | Bounding box, upper corner |
| 32 | 4 | int32 | szhitboxnameindex | Offset of the hit box name; 0 means no name |
| 36 | 32 | int32[8] | unused | Unused |
Attachments
mstudioattachment_t, 92 bytes, at localattachmentindex.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | int32 | sznameindex | Offset of the name |
| 4 | 4 | uint32 | flags | ATTACHMENT_FLAG_WORLD_ALIGN 0x10000: world aligned |
| 8 | 4 | int32 | localbone | Bone it is attached to |
| 12 | 48 | matrix3x4_t | local | Attachment point |
| 60 | 32 | int32[8] | unused | Unused |
IK chains, rules and locks
An IK chain, mstudioikchain_t at ikchainindex, is 16 bytes:
sznameindex, linktype, numlinks, linkindex (from the
start of the chain). Each link, mstudioiklink_t, is 28 bytes: bone (int),
kneeDir (Vector, the ideal bending direction) and an unused Vector.
Animations hold IK rules, mstudioikrule_t, 152 bytes:
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 4 | int32 | index | Index |
| 4 | 4 | int32 | type | IK_SELF 1, IK_WORLD 2, IK_GROUND 3, IK_RELEASE 4, IK_ATTACHMENT 5, IK_UNLATCH 6 |
| 8 | 4 | int32 | chain | IK chain |
| 12 | 4 | int32 | bone | Bone |
| 16 | 4 | int32 | slot | IK target slot, usually the same as the chain |
| 20, 24, 28 | 4 each | float | height, radius, floor | |
| 32 | 12 | Vector | pos | Position |
| 44 | 16 | Quaternion | q | Rotation |
| 60 | 4 | int32 | compressedikerrorindex | Offset of an mstudiocompressedikerror_t |
| 64 | 4 | int32 | unused2 | Unused |
| 68 | 4 | int32 | iStart | First frame of the error records |
| 72 | 4 | int32 | ikerrorindex | Offset of mstudioikerror_t records (28 bytes: Vector pos, Quaternion q), indexed by frame − iStart; 0 for none |
| 76, 80, 84, 88 | 4 each | float | start, peak, tail, end | Beginning of influence, start of full influence, end of full influence, end of all influence |
| 92 | 4 | float | unused3 | Unused |
| 96, 100, 104 | 4 each | float | contact, drop, top | Frame the footstep makes ground contact; how far down the foot should drop when reaching for IK; top of the foot box |
| 108, 112, 116 | 4 each | int32 | unused6, unused7, unused8 | Unused |
| 120 | 4 | int32 | szattachmentindex | Offset of the name of the world attachment |
| 124 | 28 | int32[7] | unused | Unused |
A compressed IK error, mstudiocompressedikerror_t, is 36 bytes: scale[6] (floats)
then offset[6] (shorts, from the start of the record). An offset of 0 or less means no value
stream; otherwise it points to an animation value stream (see
Sequences and animation).
An IK lock, mstudioiklock_t at localikautoplaylockindex (and in sequences), is 32
bytes: chain (int), flPosWeight, flLocalQWeight (floats),
flags (int) and four unused ints.
Extra bone tables in studiohdr2_t
Source bone transforms
mstudiosrcbonetransform_t, 100 bytes: sznameindex (int), then
pretransform and posttransform (matrix3x4_t, 48 bytes each). They
convert .dmx or .smd animation into MDL animation: pretransform × bone transform ×
posttransform.
Linear bones
mstudiolinearbone_t, 64 bytes: numbones (int), nine offsets (from the start of this
record) to per-bone arrays, and six unused ints. The arrays hold, for each bone: flagsindex
(int flags), parentindex (int), posindex (Vector), quatindex
(Quaternion), rotindex (RadianEuler), posetoboneindex (matrix3x4_t),
posscaleindex (Vector), rotscaleindex (Vector) and qalignmentindex
(Quaternion).
Bone flex drivers
mstudioboneflexdriver_t, 24 bytes: m_nBoneIndex (the bone driving the flex
controllers), m_nControlCount, m_nControlIndex (from the start of this record),
and three unused ints. Each control, mstudioboneflexdrivercontrol_t, is 16 bytes:
m_nBoneComponent (0 translate X, 1 translate Y, 2 translate Z, −1 invalid),
m_nFlexControllerIndex, m_flMin and m_flMax (the values of the bone
component that map to 0 and to 1 on the flex controller).