diff --git a/src/Andre/SoulsFormats/SoulsFormats/Formats/DCX.cs b/src/Andre/SoulsFormats/SoulsFormats/Formats/DCX.cs index d62fd141a..30412118a 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,7 @@ private static void CompressDCXKRAK(Span data, BinaryWriterEx bw, byte com bw.WriteBytes(compressed); bw.Pad(0x10); } + /// /// Specific compression format used for a certain file. @@ -711,7 +758,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.