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"); } }
public Stream GetPackageStream(FPakEntry entry) { lock (Reader) { return(new FPakFile(Reader, entry, Aes).GetStream()); } }
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; * }*/ }
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); } } }
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"); } }
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); } }