using System.IO.Compression; namespace GsaEditor.Core.Compression; /// /// 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 for the core deflate data. /// public static class ZlibHelper { /// /// Decompresses zlib-wrapped data into a byte array of the specified original size. /// Skips the 2-byte zlib header before feeding data to . /// /// The zlib-wrapped compressed data (header + deflate + checksum). /// The expected decompressed size in bytes. /// The decompressed byte array. /// Thrown when decompression fails or data is corrupt. 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; } /// /// Compresses data using zlib format: 2-byte header + deflate stream + 4-byte Adler-32 checksum. /// /// The uncompressed data to compress. /// The zlib-wrapped compressed byte array. 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(); } /// /// Computes the Adler-32 checksum of the given data. /// /// The data to checksum. /// The 32-bit Adler-32 checksum value. 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; } }