using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Abstractions.Extractors.Unity.Models;
namespace Abstractions.Extractors.Unity.Services;
///
/// Sniffs a file's header for reporting purposes (bundle_metadata.json / extraction_log.txt).
/// Actual bundle/assets-file decompression is handled internally by AssetsTools.NET's AssetsManager
/// (see BundleLoader) - this class only identifies what's there, it doesn't decompress anything.
///
public static class CompressionDetector
{
public static string DetectCompressionType(ReadOnlySpan data)
{
switch (data.Length)
{
case < 2:
return "unknown";
case >= 4 when data[..4].SequenceEqual("LZ4\0"u8):
return "lz4";
}
if (data.Length >= 8 && data[..8].SequenceEqual("UnityFS\0"u8))
return "unityfs";
if (data.Length >= 8 && data[..8].SequenceEqual("UnityRaw"u8))
return "raw";
return (data[0], data[1]) switch
{
(0x78, 0x9C) => "zlib",
(0x78, 0x01) => "zlib",
(0x78, 0xDA) => "zlib",
(0x1F, 0x8B) => "gzip",
_ => "unknown"
};
}
public static async Task GetFileSignatureAsync(string path, CancellationToken cancellationToken = default)
{
try
{
await using var stream = File.OpenRead(path);
var header = new byte[32];
var read = await stream.ReadAsync(header.AsMemory(0, 32), cancellationToken);
if (read <= 0)
return new FileSignature([], [], "unknown", 0);
var fileInfo = new FileInfo(path);
return new FileSignature(
header[..Math.Min(8, read)],
read > 8 ? header[8..Math.Min(12, read)] : [],
DetectCompressionType(header.AsSpan(0, read)),
fileInfo.Length);
}
catch
{
return new FileSignature([], [], "unknown", 0);
}
}
}