/** * Ported from https://github.com/KillzXGaming/Switch-Toolbox */ public static byte[] GetImageData(SBSurface surface, byte[] ImageData, int ArrayLevel, int MipLevel, int MipCount, int target = 1) { uint bpp = TextureFormatInfo.GetBPP(surface.InternalFormat); uint blkWidth = TextureFormatInfo.GetBlockWidth(surface.InternalFormat); uint blkHeight = TextureFormatInfo.GetBlockHeight(surface.InternalFormat); uint blkDepth = TextureFormatInfo.GetBlockDepth(surface.InternalFormat); uint blockHeight = GetBlockHeight(DivRoundUp((uint)surface.Height, blkHeight)); int BlockHeightLog2 = Convert.ToString(blockHeight, 2).Length - 1; uint Pitch = 0; uint DataAlignment = 512; uint TileMode = 0; int linesPerBlockHeight = (1 << BlockHeightLog2) * 8; uint ArrayOffset = 0; for (int arrayLevel = 0; arrayLevel < surface.ArrayCount; arrayLevel++) { uint SurfaceSize = 0; int blockHeightShift = 0; List <uint> MipOffsets = new List <uint>(); for (int mipLevel = 0; mipLevel < MipCount; mipLevel++) { uint width = (uint)Math.Max(1, surface.Width >> mipLevel); uint height = (uint)Math.Max(1, surface.Height >> mipLevel); uint depth = (uint)Math.Max(1, surface.Depth >> mipLevel); uint size = DivRoundUp(width, blkWidth) * DivRoundUp(height, blkHeight) * bpp; if (Pow2RoundUp(DivRoundUp(height, blkWidth)) < linesPerBlockHeight) { blockHeightShift += 1; } //SBConsole.WriteLine(height + " " + blkWidth + " " + Pow2RoundUp(DivRoundUp(height, blkWidth)) + " " + blockHeight + " " + blockHeightShift + " " + linesPerBlockHeight + " " + BlockHeightLog2 + " " + (int)Math.Max(0, BlockHeightLog2 - blockHeightShift)); uint width__ = DivRoundUp(width, blkWidth); uint height__ = DivRoundUp(height, blkHeight); //Calculate the mip size instead byte[] AlignedData = new byte[(RoundUp(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]) / surface.ArrayCount); if (msize > ImageData.Length - (ArrayOffset + MipOffsets[mipLevel])) { msize = (int)(ImageData.Length - (ArrayOffset + MipOffsets[mipLevel])); } byte[] data_ = new byte[msize]; if (ArrayLevel == arrayLevel && MipLevel == mipLevel) { Array.Copy(ImageData, ArrayOffset + MipOffsets[mipLevel], data_, 0, msize); } try { Pitch = RoundUp(width__ * bpp, 64); SurfaceSize += Pitch * RoundUp(height__, Math.Max(1, blockHeight >> blockHeightShift) * 8); if (ArrayLevel == arrayLevel && MipLevel == mipLevel) { //SBConsole.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; return(result_); } } catch (Exception e) { System.Windows.Forms.MessageBox.Show($"Failed to swizzle texture {surface.Name}!"); Console.WriteLine(e); return(new byte[0]); } } ArrayOffset += (uint)(ImageData.Length / surface.ArrayCount); } return(new byte[0]); }
public static byte[] CreateBuffer(SBSurface surface, int target = 1) { List <byte> ImageData = new List <byte>(); if (surface.Arrays.Count == 0) { return(ImageData.ToArray()); } var MipCount = surface.Arrays[0].Mipmaps.Count; uint bpp = TextureFormatInfo.GetBPP(surface.InternalFormat); uint blkWidth = TextureFormatInfo.GetBlockWidth(surface.InternalFormat); uint blkHeight = TextureFormatInfo.GetBlockHeight(surface.InternalFormat); uint blkDepth = TextureFormatInfo.GetBlockDepth(surface.InternalFormat); uint blockHeight = GetBlockHeight(DivRoundUp((uint)surface.Height, blkHeight)); uint BlockHeightLog2 = (uint)Convert.ToString(blockHeight, 2).Length - 1; uint Pitch = 0; uint DataAlignment = 512; uint TileMode = 0; int linesPerBlockHeight = (1 << (int)BlockHeightLog2) * 8; //uint ArrayOffset = 0; for (int arrayLevel = 0; arrayLevel < surface.Arrays.Count; arrayLevel++) { uint SurfaceSize = 0; int blockHeightShift = 0; List <uint> MipOffsets = new List <uint>(); for (int mipLevel = 0; mipLevel < MipCount; mipLevel++) { uint width = (uint)Math.Max(1, surface.Width >> mipLevel); uint height = (uint)Math.Max(1, surface.Height >> mipLevel); uint depth = (uint)Math.Max(1, surface.Depth >> mipLevel); uint size = DivRoundUp(width, blkWidth) * DivRoundUp(height, blkHeight) * bpp; if (Pow2RoundUp(DivRoundUp(height, blkWidth)) < linesPerBlockHeight) { blockHeightShift += 1; } uint width__ = DivRoundUp(width, blkWidth); uint height__ = DivRoundUp(height, blkHeight); //Calculate the mip size instead byte[] AlignedData = new byte[(RoundUp(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] + surface.Arrays[arrayLevel].Mipmaps[mipLevel].Length - MipOffsets[mipLevel]) / surface.ArrayCount); //try { Pitch = RoundUp(width__ * bpp, 64); SurfaceSize += Pitch * RoundUp(height__, Math.Max(1, blockHeight >> blockHeightShift) * 8); //Console.WriteLine($"{width} {height} {blkWidth} {blkHeight} {target} {bpp} {TileMode} {(int)Math.Max(0, BlockHeightLog2 - blockHeightShift)}"); var mipData = surface.Arrays[arrayLevel].Mipmaps[mipLevel]; /*byte[] padded = new byte[mipData.Length * 2]; * Array.Copy(mipData, 0, padded, 0, mipData.Length); * mipData = padded;*/ byte[] result = Swizzle(width, height, depth, blkWidth, blkHeight, blkDepth, target, bpp, TileMode, (int)Math.Max(0, BlockHeightLog2 - blockHeightShift), mipData); //Console.WriteLine(result.Length + " " + surface.Mipmaps[mipLevel].Length); ImageData.AddRange(result); } /*catch (Exception e) * { * System.Windows.Forms.MessageBox.Show($"Failed to swizzle texture {surface.Name}!"); * Console.WriteLine(e); * * return new byte[0]; * }*/ } // alignment if (arrayLevel != surface.Arrays.Count - 1) { ImageData.AddRange(new byte[0x1000 - (ImageData.Count % 0x1000)]); } } return(ImageData.ToArray()); }