示例#1
0
        private void ReadSourceVtxVertices(Stream stream, long stripGroupOffset, SourceVtxStripGroup stripGroup)
        {
            if (stripGroup.vertexCount > 0 && stripGroup.vertexOffset != 0)
            {
                stream.Position           = stripGroupOffset + stripGroup.vertexOffset;
                stripGroup.theVtxVertices = new SourceVtxVertex[stripGroup.vertexCount];
                for (int i = 0; i < stripGroup.theVtxVertices.Length; i++)
                {
                    stripGroup.theVtxVertices[i] = new SourceVtxVertex();
                    stripGroup.theVtxVertices[i].boneWeightIndex = new byte[VVDParser.MAX_NUM_BONES_PER_VERT];
                    for (int j = 0; j < stripGroup.theVtxVertices[i].boneWeightIndex.Length; j++)
                    {
                        stripGroup.theVtxVertices[i].boneWeightIndex[j] = DataParser.ReadByte(stream);
                    }

                    stripGroup.theVtxVertices[i].boneCount = DataParser.ReadByte(stream);
                    stripGroup.theVtxVertices[i].originalMeshVertexIndex = DataParser.ReadUShort(stream);

                    stripGroup.theVtxVertices[i].boneId = new byte[VVDParser.MAX_NUM_BONES_PER_VERT];
                    for (int j = 0; j < stripGroup.theVtxVertices[i].boneId.Length; j++)
                    {
                        stripGroup.theVtxVertices[i].boneId[j] = DataParser.ReadByte(stream);
                    }
                }
            }
        }
示例#2
0
 private void ReadSourceVtxStripGroups(Stream stream, long meshOffset, SourceVtxMesh mesh)
 {
     if (mesh.stripGroupCount > 0 && mesh.stripGroupOffset != 0)
     {
         long[] stripGroupOffsets = new long[mesh.stripGroupCount];
         stream.Position        = meshOffset + mesh.stripGroupOffset;
         mesh.theVtxStripGroups = new SourceVtxStripGroup[mesh.stripGroupCount];
         for (int i = 0; i < mesh.theVtxStripGroups.Length; i++)
         {
             stripGroupOffsets[i]                   = stream.Position;
             mesh.theVtxStripGroups[i]              = new SourceVtxStripGroup();
             mesh.theVtxStripGroups[i].vertexCount  = DataParser.ReadInt(stream);
             mesh.theVtxStripGroups[i].vertexOffset = DataParser.ReadInt(stream);
             mesh.theVtxStripGroups[i].indexCount   = DataParser.ReadInt(stream);
             mesh.theVtxStripGroups[i].indexOffset  = DataParser.ReadInt(stream);
             mesh.theVtxStripGroups[i].stripCount   = DataParser.ReadInt(stream);
             mesh.theVtxStripGroups[i].stripOffset  = DataParser.ReadInt(stream);
             mesh.theVtxStripGroups[i].flags        = DataParser.ReadByte(stream);
         }
         for (int i = 0; i < mesh.theVtxStripGroups.Length; i++)
         {
             ReadSourceVtxVertices(stream, stripGroupOffsets[i], mesh.theVtxStripGroups[i]);
             ReadSourceVtxIndices(stream, stripGroupOffsets[i], mesh.theVtxStripGroups[i]);
             ReadSourceVtxStrips(stream, stripGroupOffsets[i], mesh.theVtxStripGroups[i]);
         }
     }
 }
示例#3
0
 private void ReadSourceVtxStrips(Stream stream, long stripGroupOffset, SourceVtxStripGroup stripGroup)
 {
     if (stripGroup.stripCount > 0 && stripGroup.stripOffset != 0)
     {
         stream.Position         = stripGroupOffset + stripGroup.stripOffset;
         stripGroup.theVtxStrips = new SourceVtxStrip[stripGroup.stripCount];
         for (int i = 0; i < stripGroup.theVtxStrips.Length; i++)
         {
             stripGroup.theVtxStrips[i]                       = new SourceVtxStrip();
             stripGroup.theVtxStrips[i].indexCount            = DataParser.ReadInt(stream);
             stripGroup.theVtxStrips[i].indexMeshIndex        = DataParser.ReadInt(stream);
             stripGroup.theVtxStrips[i].vertexCount           = DataParser.ReadInt(stream);
             stripGroup.theVtxStrips[i].vertexMeshIndex       = DataParser.ReadInt(stream);
             stripGroup.theVtxStrips[i].boneCount             = DataParser.ReadShort(stream);
             stripGroup.theVtxStrips[i].flags                 = DataParser.ReadByte(stream);
             stripGroup.theVtxStrips[i].boneStateChangeCount  = DataParser.ReadInt(stream);
             stripGroup.theVtxStrips[i].boneStateChangeOffset = DataParser.ReadInt(stream);
         }
     }
 }
