Add project restore files and NuGet cache for GsaViewer
- Created project.nuget.cache to store NuGet package cache information. - Added project.packagespec.json to define project restore settings and dependencies. - Included rider.project.restore.info for Rider IDE integration.
This commit is contained in:
88
GsaEditor.Core/Compression/ZlibHelper.cs
Normal file
88
GsaEditor.Core/Compression/ZlibHelper.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace GsaEditor.Core.Compression;
|
||||
|
||||
/// <summary>
|
||||
/// Provides zlib compression and decompression utilities for GSA archive entries.
|
||||
/// The GSA format stores zlib-wrapped data: 2-byte header (CMF+FLG) + deflate stream + 4-byte Adler-32 checksum.
|
||||
/// This helper skips the 2-byte zlib header and uses <see cref="DeflateStream"/> for the core deflate data.
|
||||
/// </summary>
|
||||
public static class ZlibHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Decompresses zlib-wrapped data into a byte array of the specified original size.
|
||||
/// Skips the 2-byte zlib header before feeding data to <see cref="DeflateStream"/>.
|
||||
/// </summary>
|
||||
/// <param name="compressedData">The zlib-wrapped compressed data (header + deflate + checksum).</param>
|
||||
/// <param name="originalLength">The expected decompressed size in bytes.</param>
|
||||
/// <returns>The decompressed byte array.</returns>
|
||||
/// <exception cref="InvalidDataException">Thrown when decompression fails or data is corrupt.</exception>
|
||||
public static byte[] Decompress(byte[] compressedData, uint originalLength)
|
||||
{
|
||||
if (compressedData.Length < 2)
|
||||
throw new InvalidDataException("Compressed data is too short to contain a zlib header.");
|
||||
|
||||
// Skip the 2-byte zlib header (typically 0x78 0x9C for default compression)
|
||||
using var ms = new MemoryStream(compressedData, 2, compressedData.Length - 2);
|
||||
using var deflate = new DeflateStream(ms, CompressionMode.Decompress);
|
||||
|
||||
var result = new byte[originalLength];
|
||||
int totalRead = 0;
|
||||
while (totalRead < result.Length)
|
||||
{
|
||||
int bytesRead = deflate.Read(result, totalRead, result.Length - totalRead);
|
||||
if (bytesRead == 0)
|
||||
break;
|
||||
totalRead += bytesRead;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compresses data using zlib format: 2-byte header + deflate stream + 4-byte Adler-32 checksum.
|
||||
/// </summary>
|
||||
/// <param name="data">The uncompressed data to compress.</param>
|
||||
/// <returns>The zlib-wrapped compressed byte array.</returns>
|
||||
public static byte[] Compress(byte[] data)
|
||||
{
|
||||
using var output = new MemoryStream();
|
||||
|
||||
// Write zlib header: CMF=0x78 (deflate method, 32K window), FLG=0x9C (default level, valid check bits)
|
||||
output.WriteByte(0x78);
|
||||
output.WriteByte(0x9C);
|
||||
|
||||
using (var deflate = new DeflateStream(output, CompressionLevel.Optimal, leaveOpen: true))
|
||||
{
|
||||
deflate.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
// Write Adler-32 checksum in big-endian byte order
|
||||
uint adler = ComputeAdler32(data);
|
||||
output.WriteByte((byte)(adler >> 24));
|
||||
output.WriteByte((byte)(adler >> 16));
|
||||
output.WriteByte((byte)(adler >> 8));
|
||||
output.WriteByte((byte)adler);
|
||||
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the Adler-32 checksum of the given data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to checksum.</param>
|
||||
/// <returns>The 32-bit Adler-32 checksum value.</returns>
|
||||
private static uint ComputeAdler32(byte[] data)
|
||||
{
|
||||
const uint MOD_ADLER = 65521;
|
||||
uint a = 1, b = 0;
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
a = (a + data[i]) % MOD_ADLER;
|
||||
b = (b + a) % MOD_ADLER;
|
||||
}
|
||||
|
||||
return (b << 16) | a;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user