From fe7cd3f3788a6800cb86e8f6970d173a900726d4 Mon Sep 17 00:00:00 2001 From: GoogleBen Date: Thu, 10 Mar 2022 03:49:52 -0600 Subject: [PATCH] Begin Elden Ring support --- BinderTool.Core/Bdt5/Bdt5FileStream.cs | 14 +++- BinderTool.Core/Bdt5/Bdt5InnerStream.cs | 102 ++++++++++++++++++++++++ BinderTool.Core/Bhd5/Bhd5BucketEntry.cs | 24 +++--- BinderTool.Core/BinderTool.Core.csproj | 1 + BinderTool.Core/Bnd4/Bnd4File.cs | 8 +- BinderTool.Core/CryptographyUtility.cs | 65 ++++++++------- BinderTool.Core/Enc/EncFile.cs | 2 +- BinderTool.Core/GameVersion.cs | 3 +- BinderTool.Core/Sl2/Sl2UserData.cs | 6 +- BinderTool/BinderTool.csproj | 5 ++ BinderTool/DecryptionKeys.cs | 80 ++++++++++++++++++- BinderTool/DictionaryER.csv | 1 + BinderTool/FileNameDictionary.cs | 1 + BinderTool/Options.cs | 6 +- BinderTool/Program.cs | 54 +++++++++---- 15 files changed, 299 insertions(+), 73 deletions(-) create mode 100644 BinderTool.Core/Bdt5/Bdt5InnerStream.cs create mode 100644 BinderTool/DictionaryER.csv diff --git a/BinderTool.Core/Bdt5/Bdt5FileStream.cs b/BinderTool.Core/Bdt5/Bdt5FileStream.cs index c65cb66..b02c824 100644 --- a/BinderTool.Core/Bdt5/Bdt5FileStream.cs +++ b/BinderTool.Core/Bdt5/Bdt5FileStream.cs @@ -12,15 +12,21 @@ public Bdt5FileStream(Stream inputStream) _inputStream = inputStream; } - public MemoryStream Read(long fileOffset, long fileSize) + public Stream Read(long fileOffset, long fileSize) { if (fileOffset + fileSize > _inputStream.Length) throw new EndOfStreamException(); _inputStream.Seek(fileOffset, SeekOrigin.Begin); - byte[] buffer = new byte[fileSize]; - _inputStream.Read(buffer, 0, (int) fileSize); - return new MemoryStream(buffer); + if (fileSize < Bdt5InnerStream.MAX_STREAM_LEN) + { + byte[] buffer = new byte[fileSize]; + _inputStream.Read(buffer, 0, (int)fileSize); + return new MemoryStream(buffer); + } + var ans = new Bdt5InnerStream(_inputStream, fileSize); + _inputStream.Seek(fileSize, SeekOrigin.Current); + return ans; } public static Bdt5FileStream OpenFile(string path, FileMode mode, FileAccess access) diff --git a/BinderTool.Core/Bdt5/Bdt5InnerStream.cs b/BinderTool.Core/Bdt5/Bdt5InnerStream.cs new file mode 100644 index 0000000..5919979 --- /dev/null +++ b/BinderTool.Core/Bdt5/Bdt5InnerStream.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +namespace BinderTool.Core.Bdt5 +{ + public class Bdt5InnerStream : Stream + { + public const int MAX_STREAM_LEN = 1_000_000; + + private List streams; + + private long length; + + + public override long Length => length; + + public override long Position { get; set; } + + public override bool CanRead => true; + + public override bool CanSeek => true; + + public override bool CanWrite => true; + + public Bdt5InnerStream(Stream from, long length, long offset) + { + this.length = length; + this.streams = new List(); + long startPos = from.Position; + for (long pos = 0; pos < length; pos += MAX_STREAM_LEN) + { + byte[] buf = new byte[MAX_STREAM_LEN]; + from.Seek(offset + pos, SeekOrigin.Begin); + from.Read(buf, 0, (int)Math.Min(length - pos, MAX_STREAM_LEN)); + streams.Add(new MemoryStream(buf)); + } + from.Seek(startPos, SeekOrigin.Begin); + } + public Bdt5InnerStream(Stream from, long length) : this(from, length, 0) {} + public Bdt5InnerStream(long length) + { + this.length = length; + this.streams = new List(); + for (long pos = 0; pos < length; pos += MAX_STREAM_LEN) + { + byte[] buf = new byte[MAX_STREAM_LEN]; + streams.Add(new MemoryStream(buf)); + } + } + + public override void Flush() {} + + public override int Read(byte[] buffer, int offset, int count) + { + if (this.Position >= this.length) return 0; + long startPos = this.Position; + long end = Math.Min(this.Position + count, this.length); + while (this.Position < end) + { + var s = this.streams[(int)(this.Position / MAX_STREAM_LEN)]; + s.Seek(this.Position % MAX_STREAM_LEN, SeekOrigin.Begin); + var read = s.Read(buffer, offset, count); + this.Position += read; + offset += read; + count -= read; + } + return (int)(end - startPos); + } + + public override long Seek(long offset, SeekOrigin origin) + { + if (origin == SeekOrigin.Begin) this.Position = offset; + if (origin == SeekOrigin.Current) this.Position += offset; + if (origin == SeekOrigin.End) this.Position = this.length + offset; + return this.Position; + } + + public override void SetLength(long value) + { + this.length = value; + } + + public override void Write(byte[] buffer, int offset, int count) + { + long end = this.Position + count; + while (this.Position < end) + { + var s = this.streams[(int)(this.Position / MAX_STREAM_LEN)]; + s.Seek(this.Position % MAX_STREAM_LEN, SeekOrigin.Begin); + var toWrite = (int)Math.Min(s.Length - s.Position, count); + s.Write(buffer, offset, toWrite); + this.Position += toWrite; + offset += toWrite; + count -= toWrite; + } + } + } +} diff --git a/BinderTool.Core/Bhd5/Bhd5BucketEntry.cs b/BinderTool.Core/Bhd5/Bhd5BucketEntry.cs index 82982bc..cd6e23d 100644 --- a/BinderTool.Core/Bhd5/Bhd5BucketEntry.cs +++ b/BinderTool.Core/Bhd5/Bhd5BucketEntry.cs @@ -1,10 +1,11 @@ -using System.IO; +using System; +using System.IO; namespace BinderTool.Core.Bhd5 { public class Bhd5BucketEntry { - public uint FileNameHash { get; private set; } + public ulong FileNameHash { get; private set; } public long FileSize { get; set; } public long PaddedFileSize { get; set; } public long FileOffset { get; private set; } @@ -15,22 +16,19 @@ public class Bhd5BucketEntry public static Bhd5BucketEntry Read(BinaryReader reader, GameVersion version) { Bhd5BucketEntry result = new Bhd5BucketEntry(); - result.FileNameHash = reader.ReadUInt32(); + result.FileNameHash = reader.ReadUInt64(); result.PaddedFileSize = reader.ReadUInt32(); + result.FileSize = reader.ReadUInt32(); + if (result.FileSize != 0) + { + //Console.WriteLine("FS not 0"); + } result.FileOffset = reader.ReadInt64(); + long saltedHashOffset = reader.ReadInt64(); long aesKeyOffset = reader.ReadInt64(); - switch (version) - { - case GameVersion.DarkSouls3: - case GameVersion.Sekiro: - result.FileSize = reader.ReadInt64(); - break; - default: - result.FileSize = result.PaddedFileSize; - break; - } + if (saltedHashOffset != 0) { diff --git a/BinderTool.Core/BinderTool.Core.csproj b/BinderTool.Core/BinderTool.Core.csproj index 0978a42..3993ca9 100644 --- a/BinderTool.Core/BinderTool.Core.csproj +++ b/BinderTool.Core/BinderTool.Core.csproj @@ -75,6 +75,7 @@ + diff --git a/BinderTool.Core/Bnd4/Bnd4File.cs b/BinderTool.Core/Bnd4/Bnd4File.cs index c8aeff1..2bbb088 100644 --- a/BinderTool.Core/Bnd4/Bnd4File.cs +++ b/BinderTool.Core/Bnd4/Bnd4File.cs @@ -83,9 +83,13 @@ private void Read(Stream inputStream) reader.Seek(fileNameOffset); fileName = reader.ReadNullTerminatedString(); } + if (fileName.Contains("param")) Console.WriteLine($"{fileName} {fileEntrySize} {fileEntryOffset}"); - reader.Seek(fileEntryOffset); - _entries.Add(Bnd4FileEntry.Read(inputStream, fileEntrySize, fileName)); + if (fileName.Length > 0 && fileEntrySize > 0 && fileEntryOffset > 0) + { + reader.Seek(fileEntryOffset); + _entries.Add(Bnd4FileEntry.Read(inputStream, fileEntrySize, fileName)); + } reader.Seek(position); } } diff --git a/BinderTool.Core/CryptographyUtility.cs b/BinderTool.Core/CryptographyUtility.cs index 98e2fed..fb8c8ee 100644 --- a/BinderTool.Core/CryptographyUtility.cs +++ b/BinderTool.Core/CryptographyUtility.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using BinderTool.Core.Bdt5; using BinderTool.Core.Bhd5; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; @@ -11,62 +12,65 @@ namespace BinderTool.Core { public static class CryptographyUtility { - public static MemoryStream DecryptAesEcb(Stream inputStream, byte[] key) + public static Stream DecryptAesEcb(Stream inputStream, byte[] key) { var cipher = CreateAesEcbCipher(key); return DecryptAes(inputStream, cipher, inputStream.Length); } - public static MemoryStream DecryptAesCbc(Stream inputStream, byte[] key, byte[] iv) + public static Stream DecryptAesCbc(Stream inputStream, byte[] key, byte[] iv) { - AesEngine engine = new AesEngine(); + var engine = new CbcBlockCipher(new AesEngine()); KeyParameter keyParameter = new KeyParameter(key); ICipherParameters parameters = new ParametersWithIV(keyParameter, iv); - BufferedBlockCipher cipher = new BufferedBlockCipher(new CbcBlockCipher(engine)); - cipher.Init(false, parameters); - return DecryptAes(inputStream, cipher, inputStream.Length); + engine.Init(false, parameters); + return DecryptAes(inputStream, engine, inputStream.Length); } - public static MemoryStream DecryptAesCtr(Stream inputStream, byte[] key, byte[] iv) + public static Stream DecryptAesCtr(Stream inputStream, byte[] key, byte[] iv) { - AesEngine engine = new AesEngine(); + SicBlockCipher engine = new SicBlockCipher(new AesEngine()); KeyParameter keyParameter = new KeyParameter(key); ICipherParameters parameters = new ParametersWithIV(keyParameter, iv); - BufferedBlockCipher cipher = new BufferedBlockCipher(new SicBlockCipher(engine)); - cipher.Init(false, parameters); - return DecryptAes(inputStream, cipher, inputStream.Length); + engine.Init(false, parameters); + return DecryptAes(inputStream, engine, inputStream.Length); } - private static BufferedBlockCipher CreateAesEcbCipher(byte[] key) + private static IBlockCipher CreateAesEcbCipher(byte[] key) { AesEngine engine = new AesEngine(); KeyParameter parameter = new KeyParameter(key); - BufferedBlockCipher cipher = new BufferedBlockCipher(engine); - cipher.Init(false, parameter); - return cipher; + engine.Init(false, parameter); + return engine; } - private static MemoryStream DecryptAes(Stream inputStream, BufferedBlockCipher cipher, long length) + private static Stream DecryptAes(Stream inputStream, IBlockCipher cipher, long length) { int blockSize = cipher.GetBlockSize(); - int inputLength = (int)length; - int paddedLength = inputLength; + long inputLength = length; + long paddedLength = inputLength; if (paddedLength % blockSize > 0) { paddedLength += blockSize - paddedLength % blockSize; } - byte[] input = new byte[paddedLength]; - byte[] output = new byte[cipher.GetOutputSize(paddedLength)]; + byte[] input = new byte[blockSize]; + byte[] output = new byte[blockSize]; + var outputStream = new Bdt5.Bdt5InnerStream(paddedLength); - inputStream.Read(input, 0, inputLength); - int len = cipher.ProcessBytes(input, 0, input.Length, output, 0); - cipher.DoFinal(output, len); + for (long pos = 0; pos < inputLength; pos += blockSize) + { + int read = inputStream.Read(input, 0, blockSize); + if (read < blockSize) + { + for (int i = read; i < blockSize; i++) input[i] = 0; + } + cipher.ProcessBlock(input, 0, output, 0); + outputStream.Write(output, 0, blockSize); + } - MemoryStream outputStream = new MemoryStream(); - outputStream.Write(output, 0, inputLength); outputStream.Seek(0, SeekOrigin.Begin); return outputStream; } @@ -135,7 +139,7 @@ public static AsymmetricKeyParameter GetKeyOrDefault(string key) } } - public static void DecryptAesEcb(MemoryStream inputStream, byte[] key, Bhd5Range[] ranges) + public static void DecryptAesEcb(Stream inputStream, byte[] key, Bhd5Range[] ranges) { var cipher = CreateAesEcbCipher(key); @@ -148,9 +152,14 @@ public static void DecryptAesEcb(MemoryStream inputStream, byte[] key, Bhd5Range inputStream.Position = range.StartOffset; long length = range.EndOffset - range.StartOffset; - MemoryStream decryptedStream = DecryptAes(inputStream, cipher, length); + Stream decryptedStream = DecryptAes(inputStream, cipher, length); inputStream.Position = range.StartOffset; - decryptedStream.WriteTo(inputStream); + byte[] buf = new byte[1000]; + for (long pos = 0; pos < length; pos += buf.Length) + { + int read = decryptedStream.Read(buf, 0, 1000); + inputStream.Write(buf, 0, read); + } } } } diff --git a/BinderTool.Core/Enc/EncFile.cs b/BinderTool.Core/Enc/EncFile.cs index d55510a..6b2365c 100644 --- a/BinderTool.Core/Enc/EncFile.cs +++ b/BinderTool.Core/Enc/EncFile.cs @@ -19,7 +19,7 @@ private EncFile(byte[] key) private readonly byte[] _iv = new byte[EncryptionIvCbcSize]; - public MemoryStream Data { get; private set; } + public Stream Data { get; private set; } public static EncFile ReadEncFile(Stream inputStream, byte[] key, GameVersion version = GameVersion.Common) { diff --git a/BinderTool.Core/GameVersion.cs b/BinderTool.Core/GameVersion.cs index 89c6ee2..40689e4 100644 --- a/BinderTool.Core/GameVersion.cs +++ b/BinderTool.Core/GameVersion.cs @@ -6,6 +6,7 @@ public enum GameVersion DarkSouls2, DarkSouls3, Bloodborne, - Sekiro + Sekiro, + EldenRing } } \ No newline at end of file diff --git a/BinderTool.Core/Sl2/Sl2UserData.cs b/BinderTool.Core/Sl2/Sl2UserData.cs index b2921ec..167bf11 100644 --- a/BinderTool.Core/Sl2/Sl2UserData.cs +++ b/BinderTool.Core/Sl2/Sl2UserData.cs @@ -21,8 +21,10 @@ public Sl2UserData(byte[] key) public byte[] EncryptedUserData { get; private set; } - public byte[] DecryptedUserData => CryptographyUtility.DecryptAesCbc(new MemoryStream(EncryptedUserData), _key, _iv).ToArray(); - + //public byte[] DecryptedUserData => CryptographyUtility.DecryptAesCbc(new MemoryStream(EncryptedUserData), _key, _iv).ToArray(); + public byte[] DecryptedUserData => null; + + public static Sl2UserData ReadSl2UserData(Stream inputStream, byte[] key, int size, string name) { Sl2UserData sl2UserData = new Sl2UserData(key); diff --git a/BinderTool/BinderTool.csproj b/BinderTool/BinderTool.csproj index 0705577..f4066d8 100644 --- a/BinderTool/BinderTool.csproj +++ b/BinderTool/BinderTool.csproj @@ -99,6 +99,11 @@ PreserveNewest + + + PreserveNewest + +