175 lines
6.8 KiB
C#
175 lines
6.8 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using Unity.SharpZipLib.Zip.Compression.Streams;
|
|
using UnityEngine;
|
|
using CompressionLevel = System.IO.Compression.CompressionLevel;
|
|
|
|
namespace BrewMonster
|
|
{
|
|
public class AFilePackage
|
|
{
|
|
private const short Z_OK = 0;
|
|
private const short Z_ERROR = -2;
|
|
private const short Z_BUF_ERROR = -5;
|
|
private const short Z_DATA_ERROR = -3;
|
|
|
|
|
|
/*
|
|
Uncompress a data buffer
|
|
pCompressedBuffer IN buffer contains compressed data to be uncompressed
|
|
dwCompressedLength IN the compressed data size
|
|
pFileBuffer OUT the uncompressed data buffer
|
|
pdwFileLength IN/OUT the uncompressed data buffer size as input
|
|
when out, it is the real uncompressed data length
|
|
|
|
RETURN: 0, ok
|
|
-1, dest buffer is too small
|
|
-2, unknown error
|
|
*/
|
|
public static int Uncompress(byte[] compressedBuffer, int compressedLength, byte[] fileBuffer, ref uint fileLength)
|
|
{
|
|
int result = ZlibUnCompressDeflate(fileBuffer, ref fileLength, compressedBuffer, compressedLength);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compress data in zlib format (2-byte header + deflate + 4-byte Adler-32), matching what UnCompress expects.
|
|
/// 压缩为 zlib 格式(2 字节头 + deflate + 4 字节 Adler-32),与 UnCompress 一致。
|
|
/// </summary>
|
|
/// <param name="dstOffset">Start index in dst where compressed bytes are written. 压缩数据写入 dst 的起始下标。</param>
|
|
/// <param name="dstLen">On input max length; on output actual compressed length. 输入为最大长度,输出为实际压缩长度。</param>
|
|
/// <returns>0 ok, non-zero error</returns>
|
|
public static int Compress(byte[] src, int srcOffset, int srcLen, byte[] dst, int dstOffset, ref int dstLen)
|
|
{
|
|
try
|
|
{
|
|
// Raw deflate into temporary buffer (same as decompression uses DeflateStream)
|
|
byte[] deflateBytes;
|
|
using (var deflateOutput = new MemoryStream())
|
|
{
|
|
using (var deflate = new DeflateStream(deflateOutput, CompressionLevel.Optimal))
|
|
{
|
|
deflate.Write(src, srcOffset, srcLen);
|
|
}
|
|
deflateBytes = deflateOutput.ToArray();
|
|
}
|
|
|
|
// Zlib format: 2-byte header + deflate + 4-byte Adler-32 (matches ZlibUnCompressDeflate which skips 2, length-6)
|
|
const int zlibHeaderLen = 2;
|
|
const int zlibFooterLen = 4;
|
|
int totalLen = zlibHeaderLen + deflateBytes.Length + zlibFooterLen;
|
|
|
|
if (dstOffset + totalLen > dst.Length)
|
|
return Z_BUF_ERROR;
|
|
|
|
int writeAt = dstOffset;
|
|
|
|
// 2-byte zlib header (deflate, 32K window, default compression)
|
|
dst[writeAt++] = 0x78;
|
|
dst[writeAt++] = 0x9C;
|
|
|
|
Buffer.BlockCopy(deflateBytes, 0, dst, writeAt, deflateBytes.Length);
|
|
writeAt += deflateBytes.Length;
|
|
|
|
// 4-byte Adler-32 of uncompressed data (big-endian)
|
|
uint adler = Adler32(src, srcOffset, srcLen);
|
|
dst[writeAt++] = (byte)(adler >> 24);
|
|
dst[writeAt++] = (byte)(adler >> 16);
|
|
dst[writeAt++] = (byte)(adler >> 8);
|
|
dst[writeAt++] = (byte)(adler);
|
|
|
|
dstLen = totalLen;
|
|
return Z_OK;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine($"ERROR::AFilePackage::Compress: {e.Message}");
|
|
return Z_ERROR;
|
|
}
|
|
}
|
|
|
|
private static uint Adler32(byte[] data, int offset, int length)
|
|
{
|
|
const uint Mod = 65521u;
|
|
uint a = 1u, b = 0u;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
byte c = data[offset + i];
|
|
a = (a + c) % Mod;
|
|
b = (b + a) % Mod;
|
|
}
|
|
return (b << 16) | a;
|
|
}
|
|
|
|
private static int ZlibUnCompress(byte[] dest, ref int destLen, byte[] source, int sourceLen)
|
|
{
|
|
try
|
|
{
|
|
using (MemoryStream input = new MemoryStream(source, 0, sourceLen)) // No need to skip zlib headers manually
|
|
using (InflaterInputStream decompressionStream = new InflaterInputStream(input))
|
|
using (MemoryStream output = new MemoryStream())
|
|
{
|
|
decompressionStream.CopyTo(output);
|
|
|
|
int totalOut = (int)output.Length;
|
|
if (totalOut > dest.Length)
|
|
{
|
|
return Z_BUF_ERROR; // Buffer too small
|
|
}
|
|
|
|
Array.Copy(output.ToArray(), dest, totalOut);
|
|
destLen = totalOut;
|
|
}
|
|
|
|
return Z_OK; // Success
|
|
}
|
|
catch (InvalidDataException invalidDataException)
|
|
{
|
|
Console.WriteLine($"ERROR::ZlibUnCompress::InvalidDataException: {invalidDataException.Message}");
|
|
return Z_DATA_ERROR; // Invalid zlib data
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine($"ERROR::ZlibUnCompress::Exception: {e.Message}");
|
|
return Z_ERROR; // General error
|
|
}
|
|
}
|
|
|
|
private static int ZlibUnCompressDeflate(byte[] dest, ref uint destLen, byte[] source, int sourceLen)
|
|
{
|
|
try
|
|
{
|
|
using (MemoryStream input = new MemoryStream(source, 2, sourceLen - 6))
|
|
using (DeflateStream decompressionStream = new DeflateStream(input, CompressionMode.Decompress))
|
|
using (MemoryStream output = new MemoryStream())
|
|
{
|
|
decompressionStream.CopyTo(output);
|
|
|
|
uint totalOut = (uint)output.Length;
|
|
if (totalOut > dest.Length)
|
|
{
|
|
return Z_BUF_ERROR; // Buffer too small
|
|
}
|
|
|
|
Array.Copy(output.ToArray(), dest, totalOut);
|
|
destLen = totalOut;
|
|
}
|
|
|
|
return Z_OK; // Success
|
|
}
|
|
catch (InvalidDataException invalidDataException)
|
|
{
|
|
Console.WriteLine($"ERROR::ZlibUnCompress::InvalidDataException: {invalidDataException.Message}");
|
|
return Z_DATA_ERROR; // Invalid zlib data
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine($"ERROR::ZlibUnCompress::Exception: {e.Message}");
|
|
return Z_ERROR; // General error
|
|
}
|
|
}
|
|
}
|
|
}
|