Example #1
0
        public void Export(FPakEntry uasset, FPakEntry uexp, FPakEntry ubulk)
        {
            if (uasset.GetType() != typeof(FPakEntry) || uexp.GetType() != typeof(FPakEntry))
            {
                return;
            }
            var assetStream = new FPakFile(Reader, uasset, Aes).GetStream();
            var expStream   = new FPakFile(Reader, uexp, Aes).GetStream();
            var bulkStream  = ubulk.GetType() != typeof(FPakEntry) ? null : new FPakFile(Reader, ubulk, Aes).GetStream();

            try
            {
                var exports = new AssetReader(assetStream, expStream, bulkStream).Exports;
                if (exports[0] is Texture2D)
                {
                    var tex = exports[0] as Texture2D;
                    tex.GetImage();
                }
            }
            catch (IndexOutOfRangeException) { }
            catch (NotImplementedException) { }
            catch (IOException) { }
            catch (Exception e)
            {
                DebugHelper.WriteException(e, "thrown in PakReader.cs by Export");
            }
        }
Example #2
0
 public Stream GetPackageStream(FPakEntry entry)
 {
     lock (Reader)
     {
         return(new FPakFile(Reader, entry, Aes).GetStream());
     }
 }
Example #3
0
        const int     EncryptedBufferSize = 256; //?? TODO: check - may be value 16 will be better for performance

        public FPakFile(BinaryReader Reader, FPakEntry Info, byte[] key = null)
        {
            Reader.BaseStream.Seek(Info.Offset + Info.StructSize, SeekOrigin.Begin);
            if (Info.Encrypted)
            {
                long   encSize   = (Info.Size & 15) == 0 ? Info.Size : ((Info.Size / 16) + 1) * 16;
                byte[] encBuffer = Reader.ReadBytes((int)encSize);
                data = AESDecryptor.DecryptAES(encBuffer, key).SubArray(0, (int)Info.UncompressedSize);
                if (encSize != Info.Size)
                {
                    data = data.SubArray(0, (int)Info.UncompressedSize);
                }
            }
            else
            {
                data = Reader.ReadBytes((int)Info.UncompressedSize);
            }
            //File.WriteAllBytes(Path.GetFileName(info.Name), data);

            /*
             * if (info.CompressionMethod != 0)
             * {
             *  Console.WriteLine("compressed");
             *  while (size > 0)
             *  {
             *      if ((UncompressedBuffer == null) || (ArPos < UncompressedBufferPos) || (ArPos >= UncompressedBufferPos + Info.CompressionBlockSize))
             *      {
             *          // buffer is not ready
             *          if (UncompressedBuffer == null)
             *          {
             *              UncompressedBuffer = new byte[Info.CompressionBlockSize];
             *          }
             *          // prepare buffer
             *          int BlockIndex = ArPos / Info.CompressionBlockSize;
             *          UncompressedBufferPos = Info.CompressionBlockSize * BlockIndex;
             *
             *          FPakCompressedBlock Block = Info.CompressionBlocks[BlockIndex];
             *          int CompressedBlockSize = (int)(Block.CompressedEnd - Block.CompressedStart);
             *          int UncompressedBlockSize = Math.Min(Info.CompressionBlockSize, (int)Info.UncompressedSize - UncompressedBufferPos); // don't pass file end
             *
             *          byte[] CompressedData;
             *          if (Info.bEncrypted == 0)
             *          {
             *              Reader.BaseStream.Seek(Block.CompressedStart, SeekOrigin.Begin);
             *              CompressedData = Reader.ReadBytes(CompressedBlockSize);
             *          }
             *          else
             *          {
             *              int EncryptedSize = Align(CompressedBlockSize, EncryptionAlign);
             *              Reader.BaseStream.Seek(Block.CompressedStart, SeekOrigin.Begin);
             *              CompressedData = Reader.ReadBytes(EncryptedSize);
             *              CompressedData = AESDecryptor.DecryptAES(CompressedData, EncryptedSize, key, key.Length);
             *          }
             *          // appDecompress(CompressedData, CompressedBlockSize, UncompressedBuffer, UncompressedBlockSize, Info.CompressionMethod);
             *          // https://github.com/gildor2/UModel/blob/39641f9e58cb4c286dde5f191e5db9a24b19f503/Unreal/UnCoreCompression.cpp#L183
             *          throw new NotImplementedException("Decompressing of files aren't written yet");
             *      }
             *
             *      // data is in buffer, copy it
             *      int BytesToCopy = UncompressedBufferPos + Info.CompressionBlockSize - ArPos; // number of bytes until end of the buffer
             *      if (BytesToCopy > size) BytesToCopy = size;
             *      if (BytesToCopy <= 0)
             *      {
             *          throw new ArgumentOutOfRangeException("Bytes to copy is invalid");
             *      }
             *
             *      // copy uncompressed data
             *      int OffsetInBuffer = ArPos - UncompressedBufferPos;
             *      Buffer.BlockCopy(UncompressedBuffer, OffsetInBuffer, data, dataOffset, BytesToCopy);
             *
             *      // advance pointers
             *
             *      // ArPos += BytesToCopy;
             *      size -= BytesToCopy;
             *      dataOffset += BytesToCopy;
             *  }
             *  throw new NotImplementedException("Decompressing of files aren't written yet");
             * }
             * else if (Info.bEncrypted != 0)
             * {
             *  Console.WriteLine("Encrypted");
             *  // Uncompressed encrypted data. Reuse compression fields to handle decryption efficiently
             *  if (UncompressedBuffer == null)
             *  {
             *      UncompressedBuffer = new byte[EncryptedBufferSize];
             *      UncompressedBufferPos = 0x40000000; // some invalid value
             *  }
             *
             *  while (size > 0)
             *  {
             *      if ((ArPos < UncompressedBufferPos) || (ArPos >= UncompressedBufferPos + EncryptedBufferSize))
             *      {
             *          // Should fetch block and decrypt it.
             *          // Note: AES is block encryption, so we should always align read requests for correct decryption.
             *          UncompressedBufferPos = ArPos & ~(EncryptionAlign - 1);
             *          Reader.BaseStream.Seek(Info.Pos + Info.StructSize + UncompressedBufferPos, SeekOrigin.Begin);
             *          int RemainingSize = (int)Info.Size;
             *          if (RemainingSize >= 0)
             *          {
             *              if (RemainingSize > EncryptedBufferSize)
             *                  RemainingSize = EncryptedBufferSize;
             *              RemainingSize = Align(RemainingSize, EncryptionAlign); // align for AES, pak contains aligned data
             *              UncompressedBuffer = Reader.ReadBytes(RemainingSize);
             *              UncompressedBuffer = AESDecryptor.DecryptAES(UncompressedBuffer, RemainingSize, key, key.Length);
             *          }
             *      }
             *
             *      // Now copy decrypted data from UncompressedBuffer (code is very similar to those used in decompression above)
             *      int BytesToCopy = UncompressedBufferPos + EncryptedBufferSize - ArPos; // number of bytes until end of the buffer
             *      if (BytesToCopy > size) BytesToCopy = size;
             *      if (BytesToCopy <= 0)
             *      {
             *          throw new ArgumentOutOfRangeException("Bytes to copy is invalid");
             *      }
             *
             *      // copy uncompressed data
             *      int OffsetInBuffer = ArPos - UncompressedBufferPos;
             *      Buffer.BlockCopy(UncompressedBuffer, OffsetInBuffer, data, dataOffset, BytesToCopy);
             *
             *      // advance pointers
             *
             *      // ArPos += BytesToCopy;
             *      size -= BytesToCopy;
             *      dataOffset += BytesToCopy;
             *  }
             *  throw new NotImplementedException("Decryption of files aren't written yet");
             * }
             * else
             * {
             *  // Pure data
             *  // seek every time in a case if the same 'Reader' was used by different FPakFile
             *  // (this is a lightweight operation for buffered FArchive)
             *  Reader.BaseStream.Seek(Info.Pos + Info.StructSize, SeekOrigin.Begin);
             *  data = Reader.ReadBytes(size);
             *  // ArPos += size;
             * }*/
        }
