Example #1
0
        /// <summary>
        /// Reads the given <see cref="Stream"/>.
        /// </summary>
        /// <param name="input">The input <see cref="Stream"/> to read from.</param>
        public void Read(Stream input)
        {
            Reader = new BinaryReader(input);

            FileSize = Reader.ReadUInt32();

            if (FileSize == 0x55AA1234)
            {
                throw new InvalidDataException("Use ValvePak library to parse VPK files.\nSee https://github.com/SteamDatabase/ValvePak");
            }

            if (FileSize == CompiledShader.MAGIC)
            {
                throw new InvalidDataException("Use CompiledShader() class to parse compiled shader files.");
            }

            // TODO: Some real files seem to have different file size
            if (FileSize != Reader.BaseStream.Length)
            {
                //throw new Exception(string.Format("File size does not match size specified in file. {0} != {1}", FileSize, Reader.BaseStream.Length));
            }

            HeaderVersion = Reader.ReadUInt16();

            if (HeaderVersion != KnownHeaderVersion)
            {
                throw new InvalidDataException(string.Format("Bad header version. ({0} != expected {1})", HeaderVersion, KnownHeaderVersion));
            }

            Version = Reader.ReadUInt16();

            var blockOffset = Reader.ReadUInt32();
            var blockCount  = Reader.ReadUInt32();

            Reader.BaseStream.Position += blockOffset - 8; // 8 is 2 uint32s we just read

            for (var i = 0; i < blockCount; i++)
            {
                var blockType = Encoding.UTF8.GetString(Reader.ReadBytes(4));

                var   position = Reader.BaseStream.Position;
                var   offset   = (uint)position + Reader.ReadUInt32();
                var   size     = Reader.ReadUInt32();
                Block block    = null;

                // Peek data to detect VKV3
                // Valve has deprecated NTRO as reported by resourceinfo.exe
                // TODO: Find a better way without checking against resource type
                if (size >= 4 && blockType == "DATA" && !IshandledResourceType(ResourceType))
                {
                    Reader.BaseStream.Position = offset;

                    var magic = Reader.ReadUInt32();

                    if (magic == BinaryKV3.MAGIC || magic == BinaryKV3.MAGIC2 || magic == BinaryKV3.MAGIC3)
                    {
                        block = new BinaryKV3();
                    }
                    else if (magic == BinaryKV1.MAGIC)
                    {
                        block = new BinaryKV1();
                    }

                    Reader.BaseStream.Position = position;
                }

                if (block == null)
                {
                    block = ConstructFromType(blockType);
                }

                block.Offset = offset;
                block.Size   = size;

                if (blockType == "REDI" || blockType == "NTRO")
                {
                    block.Read(Reader, this);
                }

                Blocks.Add(block);

                switch (block.Type)
                {
                case BlockType.REDI:
                    // Try to determine resource type by looking at first compiler indentifier
                    if (ResourceType == ResourceType.Unknown && EditInfo.Structs.ContainsKey(ResourceEditInfo.REDIStruct.SpecialDependencies))
                    {
                        var specialDeps = (SpecialDependencies)EditInfo.Structs[ResourceEditInfo.REDIStruct.SpecialDependencies];

                        if (specialDeps.List.Count > 0)
                        {
                            ResourceType = DetermineResourceTypeByCompilerIdentifier(specialDeps.List[0]);
                        }
                    }

                    break;

                case BlockType.NTRO:
                    if (ResourceType == ResourceType.Unknown && IntrospectionManifest.ReferencedStructs.Count > 0)
                    {
                        switch (IntrospectionManifest.ReferencedStructs[0].Name)
                        {
                        case "VSoundEventScript_t":
                            ResourceType = ResourceType.SoundEventScript;
                            break;

                        case "CWorldVisibility":
                            ResourceType = ResourceType.WorldVisibility;
                            break;
                        }
                    }

                    break;
                }

                Reader.BaseStream.Position = position + 8;
            }

            foreach (var block in Blocks)
            {
                if (block.Type != BlockType.REDI && block.Type != BlockType.NTRO)
                {
                    block.Read(Reader, this);
                }
            }
        }
Example #2
0
        /// <summary>
        /// Reads the given <see cref="Stream"/>.
        /// </summary>
        /// <param name="input">The input <see cref="Stream"/> to read from.</param>
        /// <param name="verifyFileSize">Whether to verify that the stream was correctly consumed.</param>
        public void Read(Stream input, bool verifyFileSize = true)
        {
            Reader = new BinaryReader(input);

            FileSize = Reader.ReadUInt32();

            if (FileSize == 0x55AA1234)
            {
                throw new InvalidDataException("Use ValvePak library to parse VPK files.\nSee https://github.com/SteamDatabase/ValvePak");
            }

            if (FileSize == ShaderFile.MAGIC)
            {
                throw new InvalidDataException("Use CompiledShader() class to parse compiled shader files.");
            }

            HeaderVersion = Reader.ReadUInt16();

            if (HeaderVersion != KnownHeaderVersion)
            {
                throw new UnexpectedMagicException($"Unexpected header (expected {KnownHeaderVersion})", HeaderVersion, nameof(HeaderVersion));
            }

            if (FileName != null)
            {
                ResourceType = DetermineResourceTypeByFileExtension();
            }

            Version = Reader.ReadUInt16();

            var blockOffset = Reader.ReadUInt32();
            var blockCount  = Reader.ReadUInt32();

            Reader.BaseStream.Position += blockOffset - 8; // 8 is 2 uint32s we just read

            for (var i = 0; i < blockCount; i++)
            {
                var blockType = Encoding.UTF8.GetString(Reader.ReadBytes(4));

                var   position = Reader.BaseStream.Position;
                var   offset   = (uint)position + Reader.ReadUInt32();
                var   size     = Reader.ReadUInt32();
                Block block    = null;

                // Peek data to detect VKV3
                // Valve has deprecated NTRO as reported by resourceinfo.exe
                // TODO: Find a better way without checking against resource type
                if (size >= 4 && blockType == nameof(BlockType.DATA) && !IsHandledResourceType(ResourceType))
                {
                    Reader.BaseStream.Position = offset;

                    var magic = Reader.ReadUInt32();

                    if (magic == BinaryKV3.MAGIC || magic == BinaryKV3.MAGIC2 || magic == BinaryKV3.MAGIC3)
                    {
                        block = new BinaryKV3();
                    }
                    else if (magic == BinaryKV1.MAGIC)
                    {
                        block = new BinaryKV1();
                    }

                    Reader.BaseStream.Position = position;
                }

                if (block == null)
                {
                    block = ConstructFromType(blockType);
                }

                block.Offset = offset;
                block.Size   = size;

                Blocks.Add(block);

                switch (block.Type)
                {
                case BlockType.REDI:
                case BlockType.RED2:
                    block.Read(Reader, this);

                    EditInfo = (ResourceEditInfo)block;

                    // Try to determine resource type by looking at first compiler indentifier
                    if (ResourceType == ResourceType.Unknown && EditInfo.Structs.TryGetValue(ResourceEditInfo.REDIStruct.SpecialDependencies, out var specialBlock))
                    {
                        var specialDeps = (SpecialDependencies)specialBlock;

                        if (specialDeps.List.Count > 0)
                        {
                            ResourceType = DetermineResourceTypeByCompilerIdentifier(specialDeps.List[0]);
                        }
                    }

                    // Try to determine resource type by looking at the input dependency if there is only one
                    if (ResourceType == ResourceType.Unknown && EditInfo.Structs.TryGetValue(ResourceEditInfo.REDIStruct.InputDependencies, out var inputBlock))
                    {
                        var inputDeps = (InputDependencies)inputBlock;

                        if (inputDeps.List.Count == 1)
                        {
                            ResourceType = DetermineResourceTypeByFileExtension(Path.GetExtension(inputDeps.List[0].ContentRelativeFilename));
                        }
                    }

                    break;

                case BlockType.NTRO:
                    block.Read(Reader, this);

                    if (ResourceType == ResourceType.Unknown && IntrospectionManifest.ReferencedStructs.Count > 0)
                    {
                        switch (IntrospectionManifest.ReferencedStructs[0].Name)
                        {
                        case "VSoundEventScript_t":
                            ResourceType = ResourceType.SoundEventScript;
                            break;

                        case "CWorldVisibility":
                            ResourceType = ResourceType.WorldVisibility;
                            break;
                        }
                    }

                    break;
                }

                Reader.BaseStream.Position = position + 8;
            }

            foreach (var block in Blocks)
            {
                if (block.Type is not BlockType.REDI and not BlockType.RED2 and not BlockType.NTRO)
                {
                    block.Read(Reader, this);
                }
            }

            if (verifyFileSize && Reader.BaseStream.Length != FullFileSize)
            {
                if (ResourceType == ResourceType.Texture)
                {
                    var data = (Texture)DataBlock;

                    // TODO: We do not currently have a way of calculating buffer size for these types
                    // Texture.GenerateBitmap also just reads until end of the buffer
                    if (data.Format == VTexFormat.JPEG_DXT5 || data.Format == VTexFormat.JPEG_RGBA8888)
                    {
                        return;
                    }
                }

                throw new InvalidDataException($"File size ({Reader.BaseStream.Length}) does not match size specified in file ({FullFileSize}) ({ResourceType}).");
            }
        }