Esempio n. 1
0
        private void Decompress(Stream stream, byte[] key, string[] compressionMethods, byte[] outData)
        {
            if (compressionMethods == null || compressionMethods.Length == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(compressionMethods), "CompressionMethods are null or empty");
            }

            string compressionMethod = compressionMethods[CompressionMethodIndex - 1]; // -1 because we dont have 'NAME_None' in the array
            int    bytesRead         = 0;

            for (int i = 0; i < CompressionBlocks.Length; i++)
            {
                stream.Position = Offset + CompressionBlocks[i].CompressedStart;
                int uncompressedSize = (int)Math.Min(CompressionBlockSize, outData.Length - bytesRead);

                byte[] blockBbuffer;
                if (Encrypted)
                {
                    blockBbuffer = new byte[BinaryHelper.Align(CompressionBlocks[i].Size, AESDecryptor.BLOCK_SIZE)];
                    stream.Read(blockBbuffer, 0, blockBbuffer.Length);
                    blockBbuffer = AESDecryptor.DecryptAES(blockBbuffer, key);
                }
                else
                {
                    blockBbuffer = new byte[CompressionBlocks[i].Size];
                    stream.Read(blockBbuffer, 0, blockBbuffer.Length);
                }

                using var blockMs = new MemoryStream(blockBbuffer, false);
                using Stream compressionStream = compressionMethod switch
                      {
                          "Zlib" => new ZlibStream(blockMs, CompressionMode.Decompress),
                          "Gzip" => new GZipStream(blockMs, CompressionMode.Decompress),
                          "Oodle" => new OodleStream(blockBbuffer, uncompressedSize),
                          _ => throw new NotImplementedException($"Decompression not yet implemented ({compressionMethod})")
                      };

                bytesRead += compressionStream.Read(outData, bytesRead, uncompressedSize);
            }
        }
Esempio n. 2
0
        FPakEntry GetEntry(string name, int pakLocation, byte[] encodedPakEntries)
        {
            if (pakLocation >= 0)
            {
                // Grab the big bitfield value:
                // Bit 31 = Offset 32-bit safe?
                // Bit 30 = Uncompressed size 32-bit safe?
                // Bit 29 = Size 32-bit safe?
                // Bits 28-23 = Compression method
                // Bit 22 = Encrypted
                // Bits 21-6 = Compression blocks count
                // Bits 5-0 = Compression block size

                // Filter out the CompressionMethod.

                long Offset, UncompressedSize, Size;
                uint CompressionMethodIndex, CompressionBlockSize;
                bool Encrypted, Deleted;

                uint Value = BitConverter.ToUInt32(encodedPakEntries, pakLocation);
                pakLocation += sizeof(uint);

                CompressionMethodIndex = ((Value >> 23) & 0x3f);

                // Test for 32-bit safe values. Grab it, or memcpy the 64-bit value
                // to avoid alignment exceptions on platforms requiring 64-bit alignment
                // for 64-bit variables.
                //
                // Read the Offset.
                bool bIsOffset32BitSafe = (Value & (1 << 31)) != 0;
                if (bIsOffset32BitSafe)
                {
                    Offset       = BitConverter.ToUInt32(encodedPakEntries, pakLocation);
                    pakLocation += sizeof(uint);
                }
                else
                {
                    Offset       = BitConverter.ToInt64(encodedPakEntries, pakLocation);
                    pakLocation += sizeof(long);
                }

                // Read the UncompressedSize.
                bool bIsUncompressedSize32BitSafe = (Value & (1 << 30)) != 0;
                if (bIsUncompressedSize32BitSafe)
                {
                    UncompressedSize = BitConverter.ToUInt32(encodedPakEntries, pakLocation);
                    pakLocation     += sizeof(uint);
                }
                else
                {
                    UncompressedSize = BitConverter.ToInt64(encodedPakEntries, pakLocation);
                    pakLocation     += sizeof(long);
                }

                // Fill in the Size.
                if (CompressionMethodIndex != 0)
                {
                    // Size is only present if compression is applied.
                    bool bIsSize32BitSafe = (Value & (1 << 29)) != 0;
                    if (bIsSize32BitSafe)
                    {
                        Size         = BitConverter.ToUInt32(encodedPakEntries, pakLocation);
                        pakLocation += sizeof(uint);
                    }
                    else
                    {
                        Size         = BitConverter.ToInt64(encodedPakEntries, pakLocation);
                        pakLocation += sizeof(long);
                    }
                }
                else
                {
                    // The Size is the same thing as the UncompressedSize when
                    // CompressionMethod == COMPRESS_None.
                    Size = UncompressedSize;
                }

                // Filter the encrypted flag.
                Encrypted = (Value & (1 << 22)) != 0;

                // This should clear out any excess CompressionBlocks that may be valid in the user's
                // passed in entry.
                var CompressionBlocksCount = (Value >> 6) & 0xffff;
                FPakCompressedBlock[] CompressionBlocks = new FPakCompressedBlock[CompressionBlocksCount];

                // Filter the compression block size or use the UncompressedSize if less that 64k.
                CompressionBlockSize = 0;
                if (CompressionBlocksCount > 0)
                {
                    CompressionBlockSize = UncompressedSize < 65536 ? (uint)UncompressedSize : ((Value & 0x3f) << 11);
                }

                // Set bDeleteRecord to false, because it obviously isn't deleted if we are here.
                Deleted = false;

                // Base offset to the compressed data
                long BaseOffset = true ? 0 : Offset; // HasRelativeCompressedChunkOffsets -> Version >= PakFile_Version_RelativeChunkOffsets

                // Handle building of the CompressionBlocks array.
                if (CompressionBlocks.Length == 1 && !Encrypted)
                {
                    // If the number of CompressionBlocks is 1, we didn't store any extra information.
                    // Derive what we can from the entry's file offset and size.
                    var start = BaseOffset + FPakEntry.GetSize(EPakVersion.LATEST, CompressionMethodIndex, CompressionBlocksCount);
                    CompressionBlocks[0] = new FPakCompressedBlock(start, start + Size);
                }
                else if (CompressionBlocks.Length > 0)
                {
                    // Get the right pointer to start copying the CompressionBlocks information from.

                    // Alignment of the compressed blocks
                    var CompressedBlockAlignment = Encrypted ? 16 : 1;

                    // CompressedBlockOffset is the starting offset. Everything else can be derived from there.
                    long CompressedBlockOffset = BaseOffset + FPakEntry.GetSize(EPakVersion.LATEST, CompressionMethodIndex, CompressionBlocksCount);
                    for (int CompressionBlockIndex = 0; CompressionBlockIndex < CompressionBlocks.Length; ++CompressionBlockIndex)
                    {
                        FPakCompressedBlock CompressionBlock = new FPakCompressedBlock(CompressedBlockOffset, CompressedBlockOffset + BitConverter.ToUInt32(encodedPakEntries, pakLocation));
                        CompressionBlocks[CompressionBlockIndex] = CompressionBlock;
                        CompressedBlockOffset += BinaryHelper.Align(CompressionBlock.CompressedEnd - CompressionBlock.CompressedStart, CompressedBlockAlignment);

                        pakLocation += 4;
                    }
                }
                return(new FPakEntry(this.FileName, name, Offset, Size, UncompressedSize, CompressionBlocks, CompressionBlockSize, CompressionMethodIndex, (byte)((Encrypted ? 0x01 : 0x00) | (Deleted ? 0x02 : 0x00))));
            }
            else
            {
                throw new FileLoadException("list indexes aren't supported");
            }
        }