diff --git a/AssetStudio.Utility/Texture2DConverter.cs b/AssetStudio.Utility/Texture2DConverter.cs index 0e38719..91c6594 100644 --- a/AssetStudio.Utility/Texture2DConverter.cs +++ b/AssetStudio.Utility/Texture2DConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Runtime.CompilerServices; using Texture2DDecoder; @@ -32,185 +33,192 @@ namespace AssetStudio return false; } var flag = false; - var buff = BigArrayPool.Shared.Rent(reader.Size); - reader.GetData(buff); - switch (m_TextureFormat) + var buff = ArrayPool.Shared.Rent(reader.Size); + try { - case TextureFormat.Alpha8: //test pass - flag = DecodeAlpha8(buff, bytes); - break; - case TextureFormat.ARGB4444: //test pass - SwapBytesForXbox(buff); - flag = DecodeARGB4444(buff, bytes); - break; - case TextureFormat.RGB24: //test pass - flag = DecodeRGB24(buff, bytes); - break; - case TextureFormat.RGBA32: //test pass - flag = DecodeRGBA32(buff, bytes); - break; - case TextureFormat.ARGB32: //test pass - flag = DecodeARGB32(buff, bytes); - break; - case TextureFormat.RGB565: //test pass - SwapBytesForXbox(buff); - flag = DecodeRGB565(buff, bytes); - break; - case TextureFormat.R16: //test pass - case TextureFormat.R16_Alt: //test pass - flag = DecodeR16(buff, bytes); - break; - case TextureFormat.DXT1: //test pass - SwapBytesForXbox(buff); - flag = DecodeDXT1(buff, bytes); - break; - case TextureFormat.DXT3: - break; - case TextureFormat.DXT5: //test pass - SwapBytesForXbox(buff); - flag = DecodeDXT5(buff, bytes); - break; - case TextureFormat.RGBA4444: //test pass - flag = DecodeRGBA4444(buff, bytes); - break; - case TextureFormat.BGRA32: //test pass - flag = DecodeBGRA32(buff, bytes); - break; - case TextureFormat.RHalf: - flag = DecodeRHalf(buff, bytes); - break; - case TextureFormat.RGHalf: - flag = DecodeRGHalf(buff, bytes); - break; - case TextureFormat.RGBAHalf: //test pass - flag = DecodeRGBAHalf(buff, bytes); - break; - case TextureFormat.RFloat: - flag = DecodeRFloat(buff, bytes); - break; - case TextureFormat.RGFloat: - flag = DecodeRGFloat(buff, bytes); - break; - case TextureFormat.RGBAFloat: - flag = DecodeRGBAFloat(buff, bytes); - break; - case TextureFormat.YUY2: //test pass - flag = DecodeYUY2(buff, bytes); - break; - case TextureFormat.RGB9e5Float: //test pass - flag = DecodeRGB9e5Float(buff, bytes); - break; - case TextureFormat.BC6H: //test pass - flag = DecodeBC6H(buff, bytes); - break; - case TextureFormat.BC7: //test pass - flag = DecodeBC7(buff, bytes); - break; - case TextureFormat.BC4: //test pass - flag = DecodeBC4(buff, bytes); - break; - case TextureFormat.BC5: //test pass - flag = DecodeBC5(buff, bytes); - break; - case TextureFormat.DXT1Crunched: //test pass - flag = DecodeDXT1Crunched(buff, bytes); - break; - case TextureFormat.DXT5Crunched: //test pass - flag = DecodeDXT5Crunched(buff, bytes); - break; - case TextureFormat.PVRTC_RGB2: //test pass - case TextureFormat.PVRTC_RGBA2: //test pass - flag = DecodePVRTC(buff, bytes, true); - break; - case TextureFormat.PVRTC_RGB4: //test pass - case TextureFormat.PVRTC_RGBA4: //test pass - flag = DecodePVRTC(buff, bytes, false); - break; - case TextureFormat.ETC_RGB4: //test pass - case TextureFormat.ETC_RGB4_3DS: - flag = DecodeETC1(buff, bytes); - break; - case TextureFormat.ATC_RGB4: //test pass - flag = DecodeATCRGB4(buff, bytes); - break; - case TextureFormat.ATC_RGBA8: //test pass - flag = DecodeATCRGBA8(buff, bytes); - break; - case TextureFormat.EAC_R: //test pass - flag = DecodeEACR(buff, bytes); - break; - case TextureFormat.EAC_R_SIGNED: - flag = DecodeEACRSigned(buff, bytes); - break; - case TextureFormat.EAC_RG: //test pass - flag = DecodeEACRG(buff, bytes); - break; - case TextureFormat.EAC_RG_SIGNED: - flag = DecodeEACRGSigned(buff, bytes); - break; - case TextureFormat.ETC2_RGB: //test pass - flag = DecodeETC2(buff, bytes); - break; - case TextureFormat.ETC2_RGBA1: //test pass - flag = DecodeETC2A1(buff, bytes); - break; - case TextureFormat.ETC2_RGBA8: //test pass - case TextureFormat.ETC_RGBA8_3DS: - flag = DecodeETC2A8(buff, bytes); - break; - case TextureFormat.ASTC_RGB_4x4: //test pass - case TextureFormat.ASTC_RGBA_4x4: //test pass - case TextureFormat.ASTC_HDR_4x4: //test pass - flag = DecodeASTC(buff, bytes, 4); - break; - case TextureFormat.ASTC_RGB_5x5: //test pass - case TextureFormat.ASTC_RGBA_5x5: //test pass - case TextureFormat.ASTC_HDR_5x5: //test pass - flag = DecodeASTC(buff, bytes, 5); - break; - case TextureFormat.ASTC_RGB_6x6: //test pass - case TextureFormat.ASTC_RGBA_6x6: //test pass - case TextureFormat.ASTC_HDR_6x6: //test pass - flag = DecodeASTC(buff, bytes, 6); - break; - case TextureFormat.ASTC_RGB_8x8: //test pass - case TextureFormat.ASTC_RGBA_8x8: //test pass - case TextureFormat.ASTC_HDR_8x8: //test pass - flag = DecodeASTC(buff, bytes, 8); - break; - case TextureFormat.ASTC_RGB_10x10: //test pass - case TextureFormat.ASTC_RGBA_10x10: //test pass - case TextureFormat.ASTC_HDR_10x10: //test pass - flag = DecodeASTC(buff, bytes, 10); - break; - case TextureFormat.ASTC_RGB_12x12: //test pass - case TextureFormat.ASTC_RGBA_12x12: //test pass - case TextureFormat.ASTC_HDR_12x12: //test pass - flag = DecodeASTC(buff, bytes, 12); - break; - case TextureFormat.RG16: //test pass - flag = DecodeRG16(buff, bytes); - break; - case TextureFormat.R8: //test pass - flag = DecodeR8(buff, bytes); - break; - case TextureFormat.ETC_RGB4Crunched: //test pass - flag = DecodeETC1Crunched(buff, bytes); - break; - case TextureFormat.ETC2_RGBA8Crunched: //test pass - flag = DecodeETC2A8Crunched(buff, bytes); - break; - case TextureFormat.RG32: //test pass - flag = DecodeRG32(buff, bytes); - break; - case TextureFormat.RGB48: //test pass - flag = DecodeRGB48(buff, bytes); - break; - case TextureFormat.RGBA64: //test pass - flag = DecodeRGBA64(buff, bytes); - break; + reader.GetData(buff); + switch (m_TextureFormat) + { + case TextureFormat.Alpha8: //test pass + flag = DecodeAlpha8(buff, bytes); + break; + case TextureFormat.ARGB4444: //test pass + SwapBytesForXbox(buff); + flag = DecodeARGB4444(buff, bytes); + break; + case TextureFormat.RGB24: //test pass + flag = DecodeRGB24(buff, bytes); + break; + case TextureFormat.RGBA32: //test pass + flag = DecodeRGBA32(buff, bytes); + break; + case TextureFormat.ARGB32: //test pass + flag = DecodeARGB32(buff, bytes); + break; + case TextureFormat.RGB565: //test pass + SwapBytesForXbox(buff); + flag = DecodeRGB565(buff, bytes); + break; + case TextureFormat.R16: //test pass + case TextureFormat.R16_Alt: //test pass + flag = DecodeR16(buff, bytes); + break; + case TextureFormat.DXT1: //test pass + SwapBytesForXbox(buff); + flag = DecodeDXT1(buff, bytes); + break; + case TextureFormat.DXT3: + break; + case TextureFormat.DXT5: //test pass + SwapBytesForXbox(buff); + flag = DecodeDXT5(buff, bytes); + break; + case TextureFormat.RGBA4444: //test pass + flag = DecodeRGBA4444(buff, bytes); + break; + case TextureFormat.BGRA32: //test pass + flag = DecodeBGRA32(buff, bytes); + break; + case TextureFormat.RHalf: + flag = DecodeRHalf(buff, bytes); + break; + case TextureFormat.RGHalf: + flag = DecodeRGHalf(buff, bytes); + break; + case TextureFormat.RGBAHalf: //test pass + flag = DecodeRGBAHalf(buff, bytes); + break; + case TextureFormat.RFloat: + flag = DecodeRFloat(buff, bytes); + break; + case TextureFormat.RGFloat: + flag = DecodeRGFloat(buff, bytes); + break; + case TextureFormat.RGBAFloat: + flag = DecodeRGBAFloat(buff, bytes); + break; + case TextureFormat.YUY2: //test pass + flag = DecodeYUY2(buff, bytes); + break; + case TextureFormat.RGB9e5Float: //test pass + flag = DecodeRGB9e5Float(buff, bytes); + break; + case TextureFormat.BC6H: //test pass + flag = DecodeBC6H(buff, bytes); + break; + case TextureFormat.BC7: //test pass + flag = DecodeBC7(buff, bytes); + break; + case TextureFormat.BC4: //test pass + flag = DecodeBC4(buff, bytes); + break; + case TextureFormat.BC5: //test pass + flag = DecodeBC5(buff, bytes); + break; + case TextureFormat.DXT1Crunched: //test pass + flag = DecodeDXT1Crunched(buff, bytes); + break; + case TextureFormat.DXT5Crunched: //test pass + flag = DecodeDXT5Crunched(buff, bytes); + break; + case TextureFormat.PVRTC_RGB2: //test pass + case TextureFormat.PVRTC_RGBA2: //test pass + flag = DecodePVRTC(buff, bytes, true); + break; + case TextureFormat.PVRTC_RGB4: //test pass + case TextureFormat.PVRTC_RGBA4: //test pass + flag = DecodePVRTC(buff, bytes, false); + break; + case TextureFormat.ETC_RGB4: //test pass + case TextureFormat.ETC_RGB4_3DS: + flag = DecodeETC1(buff, bytes); + break; + case TextureFormat.ATC_RGB4: //test pass + flag = DecodeATCRGB4(buff, bytes); + break; + case TextureFormat.ATC_RGBA8: //test pass + flag = DecodeATCRGBA8(buff, bytes); + break; + case TextureFormat.EAC_R: //test pass + flag = DecodeEACR(buff, bytes); + break; + case TextureFormat.EAC_R_SIGNED: + flag = DecodeEACRSigned(buff, bytes); + break; + case TextureFormat.EAC_RG: //test pass + flag = DecodeEACRG(buff, bytes); + break; + case TextureFormat.EAC_RG_SIGNED: + flag = DecodeEACRGSigned(buff, bytes); + break; + case TextureFormat.ETC2_RGB: //test pass + flag = DecodeETC2(buff, bytes); + break; + case TextureFormat.ETC2_RGBA1: //test pass + flag = DecodeETC2A1(buff, bytes); + break; + case TextureFormat.ETC2_RGBA8: //test pass + case TextureFormat.ETC_RGBA8_3DS: + flag = DecodeETC2A8(buff, bytes); + break; + case TextureFormat.ASTC_RGB_4x4: //test pass + case TextureFormat.ASTC_RGBA_4x4: //test pass + case TextureFormat.ASTC_HDR_4x4: //test pass + flag = DecodeASTC(buff, bytes, 4); + break; + case TextureFormat.ASTC_RGB_5x5: //test pass + case TextureFormat.ASTC_RGBA_5x5: //test pass + case TextureFormat.ASTC_HDR_5x5: //test pass + flag = DecodeASTC(buff, bytes, 5); + break; + case TextureFormat.ASTC_RGB_6x6: //test pass + case TextureFormat.ASTC_RGBA_6x6: //test pass + case TextureFormat.ASTC_HDR_6x6: //test pass + flag = DecodeASTC(buff, bytes, 6); + break; + case TextureFormat.ASTC_RGB_8x8: //test pass + case TextureFormat.ASTC_RGBA_8x8: //test pass + case TextureFormat.ASTC_HDR_8x8: //test pass + flag = DecodeASTC(buff, bytes, 8); + break; + case TextureFormat.ASTC_RGB_10x10: //test pass + case TextureFormat.ASTC_RGBA_10x10: //test pass + case TextureFormat.ASTC_HDR_10x10: //test pass + flag = DecodeASTC(buff, bytes, 10); + break; + case TextureFormat.ASTC_RGB_12x12: //test pass + case TextureFormat.ASTC_RGBA_12x12: //test pass + case TextureFormat.ASTC_HDR_12x12: //test pass + flag = DecodeASTC(buff, bytes, 12); + break; + case TextureFormat.RG16: //test pass + flag = DecodeRG16(buff, bytes); + break; + case TextureFormat.R8: //test pass + flag = DecodeR8(buff, bytes); + break; + case TextureFormat.ETC_RGB4Crunched: //test pass + flag = DecodeETC1Crunched(buff, bytes); + break; + case TextureFormat.ETC2_RGBA8Crunched: //test pass + flag = DecodeETC2A8Crunched(buff, bytes); + break; + case TextureFormat.RG32: //test pass + flag = DecodeRG32(buff, bytes); + break; + case TextureFormat.RGB48: //test pass + flag = DecodeRGB48(buff, bytes); + break; + case TextureFormat.RGBA64: //test pass + flag = DecodeRGBA64(buff, bytes); + break; + } } - BigArrayPool.Shared.Return(buff); + finally + { + ArrayPool.Shared.Return(buff, true); + } + return flag; } diff --git a/AssetStudio.Utility/Texture2DExtensions.cs b/AssetStudio.Utility/Texture2DExtensions.cs index 847aaf6..523ccb2 100644 --- a/AssetStudio.Utility/Texture2DExtensions.cs +++ b/AssetStudio.Utility/Texture2DExtensions.cs @@ -1,6 +1,7 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using System.Buffers; using System.IO; namespace AssetStudio @@ -17,7 +18,7 @@ namespace AssetStudio public static Image ConvertToImage(this Texture2D m_Texture2D, bool flip) { var converter = new Texture2DConverter(m_Texture2D); - var buff = BigArrayPool.Shared.Rent(m_Texture2D.m_Width * m_Texture2D.m_Height * 4); + var buff = ArrayPool.Shared.Rent(m_Texture2D.m_Width * m_Texture2D.m_Height * 4); try { if (converter.DecodeTexture2D(buff)) @@ -33,7 +34,7 @@ namespace AssetStudio } finally { - BigArrayPool.Shared.Return(buff); + ArrayPool.Shared.Return(buff, true); } } diff --git a/AssetStudio/BigArrayPool.cs b/AssetStudio/BigArrayPool.cs deleted file mode 100644 index 369e689..0000000 --- a/AssetStudio/BigArrayPool.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Buffers; - -namespace AssetStudio -{ - public static class BigArrayPool - { - private static readonly ArrayPool s_shared = ArrayPool.Create(64 * 1024 * 1024, 3); - public static ArrayPool Shared => s_shared; - } -} diff --git a/AssetStudio/BlbFile.cs b/AssetStudio/BlbFile.cs index 53f01d1..cb39e26 100644 --- a/AssetStudio/BlbFile.cs +++ b/AssetStudio/BlbFile.cs @@ -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.Shared.Rent(compressedSize); - var uncompressedBytes = BigArrayPool.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.Shared.Rent(compressedSize); + var uncompressedBytes = ArrayPool.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.Shared.Return(compressedBytes); - BigArrayPool.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.Shared.Return(compressedBytes, true); + ArrayPool.Shared.Return(uncompressedBytes, true); + } } } diff --git a/AssetStudio/BundleFile.cs b/AssetStudio/BundleFile.cs index 34d8904..5b205b1 100644 --- a/AssetStudio/BundleFile.cs +++ b/AssetStudio/BundleFile.cs @@ -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.Shared.Rent((int)uncompressedSize); - var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, (int)uncompressedSize); - var numWrite = LZ4.Decompress(blocksInfoBytesSpan, uncompressedBytesSpan); - if (numWrite != uncompressedSize) + var uncompressedBytes = ArrayPool.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.Shared.Return(uncompressedBytes, true); } - blocksInfoUncompresseddStream = new MemoryStream(uncompressedBytesSpan.ToArray()); - BigArrayPool.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.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.Shared.Rent(uncompressedSize); - var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize); - var numWrite = LZ4.Decompress(compressedBytesSpan, uncompressedBytesSpan); - if (numWrite != uncompressedSize) + + var compressedBytes = ArrayPool.Shared.Rent(compressedSize); + var uncompressedBytes = ArrayPool.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.Shared.Return(compressedBytes, true); + ArrayPool.Shared.Return(uncompressedBytes, true); } - blocksStream.Write(uncompressedBytes, 0, uncompressedSize); - BigArrayPool.Shared.Return(compressedBytes); - BigArrayPool.Shared.Return(uncompressedBytes); break; } case CompressionType.Lz4Inv when Game.Type.IsArknightsEndfield(): { var compressedSize = (int)blockInfo.compressedSize; - var compressedBytes = BigArrayPool.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.Shared.Rent(uncompressedSize); + + var compressedBytes = ArrayPool.Shared.Rent(compressedSize); + var uncompressedBytes = ArrayPool.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.Shared.Return(compressedBytes, true); + ArrayPool.Shared.Return(uncompressedBytes, true); } - blocksStream.Write(uncompressedBytes, 0, uncompressedSize); - BigArrayPool.Shared.Return(compressedBytes); - BigArrayPool.Shared.Return(uncompressedBytes); break; } case CompressionType.Zstd when !Game.Type.IsMhyGroup(): //Zstd { var compressedSize = (int)blockInfo.compressedSize; - var compressedBytes = BigArrayPool.Shared.Rent(compressedSize); - reader.Read(compressedBytes, 0, compressedSize); - var uncompressedSize = (int)blockInfo.uncompressedSize; - var uncompressedBytes = BigArrayPool.Shared.Rent(uncompressedSize); + + var compressedBytes = ArrayPool.Shared.Rent(compressedSize); + var uncompressedBytes = ArrayPool.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.Shared.Return(compressedBytes); - BigArrayPool.Shared.Return(uncompressedBytes); + finally + { + ArrayPool.Shared.Return(compressedBytes, true); + ArrayPool.Shared.Return(uncompressedBytes, true); + } break; } default: diff --git a/AssetStudio/Classes/AnimationClip.cs b/AssetStudio/Classes/AnimationClip.cs index 7159afb..34894a1 100644 --- a/AssetStudio/Classes/AnimationClip.cs +++ b/AssetStudio/Classes/AnimationClip.cs @@ -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()); diff --git a/AssetStudio/ImportHelper.cs b/AssetStudio/ImportHelper.cs index 18b5245..1ebee94 100644 --- a/AssetStudio/ImportHelper.cs +++ b/AssetStudio/ImportHelper.cs @@ -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; diff --git a/AssetStudio/MhyFile.cs b/AssetStudio/MhyFile.cs index e885dc0..91e9899 100644 --- a/AssetStudio/MhyFile.cs +++ b/AssetStudio/MhyFile.cs @@ -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.Shared.Rent((int)m_Header.uncompressedBlocksInfoSize); + + var uncompressedBlocksInfo = ArrayPool.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(); - 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(); - 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(); + 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(); + 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.Shared.Return(uncompressedBlocksInfo); + finally + { + ArrayPool.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.Shared.Rent(compressedSize); - var uncompressedBytes = BigArrayPool.Shared.Rent(uncompressedSize); - reader.Read(compressedBytes, 0, compressedSize); + var compressedBytes = ArrayPool.Shared.Rent(compressedSize); + var uncompressedBytes = ArrayPool.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.Shared.Return(compressedBytes); - BigArrayPool.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.Shared.Return(compressedBytes, true); + ArrayPool.Shared.Return(uncompressedBytes, true); + } } } diff --git a/AssetStudio/OffsetStream.cs b/AssetStudio/OffsetStream.cs index c4e77f6..44c8ee1 100644 --- a/AssetStudio/OffsetStream.cs +++ b/AssetStudio/OffsetStream.cs @@ -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.Shared.Rent(BufferSize); - while (Remaining > 0) + var buffer = ArrayPool.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.Shared.Return(buffer); + finally + { + ArrayPool.Shared.Return(buffer, true); + } } } }