diff --git a/AssetStudio/AssetStudio.csproj b/AssetStudio/AssetStudio.csproj index e540d1f..04a419c 100644 --- a/AssetStudio/AssetStudio.csproj +++ b/AssetStudio/AssetStudio.csproj @@ -8,4 +8,13 @@ Copyright © Perfare 2018-2021 + + + + + + + + + diff --git a/AssetStudio/BigArrayPool.cs b/AssetStudio/BigArrayPool.cs new file mode 100644 index 0000000..369e689 --- /dev/null +++ b/AssetStudio/BigArrayPool.cs @@ -0,0 +1,10 @@ +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/BundleFile.cs b/AssetStudio/BundleFile.cs index 1e6ac8f..3352ea7 100644 --- a/AssetStudio/BundleFile.cs +++ b/AssetStudio/BundleFile.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using K4os.Compression.LZ4; using System.IO; using System.Linq; -using Lz4; namespace AssetStudio { @@ -221,30 +219,33 @@ namespace AssetStudio { blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize); } - var blocksInfoCompressedStream = new MemoryStream(blocksInfoBytes); MemoryStream blocksInfoUncompresseddStream; + var uncompressedSize = m_Header.uncompressedBlocksInfoSize; switch (m_Header.flags & 0x3F) //kArchiveCompressionTypeMask { default: //None { - blocksInfoUncompresseddStream = blocksInfoCompressedStream; + blocksInfoUncompresseddStream = new MemoryStream(blocksInfoBytes); break; } case 1: //LZMA { - blocksInfoUncompresseddStream = new MemoryStream((int)(m_Header.uncompressedBlocksInfoSize)); - SevenZipHelper.StreamDecompress(blocksInfoCompressedStream, blocksInfoUncompresseddStream, m_Header.compressedBlocksInfoSize, m_Header.uncompressedBlocksInfoSize); + blocksInfoUncompresseddStream = new MemoryStream((int)(uncompressedSize)); + using (var blocksInfoCompressedStream = new MemoryStream(blocksInfoBytes)) + { + SevenZipHelper.StreamDecompress(blocksInfoCompressedStream, blocksInfoUncompresseddStream, m_Header.compressedBlocksInfoSize, m_Header.uncompressedBlocksInfoSize); + } blocksInfoUncompresseddStream.Position = 0; - blocksInfoCompressedStream.Close(); break; } case 2: //LZ4 case 3: //LZ4HC { - var uncompressedBytes = new byte[m_Header.uncompressedBlocksInfoSize]; - using (var decoder = new Lz4DecoderStream(blocksInfoCompressedStream)) + var uncompressedBytes = new byte[uncompressedSize]; + var numWrite = LZ4Codec.Decode(blocksInfoBytes, uncompressedBytes); + if (numWrite != uncompressedSize) { - decoder.Read(uncompressedBytes, 0, uncompressedBytes.Length); + throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); } blocksInfoUncompresseddStream = new MemoryStream(uncompressedBytes); break; @@ -299,11 +300,19 @@ namespace AssetStudio case 2: //LZ4 case 3: //LZ4HC { - var compressedStream = new MemoryStream(reader.ReadBytes((int)blockInfo.compressedSize)); - using (var lz4Stream = new Lz4DecoderStream(compressedStream)) + 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 numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize); + if (numWrite != uncompressedSize) { - lz4Stream.CopyTo(blocksStream, blockInfo.uncompressedSize); + throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); } + blocksStream.Write(uncompressedBytes, 0, uncompressedSize); + BigArrayPool.Shared.Return(compressedBytes); + BigArrayPool.Shared.Return(uncompressedBytes); break; } } diff --git a/AssetStudio/Lz4DecoderStream.cs b/AssetStudio/Lz4DecoderStream.cs deleted file mode 100644 index e767bb7..0000000 --- a/AssetStudio/Lz4DecoderStream.cs +++ /dev/null @@ -1,540 +0,0 @@ -#define CHECK_ARGS -#define CHECK_EOF -//#define LOCAL_SHADOW - -using System; -using System.IO; - -namespace Lz4 -{ - public class Lz4DecoderStream : Stream - { - public Lz4DecoderStream(Stream input, long inputLength = long.MaxValue) - { - Reset(input, inputLength); - } - - private void Reset(Stream input, long inputLength = long.MaxValue) - { - this.inputLength = inputLength; - this.input = input; - - phase = DecodePhase.ReadToken; - - decodeBufferPos = 0; - - litLen = 0; - matLen = 0; - matDst = 0; - - inBufPos = DecBufLen; - inBufEnd = DecBufLen; - } - - protected override void Dispose(bool disposing) - { - try - { - if (disposing && input != null) - { - input.Close(); - } - input = null; - decodeBuffer = null; - } - finally - { - base.Dispose(disposing); - } - } - - private long inputLength; - private Stream input; - - //because we might not be able to match back across invocations, - //we have to keep the last window's worth of bytes around for reuse - //we use a circular buffer for this - every time we write into this - //buffer, we also write the same into our output buffer - - private const int DecBufLen = 0x10000; - private const int DecBufMask = 0xFFFF; - - private const int InBufLen = 128; - - private byte[] decodeBuffer = new byte[DecBufLen + InBufLen]; - private int decodeBufferPos, inBufPos, inBufEnd; - - //we keep track of which phase we're in so that we can jump right back - //into the correct part of decoding - - private DecodePhase phase; - - private enum DecodePhase - { - ReadToken, - ReadExLiteralLength, - CopyLiteral, - ReadOffset, - ReadExMatchLength, - CopyMatch, - } - - //state within interruptable phases and across phase boundaries is - //kept here - again, so that we can punt out and restart freely - - private int litLen, matLen, matDst; - - public override int Read(byte[] buffer, int offset, int count) - { -#if CHECK_ARGS - if (buffer == null) - throw new ArgumentNullException("buffer"); - if (offset < 0 || count < 0 || buffer.Length - count < offset) - throw new ArgumentOutOfRangeException(); - - if (input == null) - throw new InvalidOperationException(); -#endif - int nRead, nToRead = count; - - var decBuf = decodeBuffer; - - //the stringy gotos are obnoxious, but their purpose is to - //make it *blindingly* obvious how the state machine transitions - //back and forth as it reads - remember, we can yield out of - //this routine in several places, and we must be able to re-enter - //and pick up where we left off! - -#if LOCAL_SHADOW - var phase = this.phase; - var inBufPos = this.inBufPos; - var inBufEnd = this.inBufEnd; -#endif - switch (phase) - { - case DecodePhase.ReadToken: - goto readToken; - - case DecodePhase.ReadExLiteralLength: - goto readExLiteralLength; - - case DecodePhase.CopyLiteral: - goto copyLiteral; - - case DecodePhase.ReadOffset: - goto readOffset; - - case DecodePhase.ReadExMatchLength: - goto readExMatchLength; - - case DecodePhase.CopyMatch: - goto copyMatch; - } - - readToken: - int tok; - if (inBufPos < inBufEnd) - { - tok = decBuf[inBufPos++]; - } - else - { -#if LOCAL_SHADOW - this.inBufPos = inBufPos; -#endif - - tok = ReadByteCore(); -#if LOCAL_SHADOW - inBufPos = this.inBufPos; - inBufEnd = this.inBufEnd; -#endif -#if CHECK_EOF - if (tok == -1) - goto finish; -#endif - } - - litLen = tok >> 4; - matLen = (tok & 0xF) + 4; - - switch (litLen) - { - case 0: - phase = DecodePhase.ReadOffset; - goto readOffset; - - case 0xF: - phase = DecodePhase.ReadExLiteralLength; - goto readExLiteralLength; - - default: - phase = DecodePhase.CopyLiteral; - goto copyLiteral; - } - - readExLiteralLength: - int exLitLen; - if (inBufPos < inBufEnd) - { - exLitLen = decBuf[inBufPos++]; - } - else - { -#if LOCAL_SHADOW - this.inBufPos = inBufPos; -#endif - exLitLen = ReadByteCore(); -#if LOCAL_SHADOW - inBufPos = this.inBufPos; - inBufEnd = this.inBufEnd; -#endif - -#if CHECK_EOF - if (exLitLen == -1) - goto finish; -#endif - } - - litLen += exLitLen; - if (exLitLen == 255) - goto readExLiteralLength; - - phase = DecodePhase.CopyLiteral; - goto copyLiteral; - - copyLiteral: - int nReadLit = litLen < nToRead ? litLen : nToRead; - if (nReadLit != 0) - { - if (inBufPos + nReadLit <= inBufEnd) - { - int ofs = offset; - - for (int c = nReadLit; c-- != 0;) - buffer[ofs++] = decBuf[inBufPos++]; - - nRead = nReadLit; - } - else - { -#if LOCAL_SHADOW - this.inBufPos = inBufPos; -#endif - nRead = ReadCore(buffer, offset, nReadLit); -#if LOCAL_SHADOW - inBufPos = this.inBufPos; - inBufEnd = this.inBufEnd; -#endif -#if CHECK_EOF - if (nRead == 0) - goto finish; -#endif - } - - offset += nRead; - nToRead -= nRead; - - litLen -= nRead; - - if (litLen != 0) - goto copyLiteral; - } - - if (nToRead == 0) - goto finish; - - phase = DecodePhase.ReadOffset; - goto readOffset; - - readOffset: - if (inBufPos + 1 < inBufEnd) - { - matDst = (decBuf[inBufPos + 1] << 8) | decBuf[inBufPos]; - inBufPos += 2; - } - else - { -#if LOCAL_SHADOW - this.inBufPos = inBufPos; -#endif - matDst = ReadOffsetCore(); -#if LOCAL_SHADOW - inBufPos = this.inBufPos; - inBufEnd = this.inBufEnd; -#endif -#if CHECK_EOF - if (matDst == -1) - goto finish; -#endif - } - - if (matLen == 15 + 4) - { - phase = DecodePhase.ReadExMatchLength; - goto readExMatchLength; - } - else - { - phase = DecodePhase.CopyMatch; - goto copyMatch; - } - - readExMatchLength: - int exMatLen; - if (inBufPos < inBufEnd) - { - exMatLen = decBuf[inBufPos++]; - } - else - { -#if LOCAL_SHADOW - this.inBufPos = inBufPos; -#endif - exMatLen = ReadByteCore(); -#if LOCAL_SHADOW - inBufPos = this.inBufPos; - inBufEnd = this.inBufEnd; -#endif -#if CHECK_EOF - if (exMatLen == -1) - goto finish; -#endif - } - - matLen += exMatLen; - if (exMatLen == 255) - goto readExMatchLength; - - phase = DecodePhase.CopyMatch; - goto copyMatch; - - copyMatch: - int nCpyMat = matLen < nToRead ? matLen : nToRead; - if (nCpyMat != 0) - { - nRead = count - nToRead; - - int bufDst = matDst - nRead; - if (bufDst > 0) - { - //offset is fairly far back, we need to pull from the buffer - - int bufSrc = decodeBufferPos - bufDst; - if (bufSrc < 0) - bufSrc += DecBufLen; - int bufCnt = bufDst < nCpyMat ? bufDst : nCpyMat; - - for (int c = bufCnt; c-- != 0;) - buffer[offset++] = decBuf[bufSrc++ & DecBufMask]; - } - else - { - bufDst = 0; - } - - int sOfs = offset - matDst; - for (int i = bufDst; i < nCpyMat; i++) - buffer[offset++] = buffer[sOfs++]; - - nToRead -= nCpyMat; - matLen -= nCpyMat; - } - - if (nToRead == 0) - goto finish; - - phase = DecodePhase.ReadToken; - goto readToken; - - finish: - nRead = count - nToRead; - - int nToBuf = nRead < DecBufLen ? nRead : DecBufLen; - int repPos = offset - nToBuf; - - if (nToBuf == DecBufLen) - { - Buffer.BlockCopy(buffer, repPos, decBuf, 0, DecBufLen); - decodeBufferPos = 0; - } - else - { - int decPos = decodeBufferPos; - - while (nToBuf-- != 0) - decBuf[decPos++ & DecBufMask] = buffer[repPos++]; - - decodeBufferPos = decPos & DecBufMask; - } - -#if LOCAL_SHADOW - this.phase = phase; - this.inBufPos = inBufPos; -#endif - return nRead; - } - - private int ReadByteCore() - { - var buf = decodeBuffer; - - if (inBufPos == inBufEnd) - { - int nRead = input.Read(buf, DecBufLen, - InBufLen < inputLength ? InBufLen : (int)inputLength); - -#if CHECK_EOF - if (nRead == 0) - return -1; -#endif - - inputLength -= nRead; - - inBufPos = DecBufLen; - inBufEnd = DecBufLen + nRead; - } - - return buf[inBufPos++]; - } - - private int ReadOffsetCore() - { - var buf = decodeBuffer; - - if (inBufPos == inBufEnd) - { - int nRead = input.Read(buf, DecBufLen, - InBufLen < inputLength ? InBufLen : (int)inputLength); - -#if CHECK_EOF - if (nRead == 0) - return -1; -#endif - - inputLength -= nRead; - - inBufPos = DecBufLen; - inBufEnd = DecBufLen + nRead; - } - - if (inBufEnd - inBufPos == 1) - { - buf[DecBufLen] = buf[inBufPos]; - - int nRead = input.Read(buf, DecBufLen + 1, - InBufLen - 1 < inputLength ? InBufLen - 1 : (int)inputLength); - -#if CHECK_EOF - if (nRead == 0) - { - inBufPos = DecBufLen; - inBufEnd = DecBufLen + 1; - - return -1; - } -#endif - - inputLength -= nRead; - - inBufPos = DecBufLen; - inBufEnd = DecBufLen + nRead + 1; - } - - int ret = (buf[inBufPos + 1] << 8) | buf[inBufPos]; - inBufPos += 2; - - return ret; - } - - private int ReadCore(byte[] buffer, int offset, int count) - { - int nToRead = count; - - var buf = decodeBuffer; - int inBufLen = inBufEnd - inBufPos; - - int fromBuf = nToRead < inBufLen ? nToRead : inBufLen; - if (fromBuf != 0) - { - var bufPos = inBufPos; - - for (int c = fromBuf; c-- != 0;) - buffer[offset++] = buf[bufPos++]; - - inBufPos = bufPos; - nToRead -= fromBuf; - } - - if (nToRead != 0) - { - int nRead; - - if (nToRead >= InBufLen) - { - nRead = input.Read(buffer, offset, - nToRead < inputLength ? nToRead : (int)inputLength); - nToRead -= nRead; - } - else - { - nRead = input.Read(buf, DecBufLen, - InBufLen < inputLength ? InBufLen : (int)inputLength); - - inBufPos = DecBufLen; - inBufEnd = DecBufLen + nRead; - - fromBuf = nToRead < nRead ? nToRead : nRead; - - var bufPos = inBufPos; - - for (int c = fromBuf; c-- != 0;) - buffer[offset++] = buf[bufPos++]; - - inBufPos = bufPos; - nToRead -= fromBuf; - } - - inputLength -= nRead; - } - - return count - nToRead; - } - - #region Stream internals - - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override void Flush() - { - } - - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - - #endregion - } -} diff --git a/AssetStudio/ResourceReader.cs b/AssetStudio/ResourceReader.cs index fc294cd..a717111 100644 --- a/AssetStudio/ResourceReader.cs +++ b/AssetStudio/ResourceReader.cs @@ -11,6 +11,8 @@ namespace AssetStudio private long size; private BinaryReader reader; + public int Size { get => (int)size; } + public ResourceReader(string path, SerializedFile assetsFile, long offset, long size) { needSearch = true; @@ -69,6 +71,13 @@ namespace AssetStudio return binaryReader.ReadBytes((int)size); } + public void GetData(byte[] buff) + { + var binaryReader = GetReader(); + binaryReader.BaseStream.Position = offset; + binaryReader.Read(buff, 0, (int)size); + } + public void WriteData(string path) { var binaryReader = GetReader(); diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index ea2ea6c..a48fb8f 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; -using System.Drawing.Imaging; using System.Drawing.Text; using System.Globalization; using System.IO; @@ -19,8 +18,6 @@ using System.Timers; using System.Windows.Forms; using static AssetStudioGUI.Studio; using Font = AssetStudio.Font; -using ImageFormat = AssetStudio.ImageFormat; -using PixelFormat = System.Drawing.Imaging.PixelFormat; #if NET472 using Vector3 = OpenTK.Vector3; using Vector4 = OpenTK.Vector4; @@ -35,7 +32,7 @@ namespace AssetStudioGUI partial class AssetStudioGUIForm : Form { private AssetItem lastSelectedItem; - private Bitmap imageTexture; + private DirectBitmap imageTexture; private string tempClipboard; private FMOD.System system; @@ -396,7 +393,7 @@ namespace AssetStudioGUI { if (enablePreview.Checked && imageTexture != null) { - previewPanel.BackgroundImage = imageTexture; + previewPanel.BackgroundImage = imageTexture.Bitmap; } else { @@ -759,10 +756,11 @@ namespace AssetStudioGUI private void PreviewTexture2D(AssetItem assetItem, Texture2D m_Texture2D) { - var stream = m_Texture2D.ConvertToStream(ImageFormat.Png, true); - if (stream != null) + var image = m_Texture2D.ConvertToImage(true); + if (image != null) { - var bitmap = new Bitmap(stream); + var bitmap = new DirectBitmap(image.ConvertToBgra32Bytes(), m_Texture2D.m_Width, m_Texture2D.m_Height); + image.Dispose(); assetItem.InfoText = $"Width: {m_Texture2D.m_Width}\nHeight: {m_Texture2D.m_Height}\nFormat: {m_Texture2D.m_TextureFormat}"; switch (m_Texture2D.m_TextureSettings.m_FilterMode) { @@ -790,13 +788,11 @@ namespace AssetStudioGUI assetItem.InfoText += "None"; if (validChannel != 4) { - var bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); - var bytes = new byte[bitmap.Width * bitmap.Height * 4]; - Marshal.Copy(bmpData.Scan0, bytes, 0, bytes.Length); - for (int i = 0; i < bmpData.Height; i++) + var bytes = bitmap.Bits; + for (int i = 0; i < bitmap.Height; i++) { - int offset = Math.Abs(bmpData.Stride) * i; - for (int j = 0; j < bmpData.Width; j++) + int offset = Math.Abs(bitmap.Stride) * i; + for (int j = 0; j < bitmap.Width; j++) { bytes[offset] = textureChannels[0] ? bytes[offset] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; bytes[offset + 1] = textureChannels[1] ? bytes[offset + 1] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue; @@ -805,8 +801,6 @@ namespace AssetStudioGUI offset += 4; } } - Marshal.Copy(bytes, 0, bmpData.Scan0, bytes.Length); - bitmap.UnlockBits(bmpData); } PreviewTexture(bitmap); @@ -1170,10 +1164,11 @@ namespace AssetStudioGUI private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite) { - var stream = m_Sprite.GetImage(ImageFormat.Png); - if (stream != null) + var image = m_Sprite.GetImage(); + if (image != null) { - var bitmap = new Bitmap(stream); + var bitmap = new DirectBitmap(image.ConvertToBgra32Bytes(), image.Width, image.Height); + image.Dispose(); assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n"; PreviewTexture(bitmap); } @@ -1183,11 +1178,11 @@ namespace AssetStudioGUI } } - private void PreviewTexture(Bitmap bitmap) + private void PreviewTexture(DirectBitmap bitmap) { imageTexture?.Dispose(); imageTexture = bitmap; - previewPanel.BackgroundImage = imageTexture; + previewPanel.BackgroundImage = imageTexture.Bitmap; if (imageTexture.Width > previewPanel.Width || imageTexture.Height > previewPanel.Height) previewPanel.BackgroundImageLayout = ImageLayout.Zoom; else @@ -1238,6 +1233,7 @@ namespace AssetStudioGUI classesListView.Groups.Clear(); previewPanel.BackgroundImage = Properties.Resources.preview; imageTexture?.Dispose(); + imageTexture = null; previewPanel.BackgroundImageLayout = ImageLayout.Center; assetInfoLabel.Visible = false; assetInfoLabel.Text = null; diff --git a/AssetStudioGUI/DirectBitmap.cs b/AssetStudioGUI/DirectBitmap.cs new file mode 100644 index 0000000..101d955 --- /dev/null +++ b/AssetStudioGUI/DirectBitmap.cs @@ -0,0 +1,43 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; + +namespace AssetStudioGUI +{ + public sealed class DirectBitmap : IDisposable + { + public DirectBitmap(byte[] buff, int width, int height) + { + Width = width; + Height = height; + Bits = buff; + m_handle = GCHandle.Alloc(Bits, GCHandleType.Pinned); + m_bitmap = new Bitmap(Width, Height, Stride, PixelFormat.Format32bppArgb, m_handle.AddrOfPinnedObject()); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + m_bitmap.Dispose(); + m_handle.Free(); + } + m_bitmap = null; + } + + public void Dispose() + { + Dispose(true); + } + + public int Height { get; } + public int Width { get; } + public int Stride => Width * 4; + public byte[] Bits { get; } + public Bitmap Bitmap => m_bitmap; + + private Bitmap m_bitmap; + private readonly GCHandle m_handle; + } +} diff --git a/AssetStudioGUI/Exporter.cs b/AssetStudioGUI/Exporter.cs index c028877..6bc77ad 100644 --- a/AssetStudioGUI/Exporter.cs +++ b/AssetStudioGUI/Exporter.cs @@ -17,12 +17,15 @@ namespace AssetStudioGUI var type = Properties.Settings.Default.convertType; if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath)) return false; - var stream = m_Texture2D.ConvertToStream(type, true); - if (stream == null) + var image = m_Texture2D.ConvertToImage(true); + if (image == null) return false; - using (stream) + using (image) { - File.WriteAllBytes(exportFullPath, stream.ToArray()); + using (var file = File.OpenWrite(exportFullPath)) + { + image.WriteToStream(file, type); + } return true; } } @@ -229,12 +232,15 @@ namespace AssetStudioGUI var type = Properties.Settings.Default.convertType; if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath)) return false; - var stream = ((Sprite)item.Asset).GetImage(type); - if (stream != null) + var image = ((Sprite)item.Asset).GetImage(); + if (image != null) { - using (stream) + using (image) { - File.WriteAllBytes(exportFullPath, stream.ToArray()); + using (var file = File.OpenWrite(exportFullPath)) + { + image.WriteToStream(file, type); + } return true; } } diff --git a/AssetStudioUtility/ImageExtensions.cs b/AssetStudioUtility/ImageExtensions.cs index 7306c45..faafa10 100644 --- a/AssetStudioUtility/ImageExtensions.cs +++ b/AssetStudioUtility/ImageExtensions.cs @@ -1,39 +1,55 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.PixelFormats; using System.IO; +using System.Runtime.InteropServices; namespace AssetStudio { public static class ImageExtensions { - public static MemoryStream ConvertToStream(this Image image, ImageFormat imageFormat) + public static void WriteToStream(this Image image, Stream stream, ImageFormat imageFormat) { - var outputStream = new MemoryStream(); switch (imageFormat) { case ImageFormat.Jpeg: - image.SaveAsJpeg(outputStream); + image.SaveAsJpeg(stream); break; case ImageFormat.Png: - image.SaveAsPng(outputStream); + image.SaveAsPng(stream); break; case ImageFormat.Bmp: - image.Save(outputStream, new BmpEncoder + image.Save(stream, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel32, SupportTransparency = true }); break; case ImageFormat.Tga: - image.Save(outputStream, new TgaEncoder + image.Save(stream, new TgaEncoder { BitsPerPixel = TgaBitsPerPixel.Pixel32, Compression = TgaCompression.None }); break; } - return outputStream; + } + + public static MemoryStream ConvertToStream(this Image image, ImageFormat imageFormat) + { + var stream = new MemoryStream(); + image.WriteToStream(stream, imageFormat); + return stream; + } + + public static byte[] ConvertToBgra32Bytes(this Image image) + { + if (image.TryGetSinglePixelSpan(out var pixelSpan)) + { + return MemoryMarshal.AsBytes(pixelSpan).ToArray(); + } + return null; } } } diff --git a/AssetStudioUtility/ShaderConverter.cs b/AssetStudioUtility/ShaderConverter.cs index 8aa0474..e2385bc 100644 --- a/AssetStudioUtility/ShaderConverter.cs +++ b/AssetStudioUtility/ShaderConverter.cs @@ -1,10 +1,10 @@ -using System; +using K4os.Compression.LZ4; +using System; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; -using Lz4; namespace AssetStudio { @@ -15,10 +15,7 @@ namespace AssetStudio if (shader.m_SubProgramBlob != null) //5.3 - 5.4 { var decompressedBytes = new byte[shader.decompressedSize]; - using (var decoder = new Lz4DecoderStream(new MemoryStream(shader.m_SubProgramBlob))) - { - decoder.Read(decompressedBytes, 0, (int)shader.decompressedSize); - } + LZ4Codec.Decode(shader.m_SubProgramBlob, decompressedBytes); using (var blobReader = new BinaryReader(new MemoryStream(decompressedBytes))) { var program = new ShaderProgram(blobReader, shader.version); @@ -42,10 +39,7 @@ namespace AssetStudio var compressedBytes = new byte[shader.compressedLengths[i]]; Buffer.BlockCopy(shader.compressedBlob, (int)shader.offsets[i], compressedBytes, 0, (int)shader.compressedLengths[i]); var decompressedBytes = new byte[shader.decompressedLengths[i]]; - using (var decoder = new Lz4DecoderStream(new MemoryStream(compressedBytes))) - { - decoder.Read(decompressedBytes, 0, (int)shader.decompressedLengths[i]); - } + LZ4Codec.Decode(compressedBytes, decompressedBytes); using (var blobReader = new BinaryReader(new MemoryStream(decompressedBytes))) { shaderPrograms[i] = new ShaderProgram(blobReader, shader.version); diff --git a/AssetStudioUtility/SpriteHelper.cs b/AssetStudioUtility/SpriteHelper.cs index 9c748d6..ea17f50 100644 --- a/AssetStudioUtility/SpriteHelper.cs +++ b/AssetStudioUtility/SpriteHelper.cs @@ -13,20 +13,7 @@ namespace AssetStudio { public static class SpriteHelper { - public static MemoryStream GetImage(this Sprite m_Sprite, ImageFormat imageFormat) - { - var image = GetImage(m_Sprite); - if (image != null) - { - using (image) - { - return image.ConvertToStream(imageFormat); - } - } - return null; - } - - public static Image GetImage(this Sprite m_Sprite) + public static Image GetImage(this Sprite m_Sprite) { if (m_Sprite.m_SpriteAtlas != null && m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlas)) { @@ -45,7 +32,7 @@ namespace AssetStudio return null; } - private static Image CutImage(Texture2D m_Texture2D, Sprite m_Sprite, Rectf textureRect, Vector2 textureRectOffset, SpriteSettings settingsRaw) + private static Image CutImage(Texture2D m_Texture2D, Sprite m_Sprite, Rectf textureRect, Vector2 textureRectOffset, SpriteSettings settingsRaw) { var originalImage = m_Texture2D.ConvertToImage(false); if (originalImage != null) @@ -100,7 +87,7 @@ namespace AssetStudio { GraphicsOptions = graphicsOptions }; - using (var mask = new Image(rect.Width, rect.Height, SixLabors.ImageSharp.Color.Black)) + using (var mask = new Image(rect.Width, rect.Height, SixLabors.ImageSharp.Color.Black)) { mask.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, path)); var bursh = new ImageBrush(mask); diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs index a9610ba..dfaf14d 100644 --- a/AssetStudioUtility/Texture2DConverter.cs +++ b/AssetStudioUtility/Texture2DConverter.cs @@ -1,23 +1,20 @@ using System; -using System.Linq; using Texture2DDecoder; namespace AssetStudio { public class Texture2DConverter { + private ResourceReader reader; private int m_Width; private int m_Height; private TextureFormat m_TextureFormat; - private int image_data_size; - private byte[] image_data; private int[] version; private BuildTarget platform; public Texture2DConverter(Texture2D m_Texture2D) { - image_data = m_Texture2D.image_data.GetData(); - image_data_size = image_data.Length; + reader = m_Texture2D.image_data; m_Width = m_Texture2D.m_Width; m_Height = m_Texture2D.m_Height; m_TextureFormat = m_Texture2D.m_TextureFormat; @@ -25,193 +22,188 @@ namespace AssetStudio platform = m_Texture2D.platform; } - public byte[] DecodeTexture2D() + public bool DecodeTexture2D(byte[] bytes) { - byte[] bytes = null; + if (reader.Size == 0 || m_Width == 0 || m_Height == 0) + { + return false; + } + var flag = false; + var buff = BigArrayPool.Shared.Rent(reader.Size); + reader.GetData(buff); switch (m_TextureFormat) { case TextureFormat.Alpha8: //test pass - bytes = DecodeAlpha8(); + flag = DecodeAlpha8(buff, bytes); break; case TextureFormat.ARGB4444: //test pass - SwapBytesForXbox(); - bytes = DecodeARGB4444(); + SwapBytesForXbox(buff); + flag = DecodeARGB4444(buff, bytes); break; case TextureFormat.RGB24: //test pass - bytes = DecodeRGB24(); + flag = DecodeRGB24(buff, bytes); break; case TextureFormat.RGBA32: //test pass - bytes = DecodeRGBA32(); + flag = DecodeRGBA32(buff, bytes); break; case TextureFormat.ARGB32: //test pass - bytes = DecodeARGB32(); + flag = DecodeARGB32(buff, bytes); break; case TextureFormat.RGB565: //test pass - SwapBytesForXbox(); - bytes = DecodeRGB565(); + SwapBytesForXbox(buff); + flag = DecodeRGB565(buff, bytes); break; case TextureFormat.R16: //test pass - bytes = DecodeR16(); + flag = DecodeR16(buff, bytes); break; case TextureFormat.DXT1: //test pass - SwapBytesForXbox(); - bytes = DecodeDXT1(); + SwapBytesForXbox(buff); + flag = DecodeDXT1(buff, bytes); break; case TextureFormat.DXT5: //test pass - SwapBytesForXbox(); - bytes = DecodeDXT5(); + SwapBytesForXbox(buff); + flag = DecodeDXT5(buff, bytes); break; case TextureFormat.RGBA4444: //test pass - bytes = DecodeRGBA4444(); + flag = DecodeRGBA4444(buff, bytes); break; case TextureFormat.BGRA32: //test pass - bytes = DecodeBGRA32(); + flag = DecodeBGRA32(buff, bytes); break; case TextureFormat.RHalf: - bytes = DecodeRHalf(); + flag = DecodeRHalf(buff, bytes); break; case TextureFormat.RGHalf: - bytes = DecodeRGHalf(); + flag = DecodeRGHalf(buff, bytes); break; case TextureFormat.RGBAHalf: //test pass - bytes = DecodeRGBAHalf(); + flag = DecodeRGBAHalf(buff, bytes); break; case TextureFormat.RFloat: - bytes = DecodeRFloat(); + flag = DecodeRFloat(buff, bytes); break; case TextureFormat.RGFloat: - bytes = DecodeRGFloat(); + flag = DecodeRGFloat(buff, bytes); break; case TextureFormat.RGBAFloat: - bytes = DecodeRGBAFloat(); + flag = DecodeRGBAFloat(buff, bytes); break; case TextureFormat.YUY2: //test pass - bytes = DecodeYUY2(); + flag = DecodeYUY2(buff, bytes); break; case TextureFormat.RGB9e5Float: //test pass - bytes = DecodeRGB9e5Float(); + flag = DecodeRGB9e5Float(buff, bytes); break; case TextureFormat.BC4: //test pass - bytes = DecodeBC4(); + flag = DecodeBC4(buff, bytes); break; case TextureFormat.BC5: //test pass - bytes = DecodeBC5(); + flag = DecodeBC5(buff, bytes); break; case TextureFormat.BC6H: //test pass - bytes = DecodeBC6H(); + flag = DecodeBC6H(buff, bytes); break; case TextureFormat.BC7: //test pass - bytes = DecodeBC7(); + flag = DecodeBC7(buff, bytes); break; case TextureFormat.DXT1Crunched: //test pass - if (UnpackCrunch()) - { - bytes = DecodeDXT1(); - } + flag = DecodeDXT1Crunched(buff, bytes); break; case TextureFormat.DXT5Crunched: //test pass - if (UnpackCrunch()) - { - bytes = DecodeDXT5(); - } + flag = DecodeDXT5Crunched(buff, bytes); break; case TextureFormat.PVRTC_RGB2: //test pass case TextureFormat.PVRTC_RGBA2: //test pass - bytes = DecodePVRTC(true); + flag = DecodePVRTC(buff, bytes, true); break; case TextureFormat.PVRTC_RGB4: //test pass case TextureFormat.PVRTC_RGBA4: //test pass - bytes = DecodePVRTC(false); + flag = DecodePVRTC(buff, bytes, false); break; case TextureFormat.ETC_RGB4: //test pass case TextureFormat.ETC_RGB4_3DS: - bytes = DecodeETC1(); + flag = DecodeETC1(buff, bytes); break; case TextureFormat.ATC_RGB4: //test pass - bytes = DecodeATCRGB4(); + flag = DecodeATCRGB4(buff, bytes); break; case TextureFormat.ATC_RGBA8: //test pass - bytes = DecodeATCRGBA8(); + flag = DecodeATCRGBA8(buff, bytes); break; case TextureFormat.EAC_R: //test pass - bytes = DecodeEACR(); + flag = DecodeEACR(buff, bytes); break; case TextureFormat.EAC_R_SIGNED: - bytes = DecodeEACRSigned(); + flag = DecodeEACRSigned(buff, bytes); break; case TextureFormat.EAC_RG: //test pass - bytes = DecodeEACRG(); + flag = DecodeEACRG(buff, bytes); break; case TextureFormat.EAC_RG_SIGNED: - bytes = DecodeEACRGSigned(); + flag = DecodeEACRGSigned(buff, bytes); break; case TextureFormat.ETC2_RGB: //test pass - bytes = DecodeETC2(); + flag = DecodeETC2(buff, bytes); break; case TextureFormat.ETC2_RGBA1: //test pass - bytes = DecodeETC2A1(); + flag = DecodeETC2A1(buff, bytes); break; case TextureFormat.ETC2_RGBA8: //test pass case TextureFormat.ETC_RGBA8_3DS: - bytes = DecodeETC2A8(); + 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 - bytes = DecodeASTC(4); + 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 - bytes = DecodeASTC(5); + 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 - bytes = DecodeASTC(6); + 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 - bytes = DecodeASTC(8); + 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 - bytes = DecodeASTC(10); + 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 - bytes = DecodeASTC(12); + flag = DecodeASTC(buff, bytes, 12); break; case TextureFormat.RG16: //test pass - bytes = DecodeRG16(); + flag = DecodeRG16(buff, bytes); break; case TextureFormat.R8: //test pass - bytes = DecodeR8(); + flag = DecodeR8(buff, bytes); break; case TextureFormat.ETC_RGB4Crunched: //test pass - if (UnpackCrunch()) - { - bytes = DecodeETC1(); - } + flag = DecodeETC1Crunched(buff, bytes); break; case TextureFormat.ETC2_RGBA8Crunched: //test pass - if (UnpackCrunch()) - { - bytes = DecodeETC2A8(); - } + flag = DecodeETC2A8Crunched(buff, bytes); break; } - return bytes; + BigArrayPool.Shared.Return(buff); + return flag; } - private void SwapBytesForXbox() + private void SwapBytesForXbox(byte[] image_data) { if (platform == BuildTarget.XBOX360) { - for (var i = 0; i < image_data_size / 2; i++) + for (var i = 0; i < image_data.Length / 2; i++) { var b = image_data[i * 2]; image_data[i * 2] = image_data[i * 2 + 1]; @@ -220,22 +212,22 @@ namespace AssetStudio } } - private byte[] DecodeAlpha8() + private bool DecodeAlpha8(byte[] image_data, byte[] buff) { - var buff = Enumerable.Repeat(0xFF, m_Width * m_Height * 4).ToArray(); + var span = new Span(buff); + span.Fill(0xFF); for (var i = 0; i < m_Width * m_Height; i++) { buff[i * 4 + 3] = image_data[i]; } - return buff; + return true; } - private byte[] DecodeARGB4444() + private bool DecodeARGB4444(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; + var pixelNew = new byte[4]; for (var i = 0; i < m_Width * m_Height; i++) { - var pixelNew = new byte[4]; var pixelOldShort = BitConverter.ToUInt16(image_data, i * 2); pixelNew[0] = (byte)(pixelOldShort & 0x000f); pixelNew[1] = (byte)((pixelOldShort & 0x00f0) >> 4); @@ -245,12 +237,11 @@ namespace AssetStudio pixelNew[j] = (byte)((pixelNew[j] << 4) | pixelNew[j]); pixelNew.CopyTo(buff, i * 4); } - return buff; + return true; } - private byte[] DecodeRGB24() + private bool DecodeRGB24(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; i++) { buff[i * 4] = image_data[i * 3 + 2]; @@ -258,12 +249,11 @@ namespace AssetStudio buff[i * 4 + 2] = image_data[i * 3 + 0]; buff[i * 4 + 3] = 255; } - return buff; + return true; } - private byte[] DecodeRGBA32() + private bool DecodeRGBA32(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; i += 4) { buff[i] = image_data[i + 2]; @@ -271,12 +261,11 @@ namespace AssetStudio buff[i + 2] = image_data[i + 0]; buff[i + 3] = image_data[i + 3]; } - return buff; + return true; } - private byte[] DecodeARGB32() + private bool DecodeARGB32(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; i += 4) { buff[i] = image_data[i + 3]; @@ -284,12 +273,11 @@ namespace AssetStudio buff[i + 2] = image_data[i + 1]; buff[i + 3] = image_data[i + 0]; } - return buff; + return true; } - private byte[] DecodeRGB565() + private bool DecodeRGB565(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; i++) { var p = BitConverter.ToUInt16(image_data, i * 2); @@ -298,46 +286,36 @@ namespace AssetStudio buff[i * 4 + 2] = (byte)((p >> 8 & 0xf8) | (p >> 13)); buff[i * 4 + 3] = 255; } - return buff; + return true; } - private byte[] DecodeR16() + private bool DecodeR16(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; i++) { + buff[i * 4] = 0; //b + buff[i * 4 + 1] = 0; //g buff[i * 4 + 2] = image_data[i * 2 + 1]; //r buff[i * 4 + 3] = 255; //a } - return buff; + return true; } - private byte[] DecodeDXT1() + private bool DecodeDXT1(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeDXT1(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeDXT1(image_data, m_Width, m_Height, buff); } - private byte[] DecodeDXT5() + private bool DecodeDXT5(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeDXT5(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeDXT5(image_data, m_Width, m_Height, buff); } - private byte[] DecodeRGBA4444() + private bool DecodeRGBA4444(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; + var pixelNew = new byte[4]; for (var i = 0; i < m_Width * m_Height; i++) { - var pixelNew = new byte[4]; var pixelOldShort = BitConverter.ToUInt16(image_data, i * 2); pixelNew[0] = (byte)((pixelOldShort & 0x00f0) >> 4); pixelNew[1] = (byte)((pixelOldShort & 0x0f00) >> 8); @@ -347,12 +325,11 @@ namespace AssetStudio pixelNew[j] = (byte)((pixelNew[j] << 4) | pixelNew[j]); pixelNew.CopyTo(buff, i * 4); } - return buff; + return true; } - private byte[] DecodeBGRA32() + private bool DecodeBGRA32(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; i += 4) { buff[i] = image_data[i]; @@ -360,12 +337,11 @@ namespace AssetStudio buff[i + 2] = image_data[i + 2]; buff[i + 3] = image_data[i + 3]; } - return buff; + return true; } - private byte[] DecodeRHalf() + private bool DecodeRHalf(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; i += 4) { buff[i] = 0; @@ -373,12 +349,11 @@ namespace AssetStudio buff[i + 2] = (byte)Math.Round(Half.ToHalf(image_data, i / 2) * 255f); buff[i + 3] = 255; } - return buff; + return true; } - private byte[] DecodeRGHalf() + private bool DecodeRGHalf(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; i += 4) { buff[i] = 0; @@ -386,12 +361,11 @@ namespace AssetStudio buff[i + 2] = (byte)Math.Round(Half.ToHalf(image_data, i) * 255f); buff[i + 3] = 255; } - return buff; + return true; } - private byte[] DecodeRGBAHalf() + private bool DecodeRGBAHalf(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; i += 4) { buff[i] = (byte)Math.Round(Half.ToHalf(image_data, i * 2 + 4) * 255f); @@ -399,12 +373,11 @@ namespace AssetStudio buff[i + 2] = (byte)Math.Round(Half.ToHalf(image_data, i * 2) * 255f); buff[i + 3] = (byte)Math.Round(Half.ToHalf(image_data, i * 2 + 6) * 255f); } - return buff; + return true; } - private byte[] DecodeRFloat() + private bool DecodeRFloat(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; i += 4) { buff[i] = 0; @@ -412,12 +385,11 @@ namespace AssetStudio buff[i + 2] = (byte)Math.Round(BitConverter.ToSingle(image_data, i) * 255f); buff[i + 3] = 255; } - return buff; + return true; } - private byte[] DecodeRGFloat() + private bool DecodeRGFloat(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; i += 4) { buff[i] = 0; @@ -425,12 +397,11 @@ namespace AssetStudio buff[i + 2] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 2) * 255f); buff[i + 3] = 255; } - return buff; + return true; } - private byte[] DecodeRGBAFloat() + private bool DecodeRGBAFloat(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; i += 4) { buff[i] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4 + 8) * 255f); @@ -438,7 +409,7 @@ namespace AssetStudio buff[i + 2] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4) * 255f); buff[i + 3] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4 + 12) * 255f); } - return buff; + return true; } private static byte ClampByte(int x) @@ -446,9 +417,8 @@ namespace AssetStudio return (byte)(byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? x : byte.MinValue)); } - private byte[] DecodeYUY2() + private bool DecodeYUY2(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; int p = 0; int o = 0; int halfWidth = m_Width / 2; @@ -474,12 +444,11 @@ namespace AssetStudio buff[o++] = 255; } } - return buff; + return true; } - private byte[] DecodeRGB9e5Float() + private bool DecodeRGB9e5Float(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < buff.Length; i += 4) { var n = BitConverter.ToInt32(image_data, i); @@ -493,195 +462,163 @@ namespace AssetStudio buff[i + 2] = (byte)Math.Round(r * scalef * 255f); buff[i + 3] = 255; } - return buff; + return true; } - private byte[] DecodeBC4() + private bool DecodeBC4(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeBC4(image_data, m_Width, m_Height, buff)) + return TextureDecoder.DecodeBC4(image_data, m_Width, m_Height, buff); + } + + private bool DecodeBC5(byte[] image_data, byte[] buff) + { + return TextureDecoder.DecodeBC5(image_data, m_Width, m_Height, buff); + } + + private bool DecodeBC6H(byte[] image_data, byte[] buff) + { + return TextureDecoder.DecodeBC6(image_data, m_Width, m_Height, buff); + } + + private bool DecodeBC7(byte[] image_data, byte[] buff) + { + return TextureDecoder.DecodeBC7(image_data, m_Width, m_Height, buff); + } + + private bool DecodeDXT1Crunched(byte[] image_data, byte[] buff) + { + if (UnpackCrunch(image_data, out var result)) { - return null; + if (DecodeDXT1(result, buff)) + { + return true; + } } - return buff; + return false; } - private byte[] DecodeBC5() + private bool DecodeDXT5Crunched(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeBC5(image_data, m_Width, m_Height, buff)) + if (UnpackCrunch(image_data, out var result)) { - return null; + if (DecodeDXT5(result, buff)) + { + return true; + } } - return buff; + return false; } - private byte[] DecodeBC6H() + private bool DecodePVRTC(byte[] image_data, byte[] buff, bool is2bpp) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeBC6(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodePVRTC(image_data, m_Width, m_Height, buff, is2bpp); } - private byte[] DecodeBC7() + private bool DecodeETC1(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeBC7(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeETC1(image_data, m_Width, m_Height, buff); } - private byte[] DecodePVRTC(bool is2bpp) + private bool DecodeATCRGB4(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodePVRTC(image_data, m_Width, m_Height, buff, is2bpp)) - { - return null; - } - return buff; + return TextureDecoder.DecodeATCRGB4(image_data, m_Width, m_Height, buff); } - private byte[] DecodeETC1() + private bool DecodeATCRGBA8(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeETC1(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeATCRGBA8(image_data, m_Width, m_Height, buff); } - private byte[] DecodeATCRGB4() + private bool DecodeEACR(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeATCRGB4(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeEACR(image_data, m_Width, m_Height, buff); } - private byte[] DecodeATCRGBA8() + private bool DecodeEACRSigned(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeATCRGBA8(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeEACRSigned(image_data, m_Width, m_Height, buff); } - private byte[] DecodeEACR() + private bool DecodeEACRG(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeEACR(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeEACRG(image_data, m_Width, m_Height, buff); } - private byte[] DecodeEACRSigned() + private bool DecodeEACRGSigned(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeEACRSigned(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeEACRGSigned(image_data, m_Width, m_Height, buff); } - private byte[] DecodeEACRG() + private bool DecodeETC2(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeEACRG(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeETC2(image_data, m_Width, m_Height, buff); } - private byte[] DecodeEACRGSigned() + private bool DecodeETC2A1(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeEACRGSigned(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeETC2A1(image_data, m_Width, m_Height, buff); } - private byte[] DecodeETC2() + private bool DecodeETC2A8(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeETC2(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeETC2A8(image_data, m_Width, m_Height, buff); } - private byte[] DecodeETC2A1() + private bool DecodeASTC(byte[] image_data, byte[] buff, int blocksize) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeETC2A1(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; + return TextureDecoder.DecodeASTC(image_data, m_Width, m_Height, blocksize, blocksize, buff); } - private byte[] DecodeETC2A8() + private bool DecodeRG16(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeETC2A8(image_data, m_Width, m_Height, buff)) - { - return null; - } - return buff; - } - - private byte[] DecodeASTC(int blocksize) - { - var buff = new byte[m_Width * m_Height * 4]; - if (!TextureDecoder.DecodeASTC(image_data, m_Width, m_Height, blocksize, blocksize, buff)) - { - return null; - } - return buff; - } - - private byte[] DecodeRG16() - { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; i += 2) { + buff[i * 2] = 0; //B buff[i * 2 + 1] = image_data[i + 1];//G buff[i * 2 + 2] = image_data[i];//R buff[i * 2 + 3] = 255;//A } - return buff; + return true; } - private byte[] DecodeR8() + private bool DecodeR8(byte[] image_data, byte[] buff) { - var buff = new byte[m_Width * m_Height * 4]; for (var i = 0; i < m_Width * m_Height; i++) { + buff[i * 4] = 0; //B + buff[i * 4 + 1] = 0; //G buff[i * 4 + 2] = image_data[i];//R buff[i * 4 + 3] = 255;//A } - return buff; + return true; } - private bool UnpackCrunch() + private bool DecodeETC1Crunched(byte[] image_data, byte[] buff) + { + if (UnpackCrunch(image_data, out var result)) + { + if (DecodeETC1(result, buff)) + { + return true; + } + } + return false; + } + + private bool DecodeETC2A8Crunched(byte[] image_data, byte[] buff) + { + if (UnpackCrunch(image_data, out var result)) + { + if (DecodeETC2A8(result, buff)) + { + return true; + } + } + return false; + } + + private bool UnpackCrunch(byte[] image_data, out byte[] result) { - byte[] result; if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3) //2017.3 and up || m_TextureFormat == TextureFormat.ETC_RGB4Crunched || m_TextureFormat == TextureFormat.ETC2_RGBA8Crunched) @@ -694,8 +631,6 @@ namespace AssetStudio } if (result != null) { - image_data = result; - image_data_size = result.Length; return true; } return false; diff --git a/AssetStudioUtility/Texture2DExtensions.cs b/AssetStudioUtility/Texture2DExtensions.cs index 6301f1a..e855ad0 100644 --- a/AssetStudioUtility/Texture2DExtensions.cs +++ b/AssetStudioUtility/Texture2DExtensions.cs @@ -7,20 +7,27 @@ namespace AssetStudio { public static class Texture2DExtensions { - public static Image ConvertToImage(this Texture2D m_Texture2D, bool flip) + public static Image ConvertToImage(this Texture2D m_Texture2D, bool flip) { var converter = new Texture2DConverter(m_Texture2D); - var bytes = converter.DecodeTexture2D(); - if (bytes != null && bytes.Length > 0) + var buff = BigArrayPool.Shared.Rent(m_Texture2D.m_Width * m_Texture2D.m_Height * 4); + try { - var image = Image.LoadPixelData(bytes, m_Texture2D.m_Width, m_Texture2D.m_Height); - if (flip) + if (converter.DecodeTexture2D(buff)) { - image.Mutate(x => x.Flip(FlipMode.Vertical)); + var image = Image.LoadPixelData(buff, m_Texture2D.m_Width, m_Texture2D.m_Height); + if (flip) + { + image.Mutate(x => x.Flip(FlipMode.Vertical)); + } + return image; } - return image; + return null; + } + finally + { + BigArrayPool.Shared.Return(buff); } - return null; } public static MemoryStream ConvertToStream(this Texture2D m_Texture2D, ImageFormat imageFormat, bool flip)