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