/// <summary> /// Reads the PAK file metadata, including file list information and the file list /// </summary> /// <exception cref="NotSupportedException">Is thrown if the given PAK file does not have a valid signature</exception> private void ReadMetadata() { using (BinaryReader reader = new BinaryReader(new MemoryStream(File.ReadAllBytes(FilePath)))) { reader.BaseStream.Seek(-9L, SeekOrigin.End); uint fileListOffset = reader.ReadUInt32(); uint fileCount = reader.ReadUInt32(); if ((reader.ReadByte()) != 0x12) { throw new NotSupportedException("The signature of this PAK file is invalid!"); } reader.BaseStream.Seek(fileListOffset, SeekOrigin.Begin); for (uint i = 0; i < fileCount; i++) { FileEntry fileEntry = new FileEntry(); fileEntry.FileNameLength = reader.ReadByte(); fileEntry.Compression = reader.ReadByte(); fileEntry.Offset = reader.ReadUInt32(); fileEntry.FileSize = reader.ReadUInt32(); fileEntry.RealFileSize = reader.ReadUInt32(); byte[] tempName = reader.ReadBytes(fileEntry.FileNameLength); if (fileEntry.Compression < 4) { uint decryptionKey = (uint)Key; reader.BaseStream.Seek(1L, SeekOrigin.Current); fileEntry.FileName = Encoding.UTF8.GetString(XOR.Cipher(tempName, decryptionKey)); } else { uint[] decryptionKey = (uint[])Key; fileEntry.Compression ^= 0x20; fileEntry.FileName = DecryptFileName(tempName, decryptionKey); uint[] decryptionData = { fileEntry.Offset, fileEntry.RealFileSize }; uint[] resultData = XTEA.Decipher(16, decryptionData, decryptionKey); fileEntry.Offset = resultData[0]; fileEntry.RealFileSize = resultData[1]; } Entries.Add(fileEntry); } } }
/// <summary> /// Decrypts the name of a file using XTEA /// </summary> /// <param name="fileNameBuffer">Bytes of the file name</param> /// <param name="key">Key to decrypt the filename with</param> /// <returns>The decrypted filename</returns> private static string DecryptFileName(byte[] fileNameBuffer, uint[] key) { Span <byte> nameSpan = fileNameBuffer; for (int j = 0; j < nameSpan.Length; j = j + 8) { Span <byte> chunk = nameSpan.Slice(j, 8); Span <uint> decrypted = XTEA.Decipher(16, MemoryMarshal.Cast <byte, uint>(chunk).ToArray(), key); Span <byte> resource = MemoryMarshal.AsBytes(decrypted); resource.CopyTo(chunk); } return(Encoding.UTF8.GetString(nameSpan.ToArray().TakeWhile(x => x != 0x00).ToArray())); }
/// <summary> /// Reads the file entries of the PAK file /// </summary> public void ReadFileEntries() { long Position = Reader.BaseStream.Position; Reader.BaseStream.Seek(FileListOffset, SeekOrigin.Begin); for (uint i = 0; i < FileCount; i++) { FileEntry fileEntry = new FileEntry(); fileEntry.FileNameLength = Reader.ReadByte(); fileEntry.Compression = Reader.ReadByte(); fileEntry.Offset = Reader.ReadUInt32(); fileEntry.FileSize = Reader.ReadUInt32(); fileEntry.RealFileSize = Reader.ReadUInt32(); byte[] tempName = Reader.ReadBytes(fileEntry.FileNameLength); if (fileEntry.Compression < 4 && fileEntry.Compression > -1) { uint decryptionKey = (uint)Key; fileEntry.Unknown1 = Reader.ReadByte(); fileEntry.FileName = Encoding.UTF8.GetString(XOR.Cipher(tempName, decryptionKey)); } else { uint[] decryptionKey = (uint[])Key; fileEntry.Compression ^= 0x20; fileEntry.FileName = DecryptFileName(tempName, decryptionKey); uint[] decryptionData = new uint[] { fileEntry.Offset, fileEntry.RealFileSize }; uint[] resultData = XTEA.Decipher(16, decryptionData, decryptionKey); fileEntry.Offset = resultData[0]; fileEntry.RealFileSize = resultData[1]; } Entries.Add(fileEntry); } Reader.BaseStream.Seek(Position, SeekOrigin.Begin); }
/// <summary> /// Decrypts and parses the updatelist file /// /// Saves the parsed updatelist in the Document instance attribute /// </summary> public void Parse() { if (DecryptionKey == null) { throw new NullReferenceException("DecryptionKey needs to be set using the SetDecryptionKey instance method!"); } Span <byte> updateList = File.ReadAllBytes(FilePath); for (int i = 0; i < updateList.Length; i += 8) { Span <byte> chunk = updateList.Slice(i, 8); Span <uint> decrypted = XTEA.Decipher(16, MemoryMarshal.Cast <byte, uint>(chunk).ToArray(), DecryptionKey); Span <byte> resource = MemoryMarshal.AsBytes(decrypted); resource.CopyTo(chunk); } Document = Encoding.UTF8.GetString(updateList.ToArray()); }