diff --git a/AssetStudio/BundleFile.cs b/AssetStudio/BundleFile.cs index dd82712..3ada94c 100644 --- a/AssetStudio/BundleFile.cs +++ b/AssetStudio/BundleFile.cs @@ -35,7 +35,7 @@ namespace AssetStudio Lzma, Lz4, Lz4HC, - Lzham + Lz4Inv, } public class BundleFile @@ -378,20 +378,31 @@ namespace AssetStudio } case CompressionType.Lz4: case CompressionType.Lz4HC: + case CompressionType.Lz4Inv: { var compressedSize = (int)blockInfo.compressedSize; var compressedBytes = BigArrayPool.Shared.Rent(compressedSize); - reader.Read(compressedBytes, 0, compressedSize); + _ = reader.Read(compressedBytes, 0, compressedSize); var uncompressedSize = (int)blockInfo.uncompressedSize; var uncompressedBytes = BigArrayPool.Shared.Rent(uncompressedSize); - var numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize); - if (numWrite != uncompressedSize) + try { - throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); + var compressedSpan = compressedBytes.AsSpan(0, compressedSize); + var uncompressedSpan = uncompressedBytes.AsSpan(0, uncompressedSize); + var numWrite = compressionType == CompressionType.Lz4Inv + ? LZ4Inv.Instance.Decompress(compressedSpan, uncompressedSpan) + : LZ4Codec.Decode(compressedSpan, uncompressedSpan); + if (numWrite != uncompressedSize) + { + throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); + } + blocksStream.Write(uncompressedBytes, 0, uncompressedSize); + } + finally + { + BigArrayPool.Shared.Return(compressedBytes); + BigArrayPool.Shared.Return(uncompressedBytes); } - blocksStream.Write(uncompressedBytes, 0, uncompressedSize); - BigArrayPool.Shared.Return(compressedBytes); - BigArrayPool.Shared.Return(uncompressedBytes); break; } default: diff --git a/AssetStudio/LZ4/LZ4.cs b/AssetStudio/LZ4/LZ4.cs new file mode 100644 index 0000000..cb9cb26 --- /dev/null +++ b/AssetStudio/LZ4/LZ4.cs @@ -0,0 +1,75 @@ +using System; + +namespace AssetStudio +{ + public class LZ4 + { + public static LZ4 Instance => new LZ4(); + + public virtual int Decompress(ReadOnlySpan cmp, Span dec) + { + int cmpPos = 0; + int decPos = 0; + + do + { + var (encCount, litCount) = GetLiteralToken(cmp, ref cmpPos); + + //Copy literal chunk + litCount = GetLength(litCount, cmp, ref cmpPos); + + cmp.Slice(cmpPos, litCount).CopyTo(dec.Slice(decPos)); + + cmpPos += litCount; + decPos += litCount; + + if (cmpPos >= cmp.Length) + { + break; + } + + //Copy compressed chunk + int back = GetChunkEnd(cmp, ref cmpPos); + + encCount = GetLength(encCount, cmp, ref cmpPos) + 4; + + int encPos = decPos - back; + + if (encCount <= back) + { + dec.Slice(encPos, encCount).CopyTo(dec.Slice(decPos)); + decPos += encCount; + } + else + { + while (encCount-- > 0) + { + dec[decPos++] = dec[encPos++]; + } + } + } while (cmpPos < cmp.Length && decPos < dec.Length); + + return decPos; + } + + protected virtual (int encCount, int litCount) GetLiteralToken(ReadOnlySpan cmp, ref int cmpPos) => + ((cmp[cmpPos] >> 0) & 0xf, (cmp[cmpPos++] >> 4) & 0xf); + + protected virtual int GetChunkEnd(ReadOnlySpan cmp, ref int cmpPos) => + cmp[cmpPos++] << 0 | cmp[cmpPos++] << 8; + + protected virtual int GetLength(int length, ReadOnlySpan cmp, ref int cmpPos) + { + byte sum; + + if (length == 0xf) + { + do + { + length += sum = cmp[cmpPos++]; + } while (sum == 0xff); + } + return length; + } + } +} \ No newline at end of file diff --git a/AssetStudio/LZ4/LZ4Inv.cs b/AssetStudio/LZ4/LZ4Inv.cs new file mode 100644 index 0000000..25083a5 --- /dev/null +++ b/AssetStudio/LZ4/LZ4Inv.cs @@ -0,0 +1,15 @@ +using System; + +namespace AssetStudio +{ + public class LZ4Inv : LZ4 + { + public new static LZ4Inv Instance => new LZ4Inv(); + + protected override (int encCount, int litCount) GetLiteralToken(ReadOnlySpan cmp, ref int cmpPos) => + ((cmp[cmpPos] >> 4) & 0xf, (cmp[cmpPos++] >> 0) & 0xf); + + protected override int GetChunkEnd(ReadOnlySpan cmp, ref int cmpPos) => + cmp[cmpPos++] << 8 | cmp[cmpPos++] << 0; + } +} \ No newline at end of file