Update cropping method for Switch deswizzler

Inspired by AssetRipper, yeah
This commit is contained in:
VaDiM
2026-01-27 00:05:24 +03:00
parent 6fcbf4ca2e
commit abeb251488
3 changed files with 70 additions and 76 deletions
+15 -39
View File
@@ -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<byte>.Shared.Rent(reader.Size);
try
{
Texture2DSwitchDeswizzler.Unswizzle(buff, GetUncroppedSize(), blockSize, gobsPerBlock, unswizzledData);
BigArrayPool<byte>.Shared.Return(buff, clearArray: true);
buff = unswizzledData;
}
catch (Exception e)
{
BigArrayPool<byte>.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)
+1 -11
View File
@@ -18,17 +18,7 @@ namespace AssetStudio
if (!converter.DecodeTexture2D(buff))
return null;
Image<Bgra32> image;
if (converter.UsesSwitchSwizzle)
{
var uncroppedSize = converter.GetUncroppedSize();
image = Image.LoadPixelData<Bgra32>(spanBuff, uncroppedSize.Width, uncroppedSize.Height);
image.Mutate(x => x.Crop(m_Texture2D.m_Width, m_Texture2D.m_Height));
}
else
{
image = Image.LoadPixelData<Bgra32>(spanBuff, m_Texture2D.m_Width, m_Texture2D.m_Height);
}
var image = Image.LoadPixelData<Bgra32>(spanBuff, m_Texture2D.m_Width, m_Texture2D.m_Height);
if (flip)
{
+54 -26
View File
@@ -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<byte> data, Size textureSize, Size blockSize, int gobsPerBlock)
{
return (a + b - 1) / b;
}
var paddedSize = GetPaddedTextureSize(textureSize, blockSize, gobsPerBlock);
internal static void Unswizzle(ReadOnlySpan<byte> data, Size imageSize, Size blockSize, int gobsPerBlock, Span<byte> 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<byte>.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<byte>.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;
}
}
}