Add ZSTD support

This commit is contained in:
2025-10-14 11:30:24 +08:00
parent 7692cfdec7
commit 11a00be4ca
3 changed files with 123 additions and 2 deletions

View File

@@ -162,6 +162,7 @@
<Compile Include="Unity\ScriptDSM.cs" /> <Compile Include="Unity\ScriptDSM.cs" />
<Compile Include="Xuse\ArcNT.cs" /> <Compile Include="Xuse\ArcNT.cs" />
<Compile Include="Xuse\ArcWVB.cs" /> <Compile Include="Xuse\ArcWVB.cs" />
<Compile Include="ZstdStream.cs" />
<Compile Include="Zyx\ImageXMG.cs" /> <Compile Include="Zyx\ImageXMG.cs" />
<Compile Include="AudioAIFF.cs" /> <Compile Include="AudioAIFF.cs" />
<Compile Include="Basil\ArcMIF.cs" /> <Compile Include="Basil\ArcMIF.cs" />
@@ -1266,7 +1267,14 @@
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Strings\arcStrings.ru-RU.resx" /> <EmbeddedResource Include="Strings\arcStrings.ru-RU.resx" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup>
<Content Include="x64\zstd.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="x86\zstd.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PreBuildEvent>perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName) <PreBuildEvent>perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName)

View File

@@ -248,11 +248,22 @@ namespace GameRes.Formats.Circus
} }
} }
private Stream GetDecompressStream() {
long pos = m_input.Position;
uint header = m_input.ReadUInt32();
m_input.Position = pos;
if (header == 0xFD2FB528) {
var zstd = ZstdReader.Unpack(m_input.AsStream);
return new MemoryStream(zstd);
} else
return new ZLibStream(m_input.AsStream, CompressionMode.Decompress);
}
private void UnpackV2 () private void UnpackV2 ()
{ {
int pixel_size = m_bpp / 8; int pixel_size = m_bpp / 8;
int src_stride = m_width * pixel_size; int src_stride = m_width * pixel_size;
using (var zlib = new ZLibStream (m_input.AsStream, CompressionMode.Decompress, true)) using (var zlib = GetDecompressStream())
using (var src = new BinaryReader (zlib)) using (var src = new BinaryReader (zlib))
{ {
if (m_bpp >= 24) if (m_bpp >= 24)

102
ArcFormats/ZstdStream.cs Normal file
View File

@@ -0,0 +1,102 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace GameRes.Compression
{
public static class ZstdReader
{
private const int BUFFER_SIZE = 4096;
[DllImport("zstd.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr ZSTD_createDStream();
[DllImport("zstd.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int ZSTD_decompressStream(IntPtr zds, ref ZSTD_outBuffer output, ref ZSTD_inBuffer input);
[DllImport("zstd.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int ZSTD_freeDStream(IntPtr zds);
[StructLayout(LayoutKind.Sequential)]
private struct ZSTD_inBuffer
{
public IntPtr src;
public UIntPtr size;
public UIntPtr pos;
}
[StructLayout(LayoutKind.Sequential)]
private struct ZSTD_outBuffer
{
public IntPtr dst;
public UIntPtr size;
public UIntPtr pos;
}
public static byte[] Unpack(Stream compressedStream)
{
if (compressedStream == null) throw new ArgumentNullException(nameof(compressedStream));
IntPtr dctx = ZSTD_createDStream();
if (dctx == IntPtr.Zero)
throw new InvalidOperationException("Failed to create ZSTD_DStream.");
byte[] inBuf = new byte[BUFFER_SIZE];
byte[] outBuf = new byte[BUFFER_SIZE];
using (MemoryStream result = new MemoryStream())
{
try
{
ZSTD_inBuffer input = new ZSTD_inBuffer();
ZSTD_outBuffer output = new ZSTD_outBuffer();
GCHandle inHandle = default, outHandle = default;
try
{
inHandle = GCHandle.Alloc(inBuf, GCHandleType.Pinned);
outHandle = GCHandle.Alloc(outBuf, GCHandleType.Pinned);
int lastRet;
while (true)
{
int bytesRead = compressedStream.Read(inBuf, 0, inBuf.Length);
if (bytesRead == 0) break;
input.src = inHandle.AddrOfPinnedObject();
input.size = (UIntPtr)(uint)bytesRead;
input.pos = UIntPtr.Zero;
while (input.pos.ToUInt64() < input.size.ToUInt64())
{
output.dst = outHandle.AddrOfPinnedObject();
output.size = (UIntPtr)(uint)outBuf.Length;
output.pos = UIntPtr.Zero;
lastRet = ZSTD_decompressStream(dctx, ref output, ref input);
if (lastRet < 0)
throw new InvalidDataException($"ZSTD_decompressStream error: {lastRet}");
if (output.pos.ToUInt64() > 0)
result.Write(outBuf, 0, (int)output.pos.ToUInt64());
if (lastRet == 0) break;
}
}
}
finally
{
if (inHandle.IsAllocated) inHandle.Free();
if (outHandle.IsAllocated) outHandle.Free();
}
return result.ToArray();
}
finally
{
ZSTD_freeDStream(dctx);
}
}
}
}
}