- [Core] Fix bug where some buffers are unintentionally cached, causing undesired side effects.

This commit is contained in:
Razmoth
2024-01-25 11:20:14 +04:00
parent 5e35469435
commit 51a14820a8
9 changed files with 389 additions and 336 deletions

View File

@@ -1,10 +0,0 @@
using System.Buffers;
namespace AssetStudio
{
public static class BigArrayPool<T>
{
private static readonly ArrayPool<T> s_shared = ArrayPool<T>.Create(64 * 1024 * 1024, 3);
public static ArrayPool<T> Shared => s_shared;
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -124,22 +125,28 @@ namespace AssetStudio
var compressedSize = (int)blockInfo.compressedSize;
var uncompressedSize = (int)blockInfo.uncompressedSize;
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
reader.Read(compressedBytes, 0, compressedSize);
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
var numWrite = LZ4.Decompress(compressedBytesSpan, uncompressedBytesSpan);
if (numWrite != uncompressedSize)
var compressedBytes = ArrayPool<byte>.Shared.Rent(compressedSize);
var uncompressedBytes = ArrayPool<byte>.Shared.Rent(uncompressedSize);
try
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
reader.Read(compressedBytes, 0, compressedSize);
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
var numWrite = LZ4.Decompress(compressedBytesSpan, uncompressedBytesSpan);
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
}
finally
{
ArrayPool<byte>.Shared.Return(compressedBytes, true);
ArrayPool<byte>.Shared.Return(uncompressedBytes, true);
}
}
}

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Buffers;
namespace AssetStudio
{
@@ -445,15 +446,21 @@ namespace AssetStudio
case CompressionType.Lz4: //LZ4
case CompressionType.Lz4HC: //LZ4HC
{
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent((int)uncompressedSize);
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, (int)uncompressedSize);
var numWrite = LZ4.Decompress(blocksInfoBytesSpan, uncompressedBytesSpan);
if (numWrite != uncompressedSize)
var uncompressedBytes = ArrayPool<byte>.Shared.Rent((int)uncompressedSize);
try
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, (int)uncompressedSize);
var numWrite = LZ4.Decompress(blocksInfoBytesSpan, uncompressedBytesSpan);
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksInfoUncompresseddStream = new MemoryStream(uncompressedBytesSpan.ToArray());
}
finally
{
ArrayPool<byte>.Shared.Return(uncompressedBytes, true);
}
blocksInfoUncompresseddStream = new MemoryStream(uncompressedBytesSpan.ToArray());
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
break;
}
case CompressionType.Lz4Mr0k: //Lz4Mr0k
@@ -544,94 +551,114 @@ namespace AssetStudio
case CompressionType.Lz4Mr0k when Game.Type.IsMhyGroup(): //Lz4Mr0k
{
var compressedSize = (int)blockInfo.compressedSize;
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
reader.Read(compressedBytes, 0, compressedSize);
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
if (compressionType == CompressionType.Lz4Mr0k && Mr0kUtils.IsMr0k(compressedBytes))
{
Logger.Verbose($"Block encrypted with mr0k, decrypting...");
compressedBytesSpan = Mr0kUtils.Decrypt(compressedBytesSpan, (Mr0k)Game);
}
if (Game.Type.IsUnityCN() && ((int)blockInfo.flags & 0x100) != 0)
{
Logger.Verbose($"Decrypting block with UnityCN...");
UnityCN.DecryptBlock(compressedBytes, compressedSize, i);
}
if (Game.Type.IsNetEase() && i == 0)
{
NetEaseUtils.DecryptWithHeader(compressedBytesSpan);
}
if (Game.Type.IsArknightsEndfield() && i == 0)
{
FairGuardUtils.Decrypt(compressedBytesSpan);
}
if (Game.Type.IsOPFP())
{
OPFPUtils.Decrypt(compressedBytesSpan, reader.FullPath);
}
var uncompressedSize = (int)blockInfo.uncompressedSize;
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
var numWrite = LZ4.Decompress(compressedBytesSpan, uncompressedBytesSpan);
if (numWrite != uncompressedSize)
var compressedBytes = ArrayPool<byte>.Shared.Rent(compressedSize);
var uncompressedBytes = ArrayPool<byte>.Shared.Rent(uncompressedSize);
try
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
reader.Read(compressedBytesSpan);
if (compressionType == CompressionType.Lz4Mr0k && Mr0kUtils.IsMr0k(compressedBytes))
{
Logger.Verbose($"Block encrypted with mr0k, decrypting...");
compressedBytesSpan = Mr0kUtils.Decrypt(compressedBytesSpan, (Mr0k)Game);
}
if (Game.Type.IsUnityCN() && ((int)blockInfo.flags & 0x100) != 0)
{
Logger.Verbose($"Decrypting block with UnityCN...");
UnityCN.DecryptBlock(compressedBytes, compressedSize, i);
}
if (Game.Type.IsNetEase() && i == 0)
{
NetEaseUtils.DecryptWithHeader(compressedBytesSpan);
}
if (Game.Type.IsArknightsEndfield() && i == 0)
{
FairGuardUtils.Decrypt(compressedBytesSpan);
}
if (Game.Type.IsOPFP())
{
OPFPUtils.Decrypt(compressedBytesSpan, reader.FullPath);
}
var numWrite = LZ4.Decompress(compressedBytesSpan, uncompressedBytesSpan);
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksStream.Write(uncompressedBytesSpan);
}
finally
{
ArrayPool<byte>.Shared.Return(compressedBytes, true);
ArrayPool<byte>.Shared.Return(uncompressedBytes, true);
}
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
break;
}
case CompressionType.Lz4Inv when Game.Type.IsArknightsEndfield():
{
var compressedSize = (int)blockInfo.compressedSize;
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
reader.Read(compressedBytes, 0, compressedSize);
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
if (i == 0)
{
FairGuardUtils.Decrypt(compressedBytesSpan);
}
var uncompressedSize = (int)blockInfo.uncompressedSize;
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
var compressedBytes = ArrayPool<byte>.Shared.Rent(compressedSize);
var uncompressedBytes = ArrayPool<byte>.Shared.Rent(uncompressedSize);
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
var numWrite = LZ4Inv.Decompress(compressedBytesSpan, uncompressedBytesSpan);
if (numWrite != uncompressedSize)
try
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
reader.Read(compressedBytesSpan);
if (i == 0)
{
FairGuardUtils.Decrypt(compressedBytesSpan);
}
var numWrite = LZ4Inv.Decompress(compressedBytesSpan, uncompressedBytesSpan);
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksStream.Write(uncompressedBytesSpan);
}
finally
{
ArrayPool<byte>.Shared.Return(compressedBytes, true);
ArrayPool<byte>.Shared.Return(uncompressedBytes, true);
}
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
break;
}
case CompressionType.Zstd when !Game.Type.IsMhyGroup(): //Zstd
{
var compressedSize = (int)blockInfo.compressedSize;
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
reader.Read(compressedBytes, 0, compressedSize);
var uncompressedSize = (int)blockInfo.uncompressedSize;
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
var compressedBytes = ArrayPool<byte>.Shared.Rent(compressedSize);
var uncompressedBytes = ArrayPool<byte>.Shared.Rent(uncompressedSize);
try
{
reader.Read(compressedBytes, 0, compressedSize);
using var decompressor = new Decompressor();
var numWrite = decompressor.Unwrap(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize);
if (numWrite != uncompressedSize)
{
throw new IOException($"Zstd decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksStream.Write(uncompressedBytes.ToArray(), 0, uncompressedSize);
}
catch(Exception ex)
catch (Exception ex)
{
Console.WriteLine($"Zstd decompression error:\n{ex}");
}
blocksStream.Write(uncompressedBytes.ToArray(), 0, uncompressedSize);
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
finally
{
ArrayPool<byte>.Shared.Return(compressedBytes, true);
ArrayPool<byte>.Shared.Return(uncompressedBytes, true);
}
break;
}
default:

View File

@@ -1981,7 +1981,7 @@ namespace AssetStudio
var aclClip = m_MuscleClip.m_Clip.m_ACLClip as GIACLClip;
var resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size);
var ms = new MemoryStream();
using var ms = new MemoryStream();
ms.Write(aclClip.m_DatabaseData);
ms.Write(resourceReader.GetData());

View File

@@ -815,7 +815,7 @@ namespace AssetStudio
return reader;
}
MemoryStream ms = new MemoryStream();
MemoryStream ms = new();
if (version == 0x10)
{
var buffer = (stackalloc byte[8]);
@@ -929,7 +929,7 @@ namespace AssetStudio
}
Logger.Verbose("Decrypted Reverse: 1999 file successfully !!");
var stream = new MemoryStream();
MemoryStream stream = new();
stream.Write(signatureBytes);
stream.Write(remaining);
stream.Position = 0;

View File

@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -46,59 +47,65 @@ namespace AssetStudio
private void ReadBlocksInfoAndDirectory(FileReader reader)
{
int offset = 0x20;
var blocksInfo = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
DescrambleHeader(blocksInfo);
Logger.Verbose($"Descrambled blocksInfo signature {Convert.ToHexString(blocksInfo, 0 , 4)}");
using var blocksInfoStream = new MemoryStream(blocksInfo, offset, (int)m_Header.compressedBlocksInfoSize - offset);
using var blocksInfoStream = new MemoryStream(blocksInfo, 0x20, (int)m_Header.compressedBlocksInfoSize - 0x20);
using var blocksInfoReader = new EndianBinaryReader(blocksInfoStream);
m_Header.uncompressedBlocksInfoSize = blocksInfoReader.ReadMhyUInt();
Logger.Verbose($"uncompressed blocksInfo size: 0x{m_Header.uncompressedBlocksInfoSize:X8}");
var compressedBlocksInfo = blocksInfoReader.ReadBytes((int)blocksInfoReader.Remaining);
var uncompressedBlocksInfo = BigArrayPool<byte>.Shared.Rent((int)m_Header.uncompressedBlocksInfoSize);
var uncompressedBlocksInfo = ArrayPool<byte>.Shared.Rent((int)m_Header.uncompressedBlocksInfoSize);
var uncompressedBlocksInfoSpan = uncompressedBlocksInfo.AsSpan(0, (int)m_Header.uncompressedBlocksInfoSize);
var numWrite = LZ4.Decompress(compressedBlocksInfo, uncompressedBlocksInfoSpan);
if (numWrite != m_Header.uncompressedBlocksInfoSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {m_Header.uncompressedBlocksInfoSize} bytes");
}
Logger.Verbose($"Writing block and directory to blocks stream...");
using var blocksInfoUncompressedStream = new MemoryStream(uncompressedBlocksInfo);
using var blocksInfoUncompressedReader = new EndianBinaryReader(blocksInfoUncompressedStream);
var nodesCount = blocksInfoUncompressedReader.ReadMhyInt();
m_DirectoryInfo = new List<BundleFile.Node>();
Logger.Verbose($"Directory count: {nodesCount}");
for (int i = 0; i < nodesCount; i++)
try
{
m_DirectoryInfo.Add(new BundleFile.Node
var numWrite = LZ4.Decompress(compressedBlocksInfo, uncompressedBlocksInfoSpan);
if (numWrite != m_Header.uncompressedBlocksInfoSize)
{
path = blocksInfoUncompressedReader.ReadMhyString(),
flags = blocksInfoUncompressedReader.ReadBoolean() ? 4u : 0,
offset = blocksInfoUncompressedReader.ReadMhyInt(),
size = blocksInfoUncompressedReader.ReadMhyUInt()
});
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {m_Header.uncompressedBlocksInfoSize} bytes");
}
Logger.Verbose($"Directory {i} Info: {m_DirectoryInfo[i]}");
}
var blocksInfoCount = blocksInfoUncompressedReader.ReadMhyInt();
m_BlocksInfo = new List<BundleFile.StorageBlock>();
Logger.Verbose($"Blocks count: {blocksInfoCount}");
for (int i = 0; i < blocksInfoCount; i++)
{
m_BlocksInfo.Add(new BundleFile.StorageBlock
Logger.Verbose($"Writing block and directory to blocks stream...");
using var blocksInfoUncompressedStream = new MemoryStream(uncompressedBlocksInfo, 0, (int)m_Header.uncompressedBlocksInfoSize);
using var blocksInfoUncompressedReader = new EndianBinaryReader(blocksInfoUncompressedStream);
var nodesCount = blocksInfoUncompressedReader.ReadMhyInt();
m_DirectoryInfo = new List<BundleFile.Node>();
Logger.Verbose($"Directory count: {nodesCount}");
for (int i = 0; i < nodesCount; i++)
{
compressedSize = (uint)blocksInfoUncompressedReader.ReadMhyInt(),
uncompressedSize = blocksInfoUncompressedReader.ReadMhyUInt(),
flags = (StorageBlockFlags)0x43
});
m_DirectoryInfo.Add(new BundleFile.Node
{
path = blocksInfoUncompressedReader.ReadMhyString(),
flags = blocksInfoUncompressedReader.ReadBoolean() ? 4u : 0,
offset = blocksInfoUncompressedReader.ReadMhyInt(),
size = blocksInfoUncompressedReader.ReadMhyUInt()
});
Logger.Verbose($"Block {i} Info: {m_BlocksInfo[i]}");
Logger.Verbose($"Directory {i} Info: {m_DirectoryInfo[i]}");
}
var blocksInfoCount = blocksInfoUncompressedReader.ReadMhyInt();
m_BlocksInfo = new List<BundleFile.StorageBlock>();
Logger.Verbose($"Blocks count: {blocksInfoCount}");
for (int i = 0; i < blocksInfoCount; i++)
{
m_BlocksInfo.Add(new BundleFile.StorageBlock
{
compressedSize = (uint)blocksInfoUncompressedReader.ReadMhyInt(),
uncompressedSize = blocksInfoUncompressedReader.ReadMhyUInt(),
flags = (StorageBlockFlags)0x43
});
Logger.Verbose($"Block {i} Info: {m_BlocksInfo[i]}");
}
}
BigArrayPool<byte>.Shared.Return(uncompressedBlocksInfo);
finally
{
ArrayPool<byte>.Shared.Return(uncompressedBlocksInfo, true);
}
}
private Stream CreateBlocksStream(string path)
@@ -124,25 +131,31 @@ namespace AssetStudio
throw new Exception($"Wrong compressed length: {compressedSize}");
}
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
reader.Read(compressedBytes, 0, compressedSize);
var compressedBytes = ArrayPool<byte>.Shared.Rent(compressedSize);
var uncompressedBytes = ArrayPool<byte>.Shared.Rent(uncompressedSize);
var offset = 0xC;
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
DescrambleEntry(compressedBytesSpan);
Logger.Verbose($"Descrambled block signature {Convert.ToHexString(compressedBytes, 0, 4)}");
var numWrite = LZ4.Decompress(compressedBytesSpan[offset..], uncompressedBytesSpan);
if (numWrite != uncompressedSize)
try
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
reader.Read(compressedBytesSpan);
DescrambleEntry(compressedBytesSpan);
Logger.Verbose($"Descrambled block signature {Convert.ToHexString(compressedBytes, 0, 4)}");
var numWrite = LZ4.Decompress(compressedBytesSpan[0xC..], uncompressedBytesSpan);
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksStream.Write(uncompressedBytesSpan);
}
finally
{
ArrayPool<byte>.Shared.Return(compressedBytes, true);
ArrayPool<byte>.Shared.Return(uncompressedBytes, true);
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Text;
@@ -94,23 +95,29 @@ namespace AssetStudio
Logger.Verbose($"Prased signature: {signature}");
var signatureBytes = Encoding.UTF8.GetBytes(signature);
var buffer = BigArrayPool<byte>.Shared.Rent(BufferSize);
while (Remaining > 0)
var buffer = ArrayPool<byte>.Shared.Rent(BufferSize);
try
{
var index = 0;
var absOffset = AbsolutePosition;
var read = Read(buffer);
while (index < read)
while (Remaining > 0)
{
index = buffer.AsSpan(0, read).Search(signatureBytes, index);
if (index == -1) break;
var offset = absOffset + index;
Offset = offset;
yield return offset;
index++;
var index = 0;
var absOffset = AbsolutePosition;
var read = Read(buffer);
while (index < read)
{
index = buffer.AsSpan(0, read).Search(signatureBytes, index);
if (index == -1) break;
var offset = absOffset + index;
Offset = offset;
yield return offset;
index++;
}
}
}
BigArrayPool<byte>.Shared.Return(buffer);
finally
{
ArrayPool<byte>.Shared.Return(buffer, true);
}
}
}
}