- 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.
89 lines
3.3 KiB
C#
89 lines
3.3 KiB
C#
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;
|
|
}
|
|
}
|