1
0
mirror of synced 2024-12-11 15:25:59 +01:00
Switch-Toolbox/Switch_Toolbox_Library/Swizzling/TegraX1Swizzle.cs
KillzXGaming ddcd0285b3 More fixes
Fixed samplers for switch not mapping properly.
Fixed DDS files exporting mip maps wrong. Would cause them to not import back with size errors.
Fixed bntx alignment causing corrupted textures.
Fixed bfres not opening from null bone indices/unrigged meshes.
2019-03-26 15:24:20 -04:00

211 lines
8.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Switch_Toolbox.Library
{
public class TegraX1Swizzle
{
public static byte[] GetImageData(STGenericTexture texture, byte[] ImageData, int ArrayLevel, int MipLevel)
{
int target = 1;
uint bpp = STGenericTexture.GetBytesPerPixel(texture.Format);
uint blkWidth = STGenericTexture.GetBlockWidth(texture.Format);
uint blkHeight = STGenericTexture.GetBlockHeight(texture.Format);
uint blkDepth = STGenericTexture.GetBlockDepth(texture.Format);
uint blockHeight = TegraX1Swizzle.GetBlockHeight(TegraX1Swizzle.DIV_ROUND_UP(texture.Height, blkHeight));
uint BlockHeightLog2 = (uint)Convert.ToString(blockHeight, 2).Length - 1;
uint tileMode = 0;
uint Pitch = 0;
uint DataAlignment = 512;
uint TileMode = 0;
int linesPerBlockHeight = (1 << (int)BlockHeightLog2) * 8;
uint ArrayOffset = 0;
for (int arrayLevel = 0; arrayLevel < texture.ArrayCount; arrayLevel++)
{
uint SurfaceSize = 0;
int blockHeightShift = 0;
List<uint> MipOffsets = new List<uint>();
for (int mipLevel = 0; mipLevel < texture.MipCount; mipLevel++)
{
uint width = (uint)Math.Max(1, texture.Width >> mipLevel);
uint height = (uint)Math.Max(1, texture.Height >> mipLevel);
uint depth = (uint)Math.Max(1, texture.Depth >> mipLevel);
uint size = TegraX1Swizzle.DIV_ROUND_UP(width, blkWidth) * TegraX1Swizzle.DIV_ROUND_UP(height, blkHeight) * bpp;
if (TegraX1Swizzle.pow2_round_up(TegraX1Swizzle.DIV_ROUND_UP(height, blkWidth)) < linesPerBlockHeight)
blockHeightShift += 1;
uint width__ = TegraX1Swizzle.DIV_ROUND_UP(width, blkWidth);
uint height__ = TegraX1Swizzle.DIV_ROUND_UP(height, blkHeight);
//Calculate the mip size instead
byte[] AlignedData = new byte[(TegraX1Swizzle.round_up(SurfaceSize, DataAlignment) - SurfaceSize)];
SurfaceSize += (uint)AlignedData.Length;
MipOffsets.Add(SurfaceSize);
//Get the first mip offset and current one and the total image size
int msize = (int)((MipOffsets[0] + ImageData.Length - MipOffsets[mipLevel]) / texture.ArrayCount);
byte[] data_ = Utils.SubArray(ImageData, ArrayOffset + MipOffsets[mipLevel], (uint)msize);
try
{
Pitch = TegraX1Swizzle.round_up(width__ * bpp, 64);
SurfaceSize += Pitch * TegraX1Swizzle.round_up(height__, Math.Max(1, blockHeight >> blockHeightShift) * 8);
Console.WriteLine($"{width} {height} {blkWidth} {blkHeight} {target} {bpp} {TileMode} {(int)Math.Max(0, BlockHeightLog2 - blockHeightShift)} {data_.Length}");
byte[] result = deswizzle(width, height, depth, blkWidth, blkHeight, blkDepth, target, bpp, TileMode, (int)Math.Max(0, BlockHeightLog2 - blockHeightShift), data_);
//Create a copy and use that to remove uneeded data
byte[] result_ = new byte[size];
Array.Copy(result, 0, result_, 0, size);
result = null;
if (ArrayLevel == arrayLevel && MipLevel == mipLevel)
return result_;
}
catch (Exception e)
{
System.Windows.Forms.MessageBox.Show($"Failed to swizzle texture {texture.Text}!");
Console.WriteLine(e);
return new byte[0];
}
}
ArrayOffset += (uint)(ImageData.Length / texture.ArrayCount);
}
return new byte[0];
}
/*---------------------------------------
*
* Code ported from AboodXD's BNTX Extractor https://github.com/aboood40091/BNTX-Extractor/blob/master/swizzle.py
*
*---------------------------------------*/
public static uint GetBlockHeight(uint height)
{
uint blockHeight = pow2_round_up(height / 8);
if (blockHeight > 16)
blockHeight = 16;
return blockHeight;
}
public static uint DIV_ROUND_UP(uint n, uint d)
{
return (n + d - 1) / d;
}
public static uint round_up(uint x, uint y)
{
return ((x - 1) | (y - 1)) + 1;
}
public static uint pow2_round_up(uint x)
{
x -= 1;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x + 1;
}
private static byte[] _swizzle(uint width, uint height, uint depth, uint blkWidth, uint blkHeight, uint blkDepth, int roundPitch, uint bpp, uint tileMode, int blockHeightLog2, byte[] data, int toSwizzle)
{
uint block_height = (uint)(1 << blockHeightLog2);
Console.WriteLine($"Swizzle {width} {height} {blkWidth} {blkHeight} {roundPitch} {bpp} {tileMode} {blockHeightLog2} {data.Length} {toSwizzle}");
width = DIV_ROUND_UP(width, blkWidth);
height = DIV_ROUND_UP(height, blkHeight);
depth = DIV_ROUND_UP(depth, blkDepth);
uint pitch;
uint surfSize;
if (tileMode == 1)
{
pitch = width * bpp;
if (roundPitch == 1)
pitch = round_up(pitch, 32);
surfSize = pitch * height;
}
else
{
pitch = round_up(width * bpp, 64);
surfSize = pitch * round_up(height, block_height * 8);
}
byte[] result = new byte[surfSize];
for (uint y = 0; y < height; y++)
{
for (uint x = 0; x < width; x++)
{
uint pos;
uint pos_;
if (tileMode == 1)
pos = y * pitch + x * bpp;
else
pos = getAddrBlockLinear(x, y, width, bpp, 0, block_height);
pos_ = (y * width + x) * bpp;
if (pos + bpp <= surfSize)
{
if (toSwizzle == 0)
Array.Copy(data, pos, result, pos_, bpp);
else
Array.Copy(data, pos_, result, pos, bpp);
}
}
}
return result;
}
public static byte[] deswizzle(uint width, uint height, uint depth, uint blkWidth, uint blkHeight, uint blkDepth, int roundPitch, uint bpp, uint tileMode, int size_range, byte[] data)
{
return _swizzle(width, height, depth, blkWidth, blkHeight, blkDepth, roundPitch, bpp, tileMode, size_range, data, 0);
}
public static byte[] swizzle(uint width, uint height, uint depth, uint blkWidth, uint blkHeight,uint blkDepth, int roundPitch, uint bpp, uint tileMode, int size_range, byte[] data)
{
return _swizzle(width, height, depth, blkWidth, blkHeight, blkDepth, roundPitch, bpp, tileMode, size_range, data, 1);
}
static uint getAddrBlockLinear(uint x, uint y, uint width, uint bytes_per_pixel, uint base_address, uint block_height)
{
/*
From Tega X1 TRM
*/
uint image_width_in_gobs = DIV_ROUND_UP(width * bytes_per_pixel, 64);
uint GOB_address = (base_address
+ (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs
+ (x * bytes_per_pixel / 64) * 512 * block_height
+ (y % (8 * block_height) / 8) * 512);
x *= bytes_per_pixel;
uint Address = (GOB_address + ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64
+ ((x % 32) / 16) * 32 + (y % 2) * 16 + (x % 16));
return Address;
}
}
}