Example #4
0
        public PakReader(Stream stream, string name, byte[] aes = null, bool ParseFiles = true)
        {
            Aes    = aes;
            Stream = stream;
            Name   = name;
            Reader = new BinaryReader(Stream);

            Stream.Seek(-FPakInfo.Size, SeekOrigin.End);

            FPakInfo info = new FPakInfo(Reader);

            if (info.Magic != FPakInfo.PAK_FILE_MAGIC)
            {
                DebugHelper.WriteLine(".PAKs: The file magic is invalid");
                throw new FileLoadException("The file magic is invalid");
            }

            if (info.Version > (int)PAK_VERSION.PAK_LATEST)
            {
                DebugHelper.WriteLine($".PAKs: WARNING: Pak file \"{Name}\" has unsupported version {info.Version}");
            }

            if (info.bEncryptedIndex != 0)
            {
                if (Aes == null)
                {
                    DebugHelper.WriteLine(".PAKs: The file has an encrypted index");
                    throw new FileLoadException("The file has an encrypted index");
                }
            }

            // Read pak index

            Stream.Seek(info.IndexOffset, SeekOrigin.Begin);

            // Manage pak files with encrypted index
            BinaryReader infoReader = Reader;

            if (info.bEncryptedIndex != 0)
            {
                var InfoBlock = Reader.ReadBytes((int)info.IndexSize);
                InfoBlock = AESDecryptor.DecryptAES(InfoBlock, Aes);

                infoReader = new BinaryReader(new MemoryStream(InfoBlock));
                int stringLen = infoReader.ReadInt32();
                if (stringLen > 512 || stringLen < -512)
                {
                    DebugHelper.WriteLine(".PAKs: The AES key is invalid");
                    throw new FileLoadException("The AES key is invalid");
                }
                if (stringLen < 0)
                {
                    infoReader.BaseStream.Seek((stringLen - 1) * 2, SeekOrigin.Current);
                    ushort c = infoReader.ReadUInt16();
                    if (c != 0)
                    {
                        DebugHelper.WriteLine(".PAKs: The AES key is invalid");
                        throw new FileLoadException("The AES key is invalid");
                    }
                }
                else
                {
                    infoReader.BaseStream.Seek(stringLen - 1, SeekOrigin.Current);
                    byte c = infoReader.ReadByte();
                    if (c != 0)
                    {
                        DebugHelper.WriteLine(".PAKs: The AES key is invalid");
                        throw new FileLoadException("The AES key is invalid");
                    }
                }

                infoReader.BaseStream.Seek(0, SeekOrigin.Begin);
            }

            if (!ParseFiles)
            {
                return;
            }

            if (info.Version >= (int)PAK_VERSION.PAK_PATH_HASH_INDEX)
            {
                ReadIndexUpdated(infoReader, info, Aes);
            }
            else
            {
                MountPoint = infoReader.ReadFString();
                if (MountPoint.StartsWith("../../.."))
                {
                    MountPoint = MountPoint.Substring(8);
                }
                else
                {
                    MountPoint = "/";
                }

                FileInfos = new FPakEntry[infoReader.ReadInt32()];
                for (int i = 0; i < FileInfos.Length; i++)
                {
                    FileInfos[i] = new FPakEntry(infoReader, MountPoint, (PAK_VERSION)info.Version);
                }
            }
        }