示例#4
0
 private void ReadSourceVtxMeshes(Stream stream, long modelLodOffset, SourceVtxModelLod modelLod)
 {
     if (modelLod.meshCount > 0 && modelLod.meshOffset != 0)
     {
         long[] meshOffsets = new long[modelLod.meshCount];
         stream.Position       = modelLodOffset + modelLod.meshOffset;
         modelLod.theVtxMeshes = new SourceVtxMesh[modelLod.meshCount];
         for (int i = 0; i < modelLod.theVtxMeshes.Length; i++)
         {
             meshOffsets[i]           = stream.Position;
             modelLod.theVtxMeshes[i] = new SourceVtxMesh();
             modelLod.theVtxMeshes[i].stripGroupCount  = DataParser.ReadInt(stream);
             modelLod.theVtxMeshes[i].stripGroupOffset = DataParser.ReadInt(stream);
             modelLod.theVtxMeshes[i].flags            = DataParser.ReadByte(stream);
         }
         for (int i = 0; i < modelLod.theVtxMeshes.Length; i++)
         {
             ReadSourceVtxStripGroups(stream, meshOffsets[i], modelLod.theVtxMeshes[i]);
         }
     }
 }
        public static Color[] LoadVTFFile(Stream stream, long vtfBytePosition, out int width, out int height)
        {
            //Texture2D extracted = null;
            Color[] extracted = null;
            width = 0; height = 0;
            if (stream != null)
            {
                stream.Position = vtfBytePosition;
                int signature = DataParser.ReadInt(stream);
                if (signature == VTFHeader.signature)
                {
                    #region Read Header
                    VTFHeader vtfHeader;
                    uint[]    version = new uint[] { DataParser.ReadUInt(stream), DataParser.ReadUInt(stream) };
                    vtfHeader.version    = (version[0]) + (version[1] / 10f);
                    vtfHeader.headerSize = DataParser.ReadUInt(stream);
                    vtfHeader.width      = DataParser.ReadUShort(stream);
                    vtfHeader.height     = DataParser.ReadUShort(stream);
                    vtfHeader.flags      = DataParser.ReadUInt(stream);
                    vtfHeader.frames     = DataParser.ReadUShort(stream);
                    vtfHeader.firstFrame = DataParser.ReadUShort(stream);
                    vtfHeader.padding0   = new byte[4];
                    stream.Read(vtfHeader.padding0, 0, 4);
                    vtfHeader.reflectivity = new float[] { DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream) };
                    vtfHeader.padding1     = new byte[4];
                    stream.Read(vtfHeader.padding1, 0, 4);
                    vtfHeader.bumpmapScale       = DataParser.ReadFloat(stream);
                    vtfHeader.highResImageFormat = (VTFImageFormat)DataParser.ReadUInt(stream);
                    vtfHeader.mipmapCount        = DataParser.ReadByte(stream);
                    vtfHeader.lowResImageFormat  = (VTFImageFormat)DataParser.ReadUInt(stream);
                    vtfHeader.lowResImageWidth   = DataParser.ReadByte(stream);
                    vtfHeader.lowResImageHeight  = DataParser.ReadByte(stream);

                    vtfHeader.depth         = 1;
                    vtfHeader.resourceCount = 0;
                    vtfHeader.resources     = new VTFResource[0];

                    if (vtfHeader.version >= 7.2f)
                    {
                        vtfHeader.depth = DataParser.ReadUShort(stream);

                        if (vtfHeader.version >= 7.3)
                        {
                            vtfHeader.padding2 = new byte[3];
                            stream.Read(vtfHeader.padding2, 0, 3);
                            vtfHeader.resourceCount = DataParser.ReadUInt(stream);

                            if (vtfHeader.version >= 7.4)
                            {
                                vtfHeader.padding3 = new byte[8];
                                stream.Read(vtfHeader.padding3, 0, 8);
                                vtfHeader.resources = new VTFResource[vtfHeader.resourceCount];
                                for (int i = 0; i < vtfHeader.resources.Length; i++)
                                {
                                    vtfHeader.resources[i].type = DataParser.ReadUInt(stream);
                                    vtfHeader.resources[i].data = DataParser.ReadUInt(stream);
                                }
                            }
                        }
                    }
                    #endregion

                    int thumbnailBufferSize = 0;
                    int imageBufferSize     = (int)ComputeImageBufferSize(vtfHeader.width, vtfHeader.height, vtfHeader.depth, vtfHeader.mipmapCount, vtfHeader.highResImageFormat) * vtfHeader.frames;
                    if (vtfHeader.lowResImageFormat != VTFImageFormat.IMAGE_FORMAT_NONE)
                    {
                        thumbnailBufferSize = (int)ComputeImageBufferSize(vtfHeader.lowResImageWidth, vtfHeader.lowResImageHeight, 1, vtfHeader.lowResImageFormat);
                    }

                    int thumbnailBufferOffset = 0, imageBufferOffset = 0;

                    #region Read Resource Directories
                    if (vtfHeader.resources.Length > 0)
                    {
                        for (int i = 0; i < vtfHeader.resources.Length; i++)
                        {
                            if ((VTFResourceEntryType)vtfHeader.resources[i].type == VTFResourceEntryType.VTF_LEGACY_RSRC_LOW_RES_IMAGE)
                            {
                                thumbnailBufferOffset = (int)vtfHeader.resources[i].data;
                            }
                            if ((VTFResourceEntryType)vtfHeader.resources[i].type == VTFResourceEntryType.VTF_LEGACY_RSRC_IMAGE)
                            {
                                imageBufferOffset = (int)vtfHeader.resources[i].data;
                            }
                        }
                    }
                    else
                    {
                        thumbnailBufferOffset = (int)vtfHeader.headerSize;
                        imageBufferOffset     = thumbnailBufferOffset + thumbnailBufferSize;
                    }
                    #endregion

                    if (vtfHeader.highResImageFormat != VTFImageFormat.IMAGE_FORMAT_NONE)
                    {
                        int mipmapBufferOffset = 0;
                        for (uint i = 1; i <= vtfHeader.mipmapCount; i++)
                        {
                            mipmapBufferOffset += (int)ComputeMipmapSize(vtfHeader.width, vtfHeader.height, vtfHeader.depth, i, vtfHeader.highResImageFormat);
                        }
                        stream.Position = vtfBytePosition + imageBufferOffset + mipmapBufferOffset;

                        extracted = DecompressImage(stream, vtfHeader.width, vtfHeader.height, vtfHeader.highResImageFormat);
                        width     = vtfHeader.width;
                        height    = vtfHeader.height;
                    }
                    else
                    {
                        Debug.LogError("SourceTexture: Image format given was none");
                    }
                }
                else
                {
                    Debug.LogError("SourceTexture: Signature mismatch " + signature + " != " + VTFHeader.signature);
                }
            }
            else
            {
                Debug.LogError("SourceTexture: Missing VTF data");
            }

            return(extracted);
        }
