From abeb25148883c5c389ac283ed28d8e0d1024a900 Mon Sep 17 00:00:00 2001 From: VaDiM Date: Wed, 17 Dec 2025 04:52:03 +0300 Subject: [PATCH] Update cropping method for Switch deswizzler Inspired by AssetRipper, yeah --- AssetStudioUtility/Texture2DConverter.cs | 54 ++++--------- AssetStudioUtility/Texture2DExtensions.cs | 12 +-- .../Texture2DSwitchDeswizzler.cs | 80 +++++++++++++------ 3 files changed, 70 insertions(+), 76 deletions(-) diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs index a076b47..c521e6b 100644 --- a/AssetStudioUtility/Texture2DConverter.cs +++ b/AssetStudioUtility/Texture2DConverter.cs @@ -10,8 +10,6 @@ namespace AssetStudio private ResourceReader reader; private int m_Width; private int m_Height; - private int m_WidthCrop; - private int m_HeightCrop; private TextureFormat m_TextureFormat; private byte[] m_PlatformBlob; private UnityVersion version; @@ -23,13 +21,12 @@ namespace AssetStudio private SixLabors.ImageSharp.Size blockSize; public int OutputDataSize => outPutDataSize; - public bool UsesSwitchSwizzle => switchSwizzled; public Texture2DConverter(Texture2D m_Texture2D) { reader = m_Texture2D.image_data; - m_WidthCrop = m_Texture2D.m_Width; - m_HeightCrop = m_Texture2D.m_Height; + m_Width = m_Texture2D.m_Width; + m_Height = m_Texture2D.m_Height; m_TextureFormat = m_Texture2D.m_TextureFormat; m_PlatformBlob = m_Texture2D.m_PlatformBlob; version = m_Texture2D.version; @@ -42,11 +39,6 @@ namespace AssetStudio { SetupSwitchSwizzle(); } - else - { - m_Width = m_WidthCrop; - m_Height = m_HeightCrop; - } outPutDataSize = m_Width * m_Height * 4; } @@ -57,27 +49,20 @@ namespace AssetStudio //work fine for now gobsPerBlock = 1 << BitConverter.ToInt32(m_PlatformBlob, 8); - //in older versions of unity, rgb24 has a platformBlob which shouldn't - //be possible. it turns out in this case, the image is just rgba32. - //probably shouldn't be modifying the texture2d here, but eh, who cares - if (m_TextureFormat == TextureFormat.RGB24) + switch (m_TextureFormat) { - m_TextureFormat = TextureFormat.RGBA32; - } - else if (m_TextureFormat == TextureFormat.BGR24) - { - m_TextureFormat = TextureFormat.BGRA32; + //in older versions of unity, rgb24 has a platformBlob which shouldn't + //be possible. it turns out in this case, the image is just rgba32. + //probably shouldn't be modifying the texture2d here, but eh, who cares + case TextureFormat.RGB24: + m_TextureFormat = TextureFormat.RGBA32; + break; + case TextureFormat.BGR24: + m_TextureFormat = TextureFormat.BGRA32; + break; } blockSize = Texture2DSwitchDeswizzler.GetTextureFormatBlockSize(m_TextureFormat); - var realSize = Texture2DSwitchDeswizzler.GetPaddedTextureSize(m_WidthCrop, m_HeightCrop, blockSize.Width, blockSize.Height, gobsPerBlock); - m_Width = realSize.Width; - m_Height = realSize.Height; - } - - public SixLabors.ImageSharp.Size GetUncroppedSize() - { - return new SixLabors.ImageSharp.Size(m_Width, m_Height); } public bool DecodeTexture2D(byte[] bytes) @@ -93,18 +78,9 @@ namespace AssetStudio _ = reader.GetData(buff); if (switchSwizzled) { - var unswizzledData = BigArrayPool.Shared.Rent(reader.Size); - try - { - Texture2DSwitchDeswizzler.Unswizzle(buff, GetUncroppedSize(), blockSize, gobsPerBlock, unswizzledData); - BigArrayPool.Shared.Return(buff, clearArray: true); - buff = unswizzledData; - } - catch (Exception e) - { - BigArrayPool.Shared.Return(unswizzledData, clearArray: true); - Logger.Error(e.Message, e); - } + var spanBuff = buff.AsSpan(0, reader.Size); + var textureSize = new SixLabors.ImageSharp.Size(m_Width, m_Height); + Texture2DSwitchDeswizzler.Unswizzle(ref spanBuff, textureSize, blockSize, gobsPerBlock); } switch (m_TextureFormat) diff --git a/AssetStudioUtility/Texture2DExtensions.cs b/AssetStudioUtility/Texture2DExtensions.cs index 308a77d..61062bc 100644 --- a/AssetStudioUtility/Texture2DExtensions.cs +++ b/AssetStudioUtility/Texture2DExtensions.cs @@ -18,17 +18,7 @@ namespace AssetStudio if (!converter.DecodeTexture2D(buff)) return null; - Image image; - if (converter.UsesSwitchSwizzle) - { - var uncroppedSize = converter.GetUncroppedSize(); - image = Image.LoadPixelData(spanBuff, uncroppedSize.Width, uncroppedSize.Height); - image.Mutate(x => x.Crop(m_Texture2D.m_Width, m_Texture2D.m_Height)); - } - else - { - image = Image.LoadPixelData(spanBuff, m_Texture2D.m_Width, m_Texture2D.m_Height); - } + var image = Image.LoadPixelData(spanBuff, m_Texture2D.m_Width, m_Texture2D.m_Height); if (flip) { diff --git a/AssetStudioUtility/Texture2DSwitchDeswizzler.cs b/AssetStudioUtility/Texture2DSwitchDeswizzler.cs index 8608aee..1061d4a 100644 --- a/AssetStudioUtility/Texture2DSwitchDeswizzler.cs +++ b/AssetStudioUtility/Texture2DSwitchDeswizzler.cs @@ -1,4 +1,4 @@ -// https://github.com/nesrak1/AssetStudio/tree/switch-tex-deswizzle +//src: https://github.com/nesrak1/AssetStudio/tree/switch-tex-deswizzle using SixLabors.ImageSharp; using System; @@ -38,43 +38,66 @@ namespace AssetStudio NPRTVX... */ - private static int CeilDivide(int a, int b) + internal static void Unswizzle(ref Span data, Size textureSize, Size blockSize, int gobsPerBlock) { - return (a + b - 1) / b; - } + var paddedSize = GetPaddedTextureSize(textureSize, blockSize, gobsPerBlock); - internal static void Unswizzle(ReadOnlySpan data, Size imageSize, Size blockSize, int gobsPerBlock, Span newData) - { - int width = imageSize.Width; - int height = imageSize.Height; - - int blockCountX = CeilDivide(width, blockSize.Width); - int blockCountY = CeilDivide(height, blockSize.Height); + int blockCountX = CeilDivide(paddedSize.Width, blockSize.Width); + int blockCountY = CeilDivide(paddedSize.Height, blockSize.Height); int gobCountX = blockCountX / GOB_X_TEXEL_COUNT; int gobCountY = blockCountY / GOB_Y_TEXEL_COUNT; - int srcPos = 0; - for (int i = 0; i < gobCountY / gobsPerBlock; i++) + var unswizzledData = BigArrayPool.Shared.Rent(data.Length); + var unswizzledDataSpan = unswizzledData.AsSpan(0, data.Length); + try { - for (int j = 0; j < gobCountX; j++) + int srcPos = 0; + for (int i = 0; i < gobCountY / gobsPerBlock; i++) { - for (int k = 0; k < gobsPerBlock; k++) + for (int j = 0; j < gobCountX; j++) { - for (int l = 0; l < BLOCKS_IN_GOB; l++) + for (int k = 0; k < gobsPerBlock; k++) { - int gobX = GOB_X_POSES[l]; - int gobY = GOB_Y_POSES[l]; - int gobDstX = j * GOB_X_TEXEL_COUNT + gobX; - int gobDstY = (i * gobsPerBlock + k) * GOB_Y_TEXEL_COUNT + gobY; - int gobDstLinPos = gobDstY * blockCountX * TEXEL_BYTE_SIZE + gobDstX * TEXEL_BYTE_SIZE; + for (int l = 0; l < BLOCKS_IN_GOB; l++) + { + int gobX = GOB_X_POSES[l]; + int gobY = GOB_Y_POSES[l]; + int gobDstX = j * GOB_X_TEXEL_COUNT + gobX; + int gobDstY = (i * gobsPerBlock + k) * GOB_Y_TEXEL_COUNT + gobY; + int gobDstLinPos = gobDstY * blockCountX * TEXEL_BYTE_SIZE + gobDstX * TEXEL_BYTE_SIZE; - data.Slice(srcPos, TEXEL_BYTE_SIZE).CopyTo(newData.Slice(gobDstLinPos)); + data.Slice(srcPos, TEXEL_BYTE_SIZE).CopyTo(unswizzledDataSpan.Slice(gobDstLinPos)); - srcPos += TEXEL_BYTE_SIZE; + srcPos += TEXEL_BYTE_SIZE; + } } } } + + //Crop unswizzled image if needed + var blockByteSize = TEXEL_BYTE_SIZE / (blockSize.Width / blockSize.Height); + var blocksPerTexel = TEXEL_BYTE_SIZE / blockByteSize; + var paddedTexBlockCountX = CeilDivide(paddedSize.Width * blocksPerTexel, blockSize.Width); + var texBlockCountX = CeilDivide(textureSize.Width * blocksPerTexel, blockSize.Width); + if (paddedTexBlockCountX - texBlockCountX > 0) + { + var texBlockCountY = CeilDivide(textureSize.Height, blockSize.Height); + for (var y = 0; y < texBlockCountY; y++) + { + unswizzledDataSpan.Slice(y * paddedTexBlockCountX * blockByteSize, texBlockCountX * blockByteSize).CopyTo(unswizzledDataSpan.Slice(y * texBlockCountX * blockByteSize)); + } + unswizzledDataSpan.Slice(texBlockCountY * texBlockCountX * blockByteSize).Clear(); + } + unswizzledDataSpan.CopyTo(data); + } + catch (Exception e) + { + Logger.Warning($"Failed to deswizzle the image.\n{e}"); + } + finally + { + BigArrayPool.Shared.Return(unswizzledData, clearArray: true); } } @@ -117,11 +140,16 @@ namespace AssetStudio }; } - internal static Size GetPaddedTextureSize(int width, int height, int blockWidth, int blockHeight, int gobsPerBlock) + private static Size GetPaddedTextureSize(Size textureSize, Size blockSize, int gobsPerBlock) { - width = CeilDivide(width, blockWidth * GOB_X_TEXEL_COUNT) * blockWidth * GOB_X_TEXEL_COUNT; - height = CeilDivide(height, blockHeight * GOB_Y_TEXEL_COUNT * gobsPerBlock) * blockHeight * GOB_Y_TEXEL_COUNT * gobsPerBlock; + var width = CeilDivide(textureSize.Width, blockSize.Width * GOB_X_TEXEL_COUNT) * blockSize.Width * GOB_X_TEXEL_COUNT; + var height = CeilDivide(textureSize.Height, blockSize.Height * GOB_Y_TEXEL_COUNT * gobsPerBlock) * blockSize.Height * GOB_Y_TEXEL_COUNT * gobsPerBlock; return new Size(width, height); } + + private static int CeilDivide(int a, int b) + { + return (a + b - 1) / b; + } } }