Example #5
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(PAK_VERSION.PAK_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 ? AESDecryptor.BLOCK_SIZE : 1;

                    // CompressedBlockOffset is the starting offset. Everything else can be derived from there.
                    long CompressedBlockOffset = BaseOffset + FPakEntry.GetSize(PAK_VERSION.PAK_LATEST, CompressionMethodIndex, CompressionBlocksCount);
                    for (int CompressionBlockIndex = 0; CompressionBlockIndex < CompressionBlocks.Length; ++CompressionBlockIndex)
                    {
                        CompressionBlocks[CompressionBlockIndex] = new FPakCompressedBlock(CompressedBlockOffset, CompressedBlockOffset + BitConverter.ToUInt32(encodedPakEntries, pakLocation));
                        pakLocation += sizeof(uint);
                        {
                            var toAlign = CompressionBlocks[CompressionBlockIndex].CompressedEnd - CompressionBlocks[CompressionBlockIndex].CompressedStart;
                            CompressedBlockOffset += toAlign + CompressedBlockAlignment - (toAlign % CompressedBlockAlignment);
                        }
                    }
                }
                return(new FPakEntry(name, Offset, Size, UncompressedSize, new byte[20], CompressionBlocks, CompressionBlockSize, CompressionMethodIndex, (byte)((Encrypted ? 0x01 : 0x00) | (Deleted ? 0x02 : 0x00))));
            }
            else
            {
                //pakLocation = -(pakLocation + 1);
                throw new FileLoadException("list indexes aren't supported");
            }
        }
Example #6
0
        public PakReader(Stream stream, string name, byte[] aes = null, bool ParseFiles = true)
        {
            Aes    = aes;
            Stream = stream;
            Name   = name;
            Reader = new BinaryReader(Stream);

            Stream.Seek(-FPakInfo.Size, SeekOrigin.End);

            FPakInfo info = new FPakInfo(Reader);

            if (info.Magic != FPakInfo.PAK_FILE_MAGIC)
            {
                throw new FileLoadException("The file magic is invalid");
            }

            if (info.Version > (int)PAK_VERSION.PAK_LATEST)
            {
                Console.Error.WriteLine($"WARNING: Pak file \"{Name}\" has unsupported version {info.Version}");
            }

            if (info.bEncryptedIndex != 0)
            {
                if (Aes == null)
                {
                    throw new FileLoadException("The file has an encrypted index");
                }
            }

            // Read pak index

            Stream.Seek(info.IndexOffset, SeekOrigin.Begin);

            // Manage pak files with encrypted index
            BinaryReader infoReader = Reader;

            if (info.bEncryptedIndex != 0)
            {
                var InfoBlock = Reader.ReadBytes((int)info.IndexSize);
                InfoBlock = AESDecryptor.DecryptAES(InfoBlock, (int)info.IndexSize, Aes, Aes.Length);

                infoReader = new BinaryReader(new MemoryStream(InfoBlock));
                int stringLen = infoReader.ReadInt32();
                if (stringLen > 512 || stringLen < -512)
                {
                    throw new FileLoadException("The AES key is invalid");
                }
                if (stringLen < 0)
                {
                    infoReader.BaseStream.Seek((stringLen - 1) * 2, SeekOrigin.Current);
                    ushort c = infoReader.ReadUInt16();
                    if (c != 0)
                    {
                        throw new FileLoadException("The AES key is invalid");
                    }
                }
                else
                {
                    infoReader.BaseStream.Seek(stringLen - 1, SeekOrigin.Current);
                    byte c = infoReader.ReadByte();
                    if (c != 0)
                    {
                        throw new FileLoadException("The AES key is invalid");
                    }
                }
            }

            if (!ParseFiles)
            {
                return;
            }

            // Pak index reading time :)
            infoReader.BaseStream.Seek(0, SeekOrigin.Begin);
            MountPoint = infoReader.ReadString(FPakInfo.MAX_PACKAGE_PATH);

            bool badMountPoint = false;

            if (!MountPoint.StartsWith("../../.."))
            {
                badMountPoint = true;
            }
            else
            {
                MountPoint = MountPoint.Substring(8);
            }
            if (MountPoint[0] != '/' || ((MountPoint.Length > 1) && (MountPoint[1] == '.')))
            {
                badMountPoint = true;
            }

            if (badMountPoint)
            {
                Console.Error.WriteLine($"WARNING: Pak \"{Name}\" has strange mount point \"{MountPoint}\", mounting to root");
                MountPoint = "/";
            }

            FileInfos = new FPakEntry[infoReader.ReadInt32()];
            for (int i = 0; i < FileInfos.Length; i++)
            {
                FileInfos[i] = new FPakEntry(infoReader, MountPoint, info.Version);
            }
        }