예제 #1
0
        private int ReadLumpValues <T>(LumpType type, int srcOffset, T[] dst, int dstOffset, int count)
            where T : struct
        {
            var info   = GetLumpInfo(type);
            var tSize  = Marshal.SizeOf <T>();
            var length = info.Length / tSize;

            if (srcOffset > length)
            {
                srcOffset = length;
            }
            if (srcOffset + count > length)
            {
                count = length - srcOffset;
            }

            if (count <= 0)
            {
                return(0);
            }

            using (var stream = GetLumpStream(type))
            {
                stream.Seek(tSize * srcOffset, SeekOrigin.Begin);
                LumpReader <T> .ReadLumpFromStream(stream, count, dst, dstOffset);
            }

            return(count);
        }
예제 #2
0
        public ValveVertexFile(Stream stream)
        {
            using (var reader = new BinaryReader(stream))
            {
                var id      = reader.ReadInt32();
                var version = reader.ReadInt32();

                Debug.Assert(id == 0x56534449);
                Debug.Assert(version == 4);

                reader.ReadInt32();

                NumLods   = reader.ReadInt32();
                _numVerts = new int[NumLods];

                for (var i = 0; i < 8; ++i)
                {
                    var count = reader.ReadInt32();
                    if (i < NumLods)
                    {
                        _numVerts[i] = count;
                    }
                }

                var numFixups        = reader.ReadInt32();
                var fixupTableStart  = reader.ReadInt32();
                var vertexDataStart  = reader.ReadInt32();
                var tangentDataStart = reader.ReadInt32();

                if (numFixups > 0)
                {
                    reader.BaseStream.Seek(fixupTableStart, SeekOrigin.Begin);
                    _fixups = LumpReader <VertexFixup> .ReadLumpFromStream(reader.BaseStream, numFixups);
                }

                HasTangents = tangentDataStart != 0;

                var vertLength = (int)(HasTangents ? tangentDataStart - vertexDataStart : stream.Length - vertexDataStart);

                reader.BaseStream.Seek(vertexDataStart, SeekOrigin.Begin);
                _vertices = LumpReader <StudioVertex> .ReadLumpFromStream(reader.BaseStream, vertLength / Marshal.SizeOf <StudioVertex>());

                if (HasTangents)
                {
                    var tangLength = (int)(stream.Length - tangentDataStart);
                    reader.BaseStream.Seek(tangentDataStart, SeekOrigin.Begin);
                    _tangents = LumpReader <Vector4> .ReadLumpFromStream(reader.BaseStream, tangLength / Marshal.SizeOf <Vector4>());
                }
            }
        }
예제 #3
0
            private void EnsureLoaded()
            {
                lock (this)
                {
                    if (Loaded)
                    {
                        return;
                    }

                    using (var reader = new BinaryReader(_bspFile.GetLumpStream(LumpType)))
                    {
                        _numClusters = reader.ReadInt32();
                        _vpsList     = new HashSet <int> [_numClusters];
                        _offsets     = LumpReader <ByteOffset> .ReadLumpFromStream(reader.BaseStream, _numClusters);
                    }
                }
            }
        public ValveVertexLightingFile(Stream stream)
        {
            using (var reader = new BinaryReader(stream))
            {
                var version = reader.ReadInt32();

                Debug.Assert(version == 2);

                var checksum  = reader.ReadInt32();
                var vertFlags = reader.ReadUInt32();
                var vertSize  = reader.ReadUInt32();
                var vertCount = reader.ReadUInt32();
                var meshCount = reader.ReadInt32();

                reader.ReadInt64(); // Unused
                reader.ReadInt64(); // Unused

                var meshHeaders = new List <VhvMeshHeader>();
                LumpReader <VhvMeshHeader> .ReadLumpFromStream(stream, meshCount, meshHeaders);

                _samples = new VertexData4[meshHeaders.Max(x => x.Lod) + 1][][];

                for (var i = 0; i < _samples.Length; ++i)
                {
                    _samples[i] = new VertexData4[meshHeaders.Count(x => x.Lod == i)][];
                }

                foreach (var meshHeader in meshHeaders)
                {
                    stream.Seek(meshHeader.VertOffset, SeekOrigin.Begin);

                    var samples   = _samples[meshHeader.Lod];
                    var meshIndex = Array.IndexOf(samples, null);

                    if (vertFlags == 2)
                    {
                        samples[meshIndex] = LumpReader <VertexData2> .ReadLumpFromStream(stream, meshHeader.VertCount, x => x.GetVertexColor());
                    }
                    else
                    {
                        samples[meshIndex] = LumpReader <VertexData4> .ReadLumpFromStream(stream, meshHeader.VertCount, x => x.GetVertexColor());
                    }
                }
            }
        }
예제 #5
0
            private void EnsureLoaded()
            {
                lock (this)
                {
                    if (_items != null)
                    {
                        return;
                    }

                    _items = new Dictionary <string, Item>();

                    using (var reader = new BinaryReader(_bspFile.GetLumpStream(LumpType)))
                    {
                        var count = reader.ReadInt32();
                        LumpReader <Item> .ReadLumpFromStream(reader.BaseStream, count, item => _items.Add(GetIdString(item.Id), item));
                    }
                }
            }
예제 #6
0
        public ValveTextureFile(Stream stream, bool onlyHeader = false)
        {
            Header = LumpReader <TextureHeader> .ReadSingleFromStream(stream);

            var readCount = Marshal.SizeOf <TextureHeader>();

            ZSliceCount = 1;

            if (Header.MajorVersion > 7 || Header.MajorVersion == 7 && Header.MinorVersion >= 2)
            {
                ZSliceCount = stream.ReadByte() | (stream.ReadByte() << 8);
                readCount  += 2;
            }

            MipmapCount = Header.MipMapCount;
            FrameCount  = Header.Frames;
            FaceCount   = (Header.Flags & TextureFlags.ENVMAP) != 0 ? 6 : 1;

            if (onlyHeader)
            {
                return;
            }

            var thumbSize = GetImageDataSize(Header.LowResWidth, Header.LowResHeight, 1, 1, Header.LowResFormat);

            VtfResource[] resources = null;
            var           buffer    = new byte[8];

            if (Header.MajorVersion > 7 || Header.MajorVersion == 7 && Header.MinorVersion >= 3)
            {
                Skip(stream, 3);
                readCount += 3;

                stream.Read(buffer, 0, 4);
                readCount += 4;

                var resourceCount = BitConverter.ToInt32(buffer, 0);

                // Probably padding?
                stream.Read(buffer, 0, 8);
                readCount += 8;

                resources = LumpReader <VtfResource> .ReadLumpFromStream(stream, resourceCount);

                readCount += Marshal.SizeOf <VtfResource>() * resourceCount;
            }

            if (resources == null || resources.Length == 0)
            {
                resources    = new VtfResource[2];
                resources[0] = new VtfResource(VtfResourceType.LowResImage, Header.HeaderSize);
                resources[1] = new VtfResource(VtfResourceType.HiResImage, Header.HeaderSize + (uint)thumbSize);
            }

            Skip(stream, Header.HeaderSize - readCount);
            readCount = (int)Header.HeaderSize;

            switch (Header.HiResFormat)
            {
            case TextureFormat.DXT1:
            case TextureFormat.DXT3:
            case TextureFormat.DXT5:
            case TextureFormat.I8:
            case TextureFormat.IA88:
            case TextureFormat.BGR888:
            case TextureFormat.RGB888:
            case TextureFormat.RGB888_BLUESCREEN:
            case TextureFormat.ABGR8888:
            case TextureFormat.BGRA8888:
            case TextureFormat.RGBA8888:
            case TextureFormat.RGBA16161616F:
                break;

            default:
                throw new NotImplementedException($"VTF format: {Header.HiResFormat}");
            }

            _imageData = new ImageData[MipmapCount * FrameCount * FaceCount * ZSliceCount];

            var offset = 0;

            for (var mipmap = MipmapCount - 1; mipmap >= 0; --mipmap)
            {
                var length = GetImageDataSize(Header.Width >> mipmap, Header.Height >> mipmap, 1, 1, Header.HiResFormat);

                for (var frame = 0; frame < FrameCount; ++frame)
                {
                    for (var face = 0; face < FaceCount; ++face)
                    {
                        for (var zslice = 0; zslice < ZSliceCount; ++zslice)
                        {
                            var index = GetImageDataIndex(mipmap, frame, face, zslice);
                            _imageData[index] = new ImageData(offset, length);
                            offset           += length;
                        }
                    }
                }
            }

            var hiResEntry = resources.First(x => x.Type == VtfResourceType.HiResImage);

            Skip(stream, hiResEntry.Data - readCount);

            _hiResPixelData = new byte[offset];
            stream.Read(_hiResPixelData, 0, offset);
        }
        public ValveVertexLightingFile(Stream stream)
        {
            using (var reader = new BinaryReader(stream))
            {
                var version    = (int)reader.ReadByte();
                var baseOffset = 0;

                if (version == 0)
                {
                    // First 3 bytes are missing :(
                    version    = 2;
                    baseOffset = -3;
                }
                else
                {
                    version |= reader.ReadByte() << 8;
                    version |= reader.ReadUInt16() << 16;
                }

                if (version != 2)
                {
                    throw new NotSupportedException($"Vertex lighting file version {version:x} is not supported.");
                }

                var checksum  = reader.ReadInt32();
                var vertFlags = reader.ReadUInt32();
                var vertSize  = reader.ReadUInt32();
                var vertCount = reader.ReadUInt32();
                var meshCount = reader.ReadInt32();

                var unused0 = reader.ReadInt64(); // Unused
                var unused1 = reader.ReadInt64(); // Unused

                var meshHeaders = new List <VhvMeshHeader>();
                LumpReader <VhvMeshHeader> .ReadLumpFromStream(stream, meshCount, meshHeaders);

                _samples = new VertexData4[meshHeaders.Max(x => x.Lod) + 1][][];

                for (var i = 0; i < _samples.Length; ++i)
                {
                    _samples[i] = new VertexData4[meshHeaders.Count(x => x.Lod == i)][];
                }

                foreach (var meshHeader in meshHeaders)
                {
                    stream.Seek(meshHeader.VertOffset + baseOffset, SeekOrigin.Begin);

                    var samples   = _samples[meshHeader.Lod];
                    var meshIndex = Array.IndexOf(samples, null);

                    if (vertFlags == 2)
                    {
                        samples[meshIndex] = LumpReader <VertexData2> .ReadLumpFromStream(stream, meshHeader.VertCount, x => x.GetVertexColor());
                    }
                    else
                    {
                        samples[meshIndex] = LumpReader <VertexData4> .ReadLumpFromStream(stream, meshHeader.VertCount, x => x.GetVertexColor());
                    }
                }
            }
        }
예제 #8
0
        private int ReadLumpValues <T>(LumpType type, int srcOffset, T[] dst, int dstOffset, int count)
            where T : struct
        {
            var info = GetLumpInfo(type);

            // 0 = no compression
            if (info.UncompressedSize == 0)
            {
                var tSize  = Marshal.SizeOf <T>();
                var length = info.Length / tSize;

                if (srcOffset > length)
                {
                    srcOffset = length;
                }
                if (srcOffset + count > length)
                {
                    count = length - srcOffset;
                }

                if (count <= 0)
                {
                    return(0);
                }

                using (var stream = GetLumpStream(type))
                {
                    stream.Seek(tSize * srcOffset, SeekOrigin.Begin);
                    LumpReader <T> .ReadLumpFromStream(stream, count, dst, dstOffset);
                }
            }
            else
            {
                // LZMA compressed lump

                if (type == LumpType.GAME_LUMP)
                {
                    // game lumps are compressed individually
                    // https://developer.valvesoftware.com/wiki/Source_BSP_File_Format#Lump_compression
                    throw new NotImplementedException();
                }

                using (var stream = GetSubStream(info.Offset, LzmaHeader.Size))
                {
                    var lzmaHeader = LzmaHeader.Read(stream);

                    using (var compressedStream = GetSubStream(info.Offset + LzmaHeader.Size, lzmaHeader.LzmaSize))
                    {
                        using (var uncompressedStream = new MemoryStream(info.UncompressedSize))
                        {
                            Decoder decoder = new Decoder();
                            decoder.SetDecoderProperties(lzmaHeader.Properties);
                            decoder.Code(compressedStream, uncompressedStream, lzmaHeader.LzmaSize, lzmaHeader.ActualSize, null);

                            var tSize  = Marshal.SizeOf <T>();
                            var length = info.UncompressedSize / tSize;

                            if (srcOffset > length)
                            {
                                srcOffset = length;
                            }
                            if (srcOffset + count > length)
                            {
                                count = length - srcOffset;
                            }

                            if (count <= 0)
                            {
                                return(0);
                            }

                            uncompressedStream.Seek(tSize * srcOffset, SeekOrigin.Begin);
                            LumpReader <T> .ReadLumpFromStream(uncompressedStream, count, dst, dstOffset);

                            if (type == LumpType.PLANES)
                            {
                                return(count);
                            }
                        }
                    }
                }
            }

            return(count);
        }
예제 #9
0
        public StudioModelFile(Stream stream)
        {
            _header = LumpReader <Header> .ReadSingleFromStream(stream);

            if (_header.Id != 0x54534449)
            {
                throw new Exception("Not a MDL file.");
            }

            _materials               = new StudioTexture[_header.NumTextures];
            _materialNames           = new string[_header.NumTextures];
            _cachedFullMaterialPaths = new string[_header.NumTextures];

            stream.Seek(_header.TextureIndex, SeekOrigin.Begin);
            LumpReader <StudioTexture> .ReadLumpFromStream(stream, _header.NumTextures, (index, tex) =>
            {
                _materials[index] = tex;

                stream.Seek(tex.NameIndex, SeekOrigin.Current);
                _materialNames[index] = ReadNullTerminatedString(stream).Replace('\\', '/') + ".vmt";
            });

            _materialPaths = new string[_header.NumCdTextures];

            stream.Seek(_header.CdTextureIndex, SeekOrigin.Begin);
            LumpReader <int> .ReadLumpFromStream(stream, _header.NumCdTextures, (index, cdTex) =>
            {
                stream.Seek(cdTex, SeekOrigin.Begin);
                _materialPaths[index] = ReadNullTerminatedString(stream).Replace('\\', '/');
            });

            _bodyParts     = new StudioBodyPart[_header.NumBodyParts];
            _bodyPartNames = new string[_header.NumBodyParts];

            var modelList = new List <StudioModel>();
            var meshList  = new List <StudioMesh>();

            stream.Seek(_header.BodyPartIndex, SeekOrigin.Begin);
            LumpReader <StudioBodyPart> .ReadLumpFromStream(stream, _header.NumBodyParts, (partIndex, part) =>
            {
                var partPos = stream.Position;

                stream.Seek(partPos + part.NameIndex, SeekOrigin.Begin);
                _bodyPartNames[partIndex] = ReadNullTerminatedString(stream);

                stream.Seek(partPos + part.ModelIndex, SeekOrigin.Begin);

                // Now indexes into array of models
                part.ModelIndex = modelList.Count;

                LumpReader <StudioModel> .ReadLumpFromStream(stream, part.NumModels, (modelIndex, model) =>
                {
                    var modelPos = stream.Position;

                    stream.Seek(modelPos + model.MeshIndex, SeekOrigin.Begin);

                    model.MeshIndex = meshList.Count;
                    LumpReader <StudioMesh> .ReadLumpFromStream(stream, model.NumMeshes, (meshIndex, mesh) =>
                    {
                        mesh.ModelIndex = modelIndex;
                        meshList.Add(mesh);
                    });

                    modelList.Add(model);
                });

                _bodyParts[partIndex] = part;
            });

            _models = modelList.ToArray();
            _meshes = meshList.ToArray();
        }
예제 #10
0
        public ValveTriangleFile(Stream stream, StudioModelFile mdl, ValveVertexFile vvd)
        {
            var outIndices  = new List <int>();
            var outVertices = new List <StudioVertex>();

            using (var reader = new BinaryReader(stream))
            {
                var version = reader.ReadInt32();

                Debug.Assert(version == 7);

                var vertCacheSize    = reader.ReadInt32();
                var maxBonesPerStrip = reader.ReadUInt16();
                var maxBonesPerTri   = reader.ReadUInt16();
                var maxBonesPerVert  = reader.ReadInt32();

                var checksum = reader.ReadInt32();

                var numLods = NumLods = reader.ReadInt32();
                var matReplacementListOffset = reader.ReadInt32();

                var origVerts = new StudioVertex[numLods][];

                for (var i = 0; i < numLods; ++i)
                {
                    origVerts[i] = new StudioVertex[vvd.GetVertexCount(i)];
                    vvd.GetVertices(i, origVerts[i]);
                }

                var numBodyParts   = reader.ReadInt32();
                var bodyPartOffset = reader.ReadInt32();

                var verts   = new List <OptimizedVertex>();
                var indices = new List <ushort>();

                _bodyParts = new BodyPartHeader[numBodyParts];

                var modelList    = new List <ModelHeader>();
                var modelLodList = new List <ModelLodHeader>();
                var meshList     = new List <MeshData>();

                reader.BaseStream.Seek(bodyPartOffset, SeekOrigin.Begin);
                LumpReader <BodyPartHeader> .ReadLumpFromStream(reader.BaseStream, numBodyParts, (bodyPartIndex, bodyPart) =>
                {
                    reader.BaseStream.Seek(bodyPart.ModelOffset, SeekOrigin.Current);

                    bodyPart.ModelOffset = modelList.Count;

                    LumpReader <ModelHeader> .ReadLumpFromStream(reader.BaseStream, bodyPart.NumModels, (modelIndex, model) =>
                    {
                        reader.BaseStream.Seek(model.LodOffset, SeekOrigin.Current);

                        model.LodOffset = modelLodList.Count;

                        LumpReader <ModelLodHeader> .ReadLumpFromStream(reader.BaseStream, model.NumLods, (lodIndex, lod) =>
                        {
                            // TODO
                            if (lodIndex > 0)
                            {
                                return;
                            }

                            reader.BaseStream.Seek(lod.MeshOffset, SeekOrigin.Current);

                            lod.MeshOffset = meshList.Count;

                            var lodVerts       = origVerts[lodIndex];
                            var firstLodIndex  = outVertices.Count;
                            var firstLodVertex = outVertices.Count;

                            LumpReader <MeshHeader> .ReadLumpFromStream(reader.BaseStream, lod.NumMeshes, (meshIndex, mesh) =>
                            {
                                var meshData = new MeshData
                                {
                                    LodIndexOffset  = firstLodIndex,
                                    LodVertexOffset = firstLodVertex,
                                    IndexOffset     = outIndices.Count,
                                    VertexOffset    = outVertices.Count
                                };

                                var meshInfo       = mdl.GetMesh(bodyPartIndex, modelIndex, meshIndex);
                                var origVertOffset = meshInfo.VertexOffset;

                                reader.BaseStream.Seek(mesh.StripGroupHeaderOffset, SeekOrigin.Current);
                                LumpReader <StripGroupHeader> .ReadLumpFromStream(reader.BaseStream, mesh.NumStripGroups, stripGroup =>
                                {
                                    verts.Clear();
                                    indices.Clear();

                                    var start = reader.BaseStream.Position;
                                    reader.BaseStream.Seek(start + stripGroup.VertOffset, SeekOrigin.Begin);
                                    LumpReader <OptimizedVertex> .ReadLumpFromStream(reader.BaseStream,
                                                                                     stripGroup.NumVerts, verts);

                                    var meshIndexOffset = outVertices.Count - firstLodVertex;
                                    for (var i = 0; i < verts.Count; ++i)
                                    {
                                        var vertIndex = origVertOffset + verts[i].OrigMeshVertId;
                                        if (vertIndex < 0 || vertIndex >= lodVerts.Length)
                                        {
                                            throw new IndexOutOfRangeException();
                                        }
                                        outVertices.Add(lodVerts[vertIndex]);
                                    }

                                    reader.BaseStream.Seek(start + stripGroup.IndexOffset, SeekOrigin.Begin);
                                    LumpReader <ushort> .ReadLumpFromStream(reader.BaseStream,
                                                                            stripGroup.NumIndices, indices);

                                    reader.BaseStream.Seek(start + stripGroup.StripOffset, SeekOrigin.Begin);
                                    LumpReader <StripHeader> .ReadLumpFromStream(reader.BaseStream, stripGroup.NumStrips, strip =>
                                    {
                                        Debug.Assert(strip.Flags != StripHeaderFlags.IsTriStrip);

                                        for (var i = 0; i < strip.NumIndices; ++i)
                                        {
                                            outIndices.Add(meshIndexOffset + indices[strip.IndexOffset + i]);
                                        }
                                    });
                                });

                                meshData.IndexCount  = outIndices.Count - meshData.IndexOffset;
                                meshData.VertexCount = outVertices.Count - meshData.VertexOffset;

                                meshList.Add(meshData);
                            });

                            modelLodList.Add(lod);
                        });

                        modelList.Add(model);
                    });

                    _bodyParts[bodyPartIndex] = bodyPart;
                });

                _models    = modelList.ToArray();
                _modelLods = modelLodList.ToArray();
                _meshes    = meshList.ToArray();

                _indices  = outIndices.ToArray();
                _vertices = outVertices.ToArray();
            }
        }