Example #1
0
        /// <summary>
        /// Reads the entry from the VPK package.
        /// </summary>
        /// <param name="entry">Package entry.</param>
        /// <param name="progressReporter">Progress report callback.</param>
        /// <param name="validateCrc">If true, CRC32 will be calculated and verified for read data.</param>
        /// <returns>Output buffer.</returns>
        public async Task <Memory <byte> > ReadEntryAsync(PackageEntry entry, IProgress <int> progressReporter, bool validateCrc = true)
        {
            DecompressState decompressState = null;
            var             parameters      = new DecompressParameters();

            parameters.DictSizeLog2 = 20;

            var blockReadBuffer = new Memory <byte>(new byte[MAX_ENTRY_CHUNK_SIZE]);
            var outputBuffer    = new Memory <byte>(new byte[entry.SmallData.Length + entry.TotalLength]);
            var outputOffset    = 0;

            if (entry.SmallData.Length > 0)
            {
                entry.SmallData.CopyTo(outputBuffer);
                outputOffset += entry.SmallData.Length;
            }

            if (entry.TotalCompressedLength < entry.TotalLength)
            {
                decompressState = Lzham.DecompressInit(parameters);
            }

            if (entry.TotalLength > 0)
            {
                Stream fs = null;
                ushort currentArchiveIndex = 0x7FFF;

                try
                {
                    foreach (var chunk in entry.Chunks)
                    {
                        var streamOffset = chunk.Offset;

                        if (currentArchiveIndex != chunk.ArchiveIndex)
                        {
                            currentArchiveIndex = chunk.ArchiveIndex;
                            fs?.Close();
                        }

                        if (chunk.ArchiveIndex != 0x7FFF)
                        {
                            if (currentArchiveIndex != chunk.ArchiveIndex)
                            {
                                currentArchiveIndex = chunk.ArchiveIndex;
                                fs?.Close();
                            }

                            if (!IsDirVPK)
                            {
                                throw new InvalidOperationException("Given VPK is not a _dir, but entry is referencing an external archive.");
                            }

                            var vpkDirectory = Path.GetDirectoryName(FileName);
                            var vpkName      = Path.GetFileName(FileName);

                            foreach (var prefix in VPK_PREFIXES)
                            {
                                if (vpkName.StartsWith(prefix))
                                {
                                    vpkName = vpkName.Substring(prefix.Length);
                                    break;
                                }
                            }

                            var fileName = Path.Combine(vpkDirectory, $"{vpkName}_{chunk.ArchiveIndex:D3}.vpk");

                            fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                        }
                        else
                        {
                            fs = Reader.BaseStream;

                            streamOffset += HeaderSize + TreeSize;
                        }

                        fs.Seek(streamOffset, SeekOrigin.Begin);
                        var readBuffer = blockReadBuffer.Slice(0, (int)chunk.CompressedLength);
                        var bytesRead  = await fs.ReadAsync(readBuffer);

                        if (bytesRead != chunk.CompressedLength)
                        {
                            throw new InvalidOperationException($"Attempted to read {chunk.CompressedLength} bytes, got {bytesRead.ToString()}.");
                        }

                        if (chunk.CompressedLength < chunk.Length)
                        {
                            var decompressedSpan = outputBuffer.Slice(outputOffset);
                            var decompressResult = Lzham.DecompressMemory(parameters, readBuffer, ref decompressedSpan);

                            if (decompressResult != DecompressStatus.Success)
                            {
                                throw new InvalidOperationException($"Error attempting to decompress: {decompressResult.ToString()}");
                            }

                            outputOffset += (int)chunk.Length;
                        }
                        else
                        {
                            readBuffer.CopyTo(outputBuffer.Slice(outputOffset));

                            outputOffset += bytesRead;
                        }

                        if (progressReporter != null)
                        {
                            progressReporter.Report(outputOffset);
                        }
                    }
                }
                finally
                {
                    if (currentArchiveIndex != 0x7FFF)
                    {
                        fs?.Close();
                    }
                }
            }

            if (validateCrc && entry.CRC32 != Crc32.Compute(outputBuffer))
            {
                throw new InvalidDataException("CRC32 mismatch for read data.");
            }

            if (decompressState != null)
            {
                var deinitResult = Lzham.DecompressDeinit(decompressState);

                if (deinitResult >= DecompressStatus.FirstFailureCode)
                {
                    throw new InvalidOperationException($"Error attempting to deinitialize compressor: {deinitResult.ToString()}");
                }
            }

            return(outputBuffer);
        }
Example #2
0
        private void ReadEntries()
        {
            var typeEntries = new Dictionary <string, List <PackageEntry> >();

            // Types
            while (true)
            {
                var typeName = Reader.ReadNullTermString(Encoding.UTF8);

                if (string.IsNullOrEmpty(typeName))
                {
                    break;
                }

                var entries = new List <PackageEntry>();

                // Directories
                while (true)
                {
                    var directoryName = Reader.ReadNullTermString(Encoding.UTF8);

                    if (directoryName?.Length == 0)
                    {
                        break;
                    }

                    // Files
                    while (true)
                    {
                        var fileName = Reader.ReadNullTermString(Encoding.UTF8);

                        if (fileName?.Length == 0)
                        {
                            break;
                        }

                        var entry = new PackageEntry
                        {
                            FileName      = fileName,
                            DirectoryName = directoryName,
                            TypeName      = typeName,
                            CRC32         = Reader.ReadUInt32(),
                            SmallData     = new byte[Reader.ReadUInt16()],
                        };

                        var entryChunks         = new List <PackageEntryChunk>();
                        PackageEntryChunk chunk = null;

                        var archiveIndex = Reader.ReadUInt16();

                        while (archiveIndex != 0xFFFF)
                        {
                            chunk = new PackageEntryChunk()
                            {
                                ArchiveIndex     = archiveIndex,
                                Unknown1         = Reader.ReadBytes(6),
                                Offset           = Reader.ReadUInt32(),
                                Unknown2         = Reader.ReadBytes(4),
                                CompressedLength = Reader.ReadUInt32(),
                                Unknown3         = Reader.ReadBytes(4),
                                Length           = Reader.ReadUInt32(),
                                Unknown4         = Reader.ReadBytes(4),
                            };

                            entryChunks.Add(chunk);

                            archiveIndex = Reader.ReadUInt16();
                        }

                        entry.Chunks = entryChunks.ToArray();

                        if (entry.SmallData.Length > 0)
                        {
                            Reader.Read(entry.SmallData, 0, entry.SmallData.Length);
                        }

                        entries.Add(entry);
                    }
                }

                typeEntries.Add(typeName, entries);
            }

            Entries = typeEntries;
        }
Example #3
0
 /// <summary>
 /// Reads the entry from the VPK package.
 /// </summary>
 /// <param name="entry">Package entry.</param>
 /// <param name="validateCrc">If true, CRC32 will be calculated and verified for read data.</param>
 /// <returns>Output buffer.</returns>
 public Task <Memory <byte> > ReadEntryAsync(PackageEntry entry, bool validateCrc = true)
 {
     return(ReadEntryAsync(entry, null, validateCrc));
 }