Beispiel #1
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;
             * }*/
        }
Beispiel #2
0
        private void ReadIndexUpdated(BinaryReader reader, FPakInfo info, byte[] aesKey)
        {
            MountPoint = reader.ReadFString();
            if (MountPoint.StartsWith("../../.."))
            {
                MountPoint = MountPoint.Substring(8);
            }
            else
            {
                MountPoint = "/";
            }

            var filesNum = reader.ReadInt32();

            reader.ReadUInt64();

            if (reader.ReadInt32() == 0)
            {
                throw new FileLoadException("No path hash index");
            }

            //reader.ReadInt64();
            //reader.ReadInt64();
            reader.BaseStream.Position += 20L + 8L + 8L;

            if (reader.ReadInt32() == 0)
            {
                throw new FileLoadException("No directory index");
            }

            var position           = reader.ReadInt64();
            var directoryIndexSize = reader.ReadInt64();

            reader.BaseStream.Position += 20L;
            var encodedPakEntries = reader.ReadTArray(reader.ReadByte);
            var files             = reader.ReadInt32();

            if (files < 0)
            {
                throw new FileLoadException("Corrupt pak PrimaryIndex detected!");
            }

            Reader.BaseStream.Position = position;
            var directoryIndexData = Reader.ReadBytes((int)directoryIndexSize);

            if (info.bEncryptedIndex != 0)
            {
                directoryIndexData = AESDecryptor.DecryptAES(directoryIndexData, aesKey);
            }

            var directoryIndexReader = new BinaryReader(new MemoryStream(directoryIndexData));
            var directoryEntries     = directoryIndexReader.ReadTArray(() => new FPakDirectoryEntry(directoryIndexReader));

            var entries = new List <FPakEntry>(filesNum);

            foreach (var directoryEntry in directoryEntries)
            {
                foreach (var hashIndexEntry in directoryEntry.Entries)
                {
                    string path = MountPoint + directoryEntry.Directory + hashIndexEntry.Filename;
                    entries.Add(GetEntry(path, hashIndexEntry.Location, encodedPakEntries));
                }
            }
            this.FileInfos = entries.ToArray();
        }
Beispiel #3
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);
                }
            }
        }
Beispiel #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)
            {
                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);
            }
        }