示例#6
0
        private void ParseVertices(Stream stream, int rootLod)
        {
            for (int i = 0; i < rootLod; i++)
            {
                header.numLODVertices[i] = header.numLODVertices[rootLod];
            }

            //int lodIndex = 0;

            if (header.numLODs > 0)
            {
                Func <Stream, mstudiovertex_t> ReadVertexFromStream = (innerStream) =>
                {
                    mstudiovertex_t vertex = new mstudiovertex_t();

                    vertex.m_BoneWeights.weight = new float[MAX_NUM_BONES_PER_VERT]; //0 + 12 = 12
                    for (int k = 0; k < vertex.m_BoneWeights.weight.Length; k++)
                    {
                        vertex.m_BoneWeights.weight[k] = DataParser.ReadFloat(innerStream);
                    }

                    vertex.m_BoneWeights.bone = new char[MAX_NUM_BONES_PER_VERT]; //12 + 12 = 24
                    for (int k = 0; k < vertex.m_BoneWeights.bone.Length; k++)
                    {
                        vertex.m_BoneWeights.bone[k] = DataParser.ReadChar(innerStream);
                    }

                    vertex.m_BoneWeights.numbones = DataParser.ReadByte(innerStream); //24 + 1 = 25

                    float ex  = DataParser.ReadFloat(innerStream);                    //25 + 4 = 29
                    float why = DataParser.ReadFloat(innerStream);                    //33 + 4 = 37
                    float zee = DataParser.ReadFloat(innerStream);                    //29 + 4 = 33
                    vertex.m_vecPosition = new Vector3(ex, why, zee);

                    ex  = DataParser.ReadFloat(innerStream); //37 + 4 = 41
                    why = DataParser.ReadFloat(innerStream); //45 + 4 = 49
                    zee = DataParser.ReadFloat(innerStream); //41 + 4 = 45
                    vertex.m_vecNormal = new Vector3(ex, why, zee);

                    ex  = DataParser.ReadFloat(innerStream); //49 + 4 = 53
                    why = DataParser.ReadFloat(innerStream); //53 + 4 = 57
                    vertex.m_vecTexCoord = new Vector2(ex, 1 - why);
                    return(vertex);
                };

                stream.Position = fileOffsetPosition + header.vertexDataStart;

                //int vertexCount = header.numLODVertices[0];
                int vertexCount = 0;
                for (int i = 0; i < header.numLODVertices.Length; i++)
                {
                    vertexCount += header.numLODVertices[i];
                }

                vertices = new mstudiovertex_t[vertexCount];
                for (int i = 0; i < vertices.Length; i++)
                {
                    vertices[i] = ReadVertexFromStream(stream);
                }

                if (header.numFixups > 0)
                {
                    vertexCount = 0;
                    for (int fixupIndex = 0; fixupIndex < header.numFixups; fixupIndex++)
                    {
                        if (fileFixup[fixupIndex].lod < rootLod)
                        {
                            continue;
                        }

                        vertexCount += fileFixup[fixupIndex].numVertices;
                    }

                    mstudiovertex_t[] oldVertices = vertices;
                    vertices = new mstudiovertex_t[vertexCount];

                    int currentIndex = 0;
                    for (int fixupIndex = 0; fixupIndex < header.numFixups; fixupIndex++)
                    {
                        if (fileFixup[fixupIndex].lod < rootLod)
                        {
                            continue;
                        }

                        Array.Copy(oldVertices, fileFixup[fixupIndex].sourceVertexID, vertices, currentIndex, fileFixup[fixupIndex].numVertices);
                        currentIndex += fileFixup[fixupIndex].numVertices;
                    }
                }
            }
            else
            {
                Debug.LogError("VVDParser: Header's numLODs less than or equal to zero");
            }
        }
示例#7
0
        private void ParseEyeballs(Stream stream, long modelPosition, mstudiomodel_t model)
        {
            if (model.eyeballCount >= 0 && model.eyeballOffset != 0)
            {
                model.theEyeballs = new mstudioeyeball_t[model.eyeballCount];

                long nextEyeballPosition = modelPosition + model.eyeballOffset;
                for (int i = 0; i < model.theEyeballs.Length; i++)
                {
                    stream.Position = nextEyeballPosition;
                    long eyeballPosition = nextEyeballPosition;

                    model.theEyeballs[i] = new mstudioeyeball_t();

                    model.theEyeballs[i].nameOffset = DataParser.ReadInt(stream);
                    model.theEyeballs[i].boneIndex  = DataParser.ReadInt(stream);
                    model.theEyeballs[i].org        = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream));
                    model.theEyeballs[i].zOffset    = DataParser.ReadFloat(stream);
                    model.theEyeballs[i].radius     = DataParser.ReadFloat(stream);
                    model.theEyeballs[i].up         = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream));
                    model.theEyeballs[i].forward    = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream));
                    model.theEyeballs[i].texture    = DataParser.ReadInt(stream);

                    model.theEyeballs[i].unused1   = DataParser.ReadInt(stream);
                    model.theEyeballs[i].irisScale = DataParser.ReadFloat(stream);
                    model.theEyeballs[i].unused2   = DataParser.ReadInt(stream);

                    model.theEyeballs[i].upperFlexDesc = new int[3];
                    model.theEyeballs[i].lowerFlexDesc = new int[3];
                    model.theEyeballs[i].upperTarget   = new double[3];
                    model.theEyeballs[i].lowerTarget   = new double[3];

                    model.theEyeballs[i].upperFlexDesc[0] = DataParser.ReadInt(stream);
                    model.theEyeballs[i].upperFlexDesc[1] = DataParser.ReadInt(stream);
                    model.theEyeballs[i].upperFlexDesc[2] = DataParser.ReadInt(stream);
                    model.theEyeballs[i].lowerFlexDesc[0] = DataParser.ReadInt(stream);
                    model.theEyeballs[i].lowerFlexDesc[1] = DataParser.ReadInt(stream);
                    model.theEyeballs[i].lowerFlexDesc[2] = DataParser.ReadInt(stream);
                    model.theEyeballs[i].upperTarget[0]   = DataParser.ReadFloat(stream);
                    model.theEyeballs[i].upperTarget[1]   = DataParser.ReadFloat(stream);
                    model.theEyeballs[i].upperTarget[2]   = DataParser.ReadFloat(stream);
                    model.theEyeballs[i].lowerTarget[0]   = DataParser.ReadFloat(stream);
                    model.theEyeballs[i].lowerTarget[1]   = DataParser.ReadFloat(stream);
                    model.theEyeballs[i].lowerTarget[2]   = DataParser.ReadFloat(stream);

                    model.theEyeballs[i].upperLidFlexDesc = DataParser.ReadInt(stream);
                    model.theEyeballs[i].lowerLidFlexDesc = DataParser.ReadInt(stream);

                    model.theEyeballs[i].unused = new int[4];
                    for (int j = 0; j < model.theEyeballs[i].unused.Length; j++)
                    {
                        model.theEyeballs[i].unused[j] = DataParser.ReadInt(stream);
                    }

                    model.theEyeballs[i].eyeballIsNonFacs = DataParser.ReadByte(stream);

                    model.theEyeballs[i].unused3 = new char[3];
                    for (int j = 0; j < model.theEyeballs[i].unused3.Length; j++)
                    {
                        model.theEyeballs[i].unused3[j] = DataParser.ReadChar(stream);
                    }
                    model.theEyeballs[i].unused4 = new int[7];
                    for (int j = 0; j < model.theEyeballs[i].unused4.Length; j++)
                    {
                        model.theEyeballs[i].unused4[j] = DataParser.ReadInt(stream);
                    }

                    //Set the default value to -1 to distinguish it from value assigned to it by ReadMeshes()
                    model.theEyeballs[i].theTextureIndex = -1;

                    nextEyeballPosition = stream.Position;

                    if (model.theEyeballs[i].nameOffset != 0)
                    {
                        stream.Position = eyeballPosition + model.theEyeballs[i].nameOffset;

                        model.theEyeballs[i].name = DataParser.ReadNullTerminatedString(stream);
                    }
                    else
                    {
                        model.theEyeballs[i].name = "";
                    }
                }
            }
        }
示例#8
0
        private studiohdr_t ParseHeader1(Stream stream)
        {
            header1 = new studiohdr_t();

            stream.Position = fileBeginOffset;

            header1.id       = DataParser.ReadInt(stream); // Model format ID, such as "IDST" (0x49 0x44 0x53 0x54)
            header1.version  = DataParser.ReadInt(stream); // Format version number, such as 48 (0x30,0x00,0x00,0x00)
            header1.checkSum = DataParser.ReadInt(stream); // this has to be the same in the phy and vtx files to load!
            char[] name = new char[64];
            for (int i = 0; i < name.Length; i++)
            {
                name[i] = DataParser.ReadChar(stream);
            }
            header1.name = name;                             // The internal name of the model, padding with null bytes.
                                                             // Typically "my_model.mdl" will have an internal name of "my_model"
            this.name          = new string(name).Replace("\0", "");
            header1.dataLength = DataParser.ReadInt(stream); // Data size of MDL file in bytes.

            // A vector is 12 bytes, three 4-byte float-values in a row.
            header1.eyeposition   = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // Position of player viewpoint relative to model origin
            header1.illumposition = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // ?? Presumably the point used for lighting when per-vertex lighting is not enabled.
            header1.hull_min      = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // Corner of model hull box with the least X/Y/Z values
            header1.hull_max      = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // Opposite corner of model hull box
            header1.view_bbmin    = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // View Bounding Box Minimum Position
            header1.view_bbmax    = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // View Bounding Box Maximum Position

            header1.flags = DataParser.ReadInt(stream);                                                                                    // Binary flags in little-endian order.
                                                                                                                                           // ex (00000001,00000000,00000000,11000000) means flags for position 0, 30, and 31 are set.
                                                                                                                                           // Set model flags section for more information

            //Debug.Log("ID: " + header1.id + ", \nVersion: " + header1.version + ", \nCheckSum: " + header1.checkSum + ", \nName: " + this.name + ", \nLength: " + header1.dataLength);
            //Debug.Log("EyePos: " + header1.eyeposition + ", \nIllumPos: " + header1.illumposition + ", \nHullMin: " + header1.hull_min + ", \nHullMax: " + header1.hull_max + ", \nViewBBMin: " + header1.view_bbmin + ", \nViewBBMax: " + header1.view_bbmax);

            /*
             * After this point, the header contains many references to offsets
             * within the MDL file and the number of items at those offsets.
             *
             * Offsets are from the very beginning of the file.
             *
             * Note that indexes/counts are not always paired and ordered consistently.
             */

            // mstudiobone_t
            header1.bone_count  = DataParser.ReadInt(stream);   // Number of data sections (of type mstudiobone_t)
            header1.bone_offset = DataParser.ReadInt(stream);   // Offset of first data section

            // mstudiobonecontroller_t
            header1.bonecontroller_count  = DataParser.ReadInt(stream);
            header1.bonecontroller_offset = DataParser.ReadInt(stream);

            // mstudiohitboxset_t
            header1.hitbox_count  = DataParser.ReadInt(stream);
            header1.hitbox_offset = DataParser.ReadInt(stream);

            // mstudioanimdesc_t
            header1.localanim_count  = DataParser.ReadInt(stream);
            header1.localanim_offset = DataParser.ReadInt(stream);

            // mstudioseqdesc_t
            header1.localseq_count  = DataParser.ReadInt(stream);
            header1.localseq_offset = DataParser.ReadInt(stream);

            header1.activitylistversion = DataParser.ReadInt(stream); // ??
            header1.eventsindexed       = DataParser.ReadInt(stream); // ??

            // VMT texture filenames
            // mstudiotexture_t
            header1.texture_count  = DataParser.ReadInt(stream);
            header1.texture_offset = DataParser.ReadInt(stream);

            // This offset points to a series of ints.
            // Each int value, in turn, is an offset relative to the start of this header/the-file,
            // At which there is a null-terminated string.
            header1.texturedir_count  = DataParser.ReadInt(stream);
            header1.texturedir_offset = DataParser.ReadInt(stream);

            // Each skin-family assigns a texture-id to a skin location
            header1.skinreference_count = DataParser.ReadInt(stream);
            header1.skinrfamily_count   = DataParser.ReadInt(stream);
            header1.skinreference_index = DataParser.ReadInt(stream);

            // mstudiobodyparts_t
            header1.bodypart_count  = DataParser.ReadInt(stream);
            header1.bodypart_offset = DataParser.ReadInt(stream);

            // Local attachment points
            // mstudioattachment_t
            header1.attachment_count  = DataParser.ReadInt(stream);
            header1.attachment_offset = DataParser.ReadInt(stream);

            // Node values appear to be single bytes, while their names are null-terminated strings.
            header1.localnode_count      = DataParser.ReadInt(stream);
            header1.localnode_index      = DataParser.ReadInt(stream);
            header1.localnode_name_index = DataParser.ReadInt(stream);

            // mstudioflexdesc_t
            header1.flexdesc_count = DataParser.ReadInt(stream);
            header1.flexdesc_index = DataParser.ReadInt(stream);

            // mstudioflexcontroller_t
            header1.flexcontroller_count = DataParser.ReadInt(stream);
            header1.flexcontroller_index = DataParser.ReadInt(stream);

            // mstudioflexrule_t
            header1.flexrules_count = DataParser.ReadInt(stream);
            header1.flexrules_index = DataParser.ReadInt(stream);

            // IK probably referse to inverse kinematics
            // mstudioikchain_t
            header1.ikchain_count = DataParser.ReadInt(stream);
            header1.ikchain_index = DataParser.ReadInt(stream);

            // Information about any "mouth" on the model for speech animation
            // More than one sounds pretty creepy.
            // mstudiomouth_t
            header1.mouths_count = DataParser.ReadInt(stream);
            header1.mouths_index = DataParser.ReadInt(stream);

            // mstudioposeparamdesc_t
            header1.localposeparam_count = DataParser.ReadInt(stream);
            header1.localposeparam_index = DataParser.ReadInt(stream);

            /*
             * For anyone trying to follow along, as of this writing,
             * the next "surfaceprop_index" value is at position 0x0134 (308)
             * from the start of the file.
             */
            //stream.Position = 308;

            // Surface property value (single null-terminated string)
            header1.surfaceprop_index = DataParser.ReadInt(stream);

            // Unusual: In this one index comes first, then count.
            // Key-value data is a series of strings. If you can't find
            // what you're interested in, check the associated PHY file as well.
            header1.keyvalue_index = DataParser.ReadInt(stream);
            header1.keyvalue_count = DataParser.ReadInt(stream);

            // More inverse-kinematics
            // mstudioiklock_t
            header1.iklock_count = DataParser.ReadInt(stream);
            header1.iklock_index = DataParser.ReadInt(stream);


            header1.mass     = DataParser.ReadFloat(stream); // Mass of object (4-bytes)
            header1.contents = DataParser.ReadInt(stream);   // ??

            // Other models can be referenced for re-used sequences and animations
            // (See also: The $includemodel QC option.)
            // mstudiomodelgroup_t
            header1.includemodel_count = DataParser.ReadInt(stream);
            header1.includemodel_index = DataParser.ReadInt(stream);

            header1.virtualModel = DataParser.ReadInt(stream); // Placeholder for mutable-void*

            // mstudioanimblock_t
            header1.animblocks_name_index = DataParser.ReadInt(stream);
            header1.animblocks_count      = DataParser.ReadInt(stream);
            header1.animblocks_index      = DataParser.ReadInt(stream);

            header1.animblockModel = DataParser.ReadInt(stream); // Placeholder for mutable-void*

            // Points to a series of bytes?
            header1.bonetablename_index = DataParser.ReadInt(stream);

            header1.vertex_base = DataParser.ReadInt(stream); // Placeholder for void*
            header1.offset_base = DataParser.ReadInt(stream); // Placeholder for void*

            // Used with $constantdirectionallight from the QC
            // Model should have flag #13 set if enabled
            header1.directionaldotproduct = DataParser.ReadByte(stream);

            header1.rootLod = DataParser.ReadByte(stream); // Preferred rather than clamped

            // 0 means any allowed, N means Lod 0 -> (N-1)
            header1.numAllowedRootLods = DataParser.ReadByte(stream);

            //header.unused; // ??
            header1.unused1 = DataParser.ReadByte(stream);
            //header.unused; // ??
            header1.unused2 = DataParser.ReadInt(stream);

            // mstudioflexcontrollerui_t
            header1.flexcontrollerui_count = DataParser.ReadInt(stream);
            header1.flexcontrollerui_index = DataParser.ReadInt(stream);

            header1.vertAnimFixedPointScale = DataParser.ReadFloat(stream);
            header1.surfacePropLookup       = DataParser.ReadInt(stream);

            /**
             * Offset for additional header information.
             * May be zero if not present, or also 408 if it immediately
             * follows this studiohdr_t
             */
            // studiohdr2_t
            header1.studiohdr2index = DataParser.ReadInt(stream);

            //header.unused; // ??
            header1.unused3 = DataParser.ReadInt(stream);

            return(header1);
        }