From 2a55f0a87cde3e3e636f3f7614572c8fb9ea9c53 Mon Sep 17 00:00:00 2001 From: Nordgaren Date: Fri, 28 Jun 2024 18:52:14 -0700 Subject: [PATCH 1/2] zstd support --- .../SoulsFormats/SoulsFormats/Formats/DCX.cs | 98 ++++++++++++++++++- .../SoulsFormats/SoulsFormats.csproj | 1 + .../SoulsFormats/SoulsFormats/Util/SFUtil.cs | 19 ++++ 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/src/Andre/SoulsFormats/SoulsFormats/Formats/DCX.cs b/src/Andre/SoulsFormats/SoulsFormats/Formats/DCX.cs index d62fd141a..bf03ba004 100644 --- a/src/Andre/SoulsFormats/SoulsFormats/Formats/DCX.cs +++ b/src/Andre/SoulsFormats/SoulsFormats/Formats/DCX.cs @@ -3,6 +3,8 @@ using System.IO.Compression; using System.IO.MemoryMappedFiles; using DotNext.IO.MemoryMappedFiles; +using System.Linq; +using ZstdNet; namespace SoulsFormats { @@ -125,6 +127,10 @@ internal static Memory Decompress(BinaryReaderEx br, out Type type) int compressionLevel = br.GetByte(0x30); type = compressionLevel == 9 ? Type.DCX_KRAK_MAX : Type.DCX_KRAK; } + else if (format == "ZSTD") + { + type = Type.ZSTD; + } } else { @@ -155,6 +161,8 @@ internal static Memory Decompress(BinaryReaderEx br, out Type type) return DecompressDCXKRAK(br); else if (type == Type.DCX_KRAK_MAX) return DecompressDCXKRAK(br, 9); + else if (type == Type.ZSTD) + return DecompressDCXZSTD(br); else throw new FormatException("Unknown DCX format."); } @@ -382,6 +390,42 @@ private static Memory DecompressDCXKRAK(BinaryReaderEx br, byte compressio return Oodle.GetOodleCompressor(compressionLevel).Decompress(compressed, uncompressedSize); } + + private static byte[] DecompressDCXZSTD(BinaryReaderEx br) + { + br.AssertASCII("DCX\0"); + br.AssertInt32(0x11000); + br.AssertInt32(0x18); + br.AssertInt32(0x24); + br.AssertInt32(0x44); + br.AssertInt32(0x4C); + + br.AssertASCII("DCS\0"); + int uncompressedSize = br.ReadInt32(); + int compressedSize = br.ReadInt32(); + + br.AssertASCII("DCP\0"); + br.AssertASCII("ZSTD"); + br.AssertInt32(0x20); + br.AssertByte(0x15); + br.AssertByte(0); + br.AssertByte(0); + br.AssertByte(0); + br.AssertInt32(0x0); + br.AssertByte(0); + br.AssertByte(0); + br.AssertByte(0); + br.AssertByte(0); + br.AssertInt32(0x0); + br.AssertInt32(0x010100); + + br.AssertASCII("DCA\0"); + br.AssertInt32(8); + + byte[] decompressed = SFUtil.ReadZstd(br, compressedSize); + + return decompressed; + } #region Public Compress /// @@ -427,6 +471,8 @@ internal static void Compress(Span data, BinaryWriterEx bw, Type type) CompressDCXKRAK(data, bw); else if (type == Type.DCX_KRAK_MAX) CompressDCXKRAK(data, bw, 9); + else if (type == Type.ZSTD) + CompressDCXZSTD(data, bw); else if (type == Type.Unknown) throw new ArgumentException("You cannot compress a DCX with an unknown type."); else @@ -642,6 +688,51 @@ private static void CompressDCXKRAK(Span data, BinaryWriterEx bw, byte com bw.WriteBytes(compressed); bw.Pad(0x10); } + + private static void CompressDCXZSTD(Span data, BinaryWriterEx bw) + { + MemoryStream ms = new MemoryStream(); + CompressionStream cs = new CompressionStream(ms); + cs.Write(data); + cs.Flush(); + cs.Write(new byte[] {}); + cs.Flush(); + // Compressor compressor = new Compressor(); + // + // byte[] compressed = compressor.Wrap(data).ToArray(); + // compressed = compressor.Wrap(new byte[] {}); + byte[] compressed = ms.ToArray(); + File.WriteAllBytes(@"G:\Steam\steamapps\common\ELDEN RING\Game\_unknown\bingus.bin", compressed); + bw.WriteASCII("DCX\0"); + bw.WriteInt32(0x11000); + bw.WriteInt32(0x18); + bw.WriteInt32(0x24); + bw.WriteInt32(0x44); + bw.WriteInt32(0x4C); + + bw.WriteASCII("DCS\0"); + bw.WriteInt32(data.Length); + bw.WriteInt32(compressed.Length); + + bw.WriteASCII("DCP\0"); + bw.WriteASCII("ZSTD"); + bw.WriteInt32(0x20); + bw.WriteByte(0x15); + bw.WriteByte(0); + bw.WriteByte(0); + bw.WriteByte(0); + bw.WriteInt32(0x0); + bw.WriteByte(0); + bw.WriteByte(0); + bw.WriteByte(0); + bw.WriteByte(0); + bw.WriteInt32(0x0); + bw.WriteInt32(0x010100); + + bw.WriteASCII("DCA\0"); + bw.WriteInt32(8); + bw.WriteBytes(compressed); + } /// /// Specific compression format used for a certain file. @@ -711,7 +802,12 @@ public enum Type /// /// DCX header, different Oodle compression. Used in Armored Core VI. /// - DCX_KRAK_MAX + DCX_KRAK_MAX, + + /// + /// DCX header, new compression format chosen via dart board. Used in Elden Ring: Shadow of the Erdtree. + /// + ZSTD } /// diff --git a/src/Andre/SoulsFormats/SoulsFormats/SoulsFormats.csproj b/src/Andre/SoulsFormats/SoulsFormats/SoulsFormats.csproj index 33c22b0d6..688194f90 100644 --- a/src/Andre/SoulsFormats/SoulsFormats/SoulsFormats.csproj +++ b/src/Andre/SoulsFormats/SoulsFormats/SoulsFormats.csproj @@ -24,5 +24,6 @@ + diff --git a/src/Andre/SoulsFormats/SoulsFormats/Util/SFUtil.cs b/src/Andre/SoulsFormats/SoulsFormats/Util/SFUtil.cs index e625d51d9..a5a13082a 100644 --- a/src/Andre/SoulsFormats/SoulsFormats/Util/SFUtil.cs +++ b/src/Andre/SoulsFormats/SoulsFormats/Util/SFUtil.cs @@ -7,6 +7,7 @@ using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; +using ZstdNet; namespace SoulsFormats { @@ -326,6 +327,24 @@ public static byte[] ReadZlib(BinaryReaderEx br, int compressedSize) return decompressedStream.ToArray(); } } + + /// + /// Reads a ZSTD compressed file from a BinaryReaderEx and returns the uncompressed data. + /// + public static byte[] ReadZstd(BinaryReaderEx br, int compressedSize) + { + byte[] compressed = br.ReadBytes(compressedSize); + + using (var decompressedStream = new MemoryStream()) + { + using (var compressedStream = new MemoryStream(compressed)) + using (var deflateStream = new DecompressionStream(compressedStream)) + { + deflateStream.CopyTo(decompressedStream); + } + return decompressedStream.ToArray(); + } + } /// /// Computes an Adler32 checksum used by Zlib. From b83e718a71d79c4c1a74649705fd584a3f337314 Mon Sep 17 00:00:00 2001 From: Nordgaren Date: Fri, 28 Jun 2024 19:33:29 -0700 Subject: [PATCH 2/2] remove compression method that is meaningless --- .../SoulsFormats/SoulsFormats/Formats/DCX.cs | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/src/Andre/SoulsFormats/SoulsFormats/Formats/DCX.cs b/src/Andre/SoulsFormats/SoulsFormats/Formats/DCX.cs index bf03ba004..30412118a 100644 --- a/src/Andre/SoulsFormats/SoulsFormats/Formats/DCX.cs +++ b/src/Andre/SoulsFormats/SoulsFormats/Formats/DCX.cs @@ -689,50 +689,6 @@ private static void CompressDCXKRAK(Span data, BinaryWriterEx bw, byte com bw.Pad(0x10); } - private static void CompressDCXZSTD(Span data, BinaryWriterEx bw) - { - MemoryStream ms = new MemoryStream(); - CompressionStream cs = new CompressionStream(ms); - cs.Write(data); - cs.Flush(); - cs.Write(new byte[] {}); - cs.Flush(); - // Compressor compressor = new Compressor(); - // - // byte[] compressed = compressor.Wrap(data).ToArray(); - // compressed = compressor.Wrap(new byte[] {}); - byte[] compressed = ms.ToArray(); - File.WriteAllBytes(@"G:\Steam\steamapps\common\ELDEN RING\Game\_unknown\bingus.bin", compressed); - bw.WriteASCII("DCX\0"); - bw.WriteInt32(0x11000); - bw.WriteInt32(0x18); - bw.WriteInt32(0x24); - bw.WriteInt32(0x44); - bw.WriteInt32(0x4C); - - bw.WriteASCII("DCS\0"); - bw.WriteInt32(data.Length); - bw.WriteInt32(compressed.Length); - - bw.WriteASCII("DCP\0"); - bw.WriteASCII("ZSTD"); - bw.WriteInt32(0x20); - bw.WriteByte(0x15); - bw.WriteByte(0); - bw.WriteByte(0); - bw.WriteByte(0); - bw.WriteInt32(0x0); - bw.WriteByte(0); - bw.WriteByte(0); - bw.WriteByte(0); - bw.WriteByte(0); - bw.WriteInt32(0x0); - bw.WriteInt32(0x010100); - - bw.WriteASCII("DCA\0"); - bw.WriteInt32(8); - bw.WriteBytes(compressed); - } /// /// Specific compression format used for a certain file.