public void Deserialize(BinaryReader Reader)
        {
            Tag = Reader.ReadUInt32();
            if (Tag != PACKAGE_FILE_TAG)
            {
                throw new Exception("Not a valid Unreal Engine package.");
            }

            FileVersion     = Reader.ReadUInt16();
            LicenseeVersion = Reader.ReadUInt16();

            TotalHeaderSize = Reader.ReadInt32();
            FolderName.Deserialize(Reader);
            PackageFlags = Reader.ReadUInt32();

            NameCount  = Reader.ReadInt32();
            NameOffset = Reader.ReadInt32();

            ExportCount  = Reader.ReadInt32();
            ExportOffset = Reader.ReadInt32();

            ImportCount  = Reader.ReadInt32();
            ImportOffset = Reader.ReadInt32();

            DependsOffset = Reader.ReadInt32();

            Unknown1 = Reader.ReadInt32();
            Unknown2 = Reader.ReadInt32();
            Unknown3 = Reader.ReadInt32();
            Unknown4 = Reader.ReadInt32();

            Guid.Deserialize(Reader);

            Generations.Deserialize(Reader);

            EngineVersion = Reader.ReadUInt32();
            CookerVersion = Reader.ReadUInt32();

            CompressionFlags = (ECompressionFlags)(Reader.ReadUInt32());

            CompressedChunks = new TArray <FCompressedChunkInfo>(() => new FCompressedChunkInfo(this));
            CompressedChunks.Deserialize(Reader);

            Unknown5 = Reader.ReadInt32();

            UnknownStringArray.Deserialize(Reader);
            UnknownTypeArray.Deserialize(Reader);

            GarbageSize = Reader.ReadInt32();
            CompressedChunkInfoOffset = Reader.ReadInt32();
            LastBlockSize             = Reader.ReadInt32();
        }
        private static void ProcessFile(string Path, string OutPath)
        {
            using (var Input = File.OpenRead(Path))
            {
                using (var Reader = new BinaryReader(Input))
                {
                    var Sum = new FPackageFileSummary();
                    Sum.Deserialize(Reader);

                    if ((Sum.CompressionFlags & ECompressionFlags.COMPRESS_ZLIB) == 0)
                    {
                        throw new Exception("Unsupported CompressionFlags");
                    }

                    // Decrypt the rest of the package header
                    var EncryptedSize = Sum.TotalHeaderSize - Sum.GarbageSize - Sum.NameOffset;
                    EncryptedSize = (EncryptedSize + 15) & ~15;                     // Round up to the next block

                    var EncryptedData = new byte[EncryptedSize];

                    Input.Seek(Sum.NameOffset, SeekOrigin.Begin);
                    Input.Read(EncryptedData, 0, EncryptedData.Length);

                    var DecryptedData = Decrypt(EncryptedData);

                    var ChunkInfo = new TArray <FCompressedChunkInfo>(() => new FCompressedChunkInfo(Sum));

                    using (var DecryptedStream = new MemoryStream(DecryptedData))
                    {
                        using (var DecryptedReader = new BinaryReader(DecryptedStream))
                        {
                            // Get the compressed chunk info from inside the encrypted data
                            DecryptedStream.Seek(Sum.CompressedChunkInfoOffset, SeekOrigin.Begin);
                            ChunkInfo.Deserialize(DecryptedReader);

                            // Store exports for reserialization
                            DecryptedStream.Seek(Sum.ExportOffset - Sum.NameOffset, SeekOrigin.Begin);
                        }
                    }

                    // Copy the original file data
                    var FileBuf = new byte[Input.Length];
                    Input.Seek(0, SeekOrigin.Begin);
                    Input.Read(FileBuf, 0, FileBuf.Length);

                    // Save to output file
                    using (var Output = File.Open(OutPath, FileMode.Create))
                    {
                        Output.Write(FileBuf, 0, FileBuf.Length);

                        // Write decrypted data
                        Output.Seek(Sum.NameOffset, SeekOrigin.Begin);
                        Output.Write(DecryptedData, 0, DecryptedData.Length);

                        // Decompress compressed chunks
                        foreach (var Chunk in ChunkInfo)
                        {
                            Input.Seek(Chunk.CompressedOffset, SeekOrigin.Begin);
                            var Header = new FCompressedChunkHeader();
                            Header.Deserialize(Reader);

                            var TotalBlockSize = 0;
                            var Blocks         = new List <FCompressedChunkBlock>();

                            while (TotalBlockSize < Header.Sum.UncompressedSize)
                            {
                                var Block = new FCompressedChunkBlock();
                                Block.Deserialize(Reader);
                                Blocks.Add(Block);
                                TotalBlockSize += Block.UncompressedSize;
                            }

                            Output.Seek(Chunk.UncompressedOffset, SeekOrigin.Begin);

                            foreach (var Block in Blocks)
                            {
                                var CompressedData = new byte[Block.CompressedSize];
                                Input.Read(CompressedData, 0, CompressedData.Length);

                                // Zlib inflate
                                var ZlibStream = new InflaterInputStream(new MemoryStream(CompressedData));
                                ZlibStream.CopyTo(Output);
                            }
                        }
                    }
                }
            }
        }