public static byte[] Decompress(byte[] Input, int Width, int Height, bool Alpha) { byte[] Output = new byte[Width * Height * 4]; byte[] comp = new byte[4] { 0xFF, 0xFF, 0xFF, 0xFF }; int bpp = (int)STGenericTexture.GetBytesPerPixel(TEX_FORMAT.R4G4_UNORM); for (int Y = 0; Y < Height; Y++) { for (int X = 0; X < Width; X++) { int InputOffset = (Y * Width + X) * bpp; int OutputOffset = (Y * Width + X) * 4; int pixel = 0; for (int i = 0; i < bpp; i++) { pixel |= Input[InputOffset + i] << (8 * i); } comp[0] = (byte)((pixel & 0xF) * 17); comp[1] = (byte)(((pixel & 0xF0) >> 4) * 17); Output[OutputOffset + 0] = comp[0]; Output[OutputOffset + 1] = comp[1]; Output[OutputOffset + 2] = comp[2]; Output[OutputOffset + 3] = comp[3]; } } return(Output); }
//Method from https://github.com/aboood40091/BNTX-Editor/blob/master/formConv.py public static byte[] Decode(byte[] data, int width, int height, TEX_FORMAT format) { uint bpp = STGenericTexture.GetBytesPerPixel(format); int size = width * height * 4; bpp = (uint)(data.Length / (width * height)); byte[] output = new byte[size]; int inPos = 0; int outPos = 0; byte[] comp = new byte[] { 0, 0, 0, 0xFF, 0, 0xFF }; byte[] compSel = new byte[4] { 0, 1, 2, 3 }; if (format == TEX_FORMAT.LA8) { compSel = new byte[4] { 0, 0, 0, 1 }; bpp = 2; } else if (format == TEX_FORMAT.L8) { compSel = new byte[4] { 0, 0, 0, 5 } } ; for (int Y = 0; Y < height; Y++) { for (int X = 0; X < width; X++) { inPos = (Y * width + X) * (int)bpp; outPos = (Y * width + X) * 4; int pixel = 0; for (int i = 0; i < bpp; i++) { pixel |= data[inPos + i] << (8 * i); } comp = GetComponentsFromPixel(format, pixel, comp); output[outPos + 3] = comp[compSel[3]]; output[outPos + 2] = comp[compSel[2]]; output[outPos + 1] = comp[compSel[1]]; output[outPos + 0] = comp[compSel[0]]; } } return(output); } }
public static List <uint[]> GenerateMipSizes(TEX_FORMAT Format, uint Width, uint Height, uint Depth, uint SurfaceCount, uint MipCount, uint ImageSize) { List <uint[]> MipMapSizes = new List <uint[]>(); uint bpp = STGenericTexture.GetBytesPerPixel(Format); uint blkWidth = STGenericTexture.GetBlockWidth(Format); uint blkHeight = STGenericTexture.GetBlockHeight(Format); uint blkDepth = STGenericTexture.GetBlockDepth(Format); uint blockHeight = GetBlockHeight(DIV_ROUND_UP(Height, blkHeight)); uint BlockHeightLog2 = (uint)Convert.ToString(blockHeight, 2).Length - 1; uint Pitch = 0; uint DataAlignment = 512; int linesPerBlockHeight = (1 << (int)BlockHeightLog2) * 8; uint ArrayCount = SurfaceCount; uint ArrayOffset = 0; for (int arrayLevel = 0; arrayLevel < ArrayCount; arrayLevel++) { uint SurfaceSize = 0; int blockHeightShift = 0; uint[] MipOffsets = new uint[MipCount]; for (int mipLevel = 0; mipLevel < MipCount; mipLevel++) { uint width = (uint)Math.Max(1, Width >> mipLevel); uint height = (uint)Math.Max(1, Height >> mipLevel); uint depth = (uint)Math.Max(1, Depth >> mipLevel); uint size = DIV_ROUND_UP(width, blkWidth) * DIV_ROUND_UP(height, blkHeight) * bpp; MipOffsets[mipLevel] = size; } ArrayOffset += (uint)(ImageSize / ArrayCount); MipMapSizes.Add(MipOffsets); } return(MipMapSizes); }
public static byte[] GetImageData(STGenericTexture texture, byte[] ImageData, int ArrayLevel, int MipLevel, int DepthLevel, uint BlockHeightLog2, int target = 1, bool LinearTileMode = false) { 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 Pitch = 0; uint DataAlignment = 512; uint TileMode = 0; if (LinearTileMode) { TileMode = 1; } uint numDepth = 1; if (texture.Depth > 1) { numDepth = texture.Depth; } int linesPerBlockHeight = (1 << (int)BlockHeightLog2) * 8; uint ArrayOffset = 0; for (int depthLevel = 0; depthLevel < numDepth; depthLevel++) { 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; Console.WriteLine($"size " + size); 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); byte[] result = TegraX1Swizzle.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 && DepthLevel == depthLevel) { 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]); }
public static byte[] GetImageData(STGenericTexture texture, byte[] ImageData, int ArrayLevel, int MipLevel, int DepthLevel, uint BlockHeightLog2, int target = 1, bool LinearTileMode = false) { 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 TileMode = 0; if (LinearTileMode) { TileMode = 1; } uint numDepth = 1; if (texture.Depth > 1) { numDepth = texture.Depth; } var blockHeightMip0 = GetBlockHeight(DIV_ROUND_UP(texture.Height, blkHeight)); uint arrayOffset = 0; // TODO: Why is depth done like this? for (int depthLevel = 0; depthLevel < numDepth; depthLevel++) { for (int arrayLevel = 0; arrayLevel < texture.ArrayCount; arrayLevel++) { var mipOffset = 0u; for (int mipLevel = 0; mipLevel < texture.MipCount; mipLevel++) { uint width = Math.Max(1, texture.Width >> mipLevel); uint height = Math.Max(1, texture.Height >> mipLevel); uint depth = Math.Max(1, texture.Depth >> mipLevel); uint widthInBlocks = DIV_ROUND_UP(width, blkWidth); uint heightInBlocks = DIV_ROUND_UP(height, blkHeight); uint depthInBlocks = DIV_ROUND_UP(depth, blkDepth); // tegra_swizzle only allows block heights supported by the TRM (1,2,4,8,16,32). var mipBlockHeightLog2 = (int)Math.Log(GetMipBlockHeight(heightInBlocks, blockHeightMip0), 2); var mipBlockHeight = 1 << Math.Max(Math.Min(mipBlockHeightLog2, 5), 0); uint mipSize; if (Environment.Is64BitProcess) { mipSize = (uint)GetSurfaceSizeX64(widthInBlocks, heightInBlocks, depthInBlocks, (ulong)mipBlockHeight, bpp); } else { mipSize = (uint)GetSurfaceSizeX86(widthInBlocks, heightInBlocks, depthInBlocks, (uint)mipBlockHeight, bpp); } // TODO: Avoid this copy. byte[] mipData = Utils.SubArray(ImageData, arrayOffset + mipOffset, mipSize); try { if (ArrayLevel == arrayLevel && MipLevel == mipLevel && DepthLevel == depthLevel) { // The set of swizzled addresses is at least as big as the set of linear addresses. // When defined appropriately, we only require a single memory allocation for the output. byte[] result = deswizzle(width, height, depth, blkWidth, blkHeight, blkDepth, target, bpp, TileMode, mipBlockHeightLog2, mipData); return(result); } } catch (Exception e) { System.Windows.Forms.MessageBox.Show($"Failed to swizzle texture {texture.Text}!"); Console.WriteLine(e); return(new byte[0]); } mipOffset += mipSize; } arrayOffset += (uint)(ImageData.Length / texture.ArrayCount); } } return(new byte[0]); }
public static List <uint[]> GenerateMipSizes(TEX_FORMAT Format, uint Width, uint Height, uint Depth, uint SurfaceCount, uint MipCount, uint ImageSize) { List <uint[]> MipMapSizes = new List <uint[]>(); uint bpp = STGenericTexture.GetBytesPerPixel(Format); uint blkWidth = STGenericTexture.GetBlockWidth(Format); uint blkHeight = STGenericTexture.GetBlockHeight(Format); uint blkDepth = STGenericTexture.GetBlockDepth(Format); uint blockHeight = TegraX1Swizzle.GetBlockHeight(TegraX1Swizzle.DIV_ROUND_UP(Height, blkHeight)); uint BlockHeightLog2 = (uint)Convert.ToString(blockHeight, 2).Length - 1; uint Pitch = 0; uint DataAlignment = 512; int linesPerBlockHeight = (1 << (int)BlockHeightLog2) * 8; uint ArrayCount = SurfaceCount; uint ArrayOffset = 0; for (int arrayLevel = 0; arrayLevel < ArrayCount; arrayLevel++) { uint SurfaceSize = 0; int blockHeightShift = 0; uint[] MipOffsets = new uint[MipCount]; for (int mipLevel = 0; mipLevel < MipCount; mipLevel++) { uint width = (uint)Math.Max(1, Width >> mipLevel); uint height = (uint)Math.Max(1, Height >> mipLevel); uint depth = (uint)Math.Max(1, 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[mipLevel] = (SurfaceSize); //Get the first mip offset and current one and the total image size int msize = (int)((MipOffsets[0] + ImageSize - MipOffsets[mipLevel]) / ArrayCount); Pitch = TegraX1Swizzle.round_up(width__ * bpp, 64); SurfaceSize += Pitch * TegraX1Swizzle.round_up(height__, Math.Max(1, blockHeight >> blockHeightShift) * 8); } ArrayOffset += (uint)(ImageSize / ArrayCount); MipMapSizes.Add(MipOffsets); } return(MipMapSizes); }
public static List <byte[]> SwizzleSurfaceMipMaps(Texture tex, byte[] data, uint MipCount) { var TexFormat = TextureData.ConvertFormat(tex.Format); int blockHeightShift = 0; int target = 1; uint Pitch = 0; uint SurfaceSize = 0; uint blockHeight = 0; uint blkWidth = STGenericTexture.GetBlockWidth(TexFormat); uint blkHeight = STGenericTexture.GetBlockHeight(TexFormat); uint blkDepth = STGenericTexture.GetBlockDepth(TexFormat); uint bpp = STGenericTexture.GetBytesPerPixel(TexFormat); uint linesPerBlockHeight = 0; if (tex.TileMode == TileMode.LinearAligned) { blockHeight = 1; tex.BlockHeightLog2 = 0; tex.Alignment = 1; linesPerBlockHeight = 1; tex.ReadTextureLayout = 0; } else { blockHeight = TegraX1Swizzle.GetBlockHeight(DIV_ROUND_UP(tex.Height, blkHeight)); tex.BlockHeightLog2 = (uint)Convert.ToString(blockHeight, 2).Length - 1; tex.Alignment = 512; tex.ReadTextureLayout = 1; linesPerBlockHeight = blockHeight * 8; } List <byte[]> mipmaps = new List <byte[]>(); for (int mipLevel = 0; mipLevel < MipCount; mipLevel++) { var result = TextureHelper.GetCurrentMipSize(tex.Width, tex.Height, blkWidth, blkHeight, bpp, mipLevel); uint offset = result.Item1; uint size = result.Item2; byte[] data_ = Utils.SubArray(data, offset, size); uint width_ = Math.Max(1, tex.Width >> mipLevel); uint height_ = Math.Max(1, tex.Height >> mipLevel); uint depth_ = Math.Max(1, tex.Depth >> mipLevel); uint width__ = DIV_ROUND_UP(width_, blkWidth); uint height__ = DIV_ROUND_UP(height_, blkHeight); uint depth__ = DIV_ROUND_UP(depth_, blkDepth); byte[] AlignedData = new byte[(TegraX1Swizzle.round_up(SurfaceSize, (uint)tex.Alignment) - SurfaceSize)]; SurfaceSize += (uint)AlignedData.Length; // Console.WriteLine("SurfaceSize Aligned " + AlignedData); Console.WriteLine("MipOffsets " + SurfaceSize); Console.WriteLine("size " + size); tex.MipOffsets[mipLevel] = SurfaceSize; if (tex.TileMode == TileMode.LinearAligned) { Pitch = width__ * bpp; if (target == 1) { Pitch = TegraX1Swizzle.round_up(width__ * bpp, 32); } SurfaceSize += Pitch * height__; } else { if (TegraX1Swizzle.pow2_round_up(height__) < linesPerBlockHeight) { blockHeightShift += 1; } Pitch = TegraX1Swizzle.round_up(width__ * bpp, 64); SurfaceSize += Pitch * TegraX1Swizzle.round_up(height__, Math.Max(1, blockHeight >> blockHeightShift) * 8); } byte[] SwizzledData = TegraX1Swizzle.swizzle(width_, height_, depth_, blkWidth, blkHeight, blkDepth, target, bpp, (uint)tex.TileMode, (int)Math.Max(0, tex.BlockHeightLog2 - blockHeightShift), data_); mipmaps.Add(AlignedData.Concat(SwizzledData).ToArray()); } tex.ImageSize = SurfaceSize; return(mipmaps); }
//Parse info help from //https://github.com/Joschuka/fmt_g1m/blob/master/Noesis/plugins/python/fmt_g1m.py public void Read(FileReader reader) { reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian; long StartPos = reader.Position; string Magic = reader.ReadString(4); if (Magic == "GT1G") { reader.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian; } else { reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian; } IsBigEndian = reader.IsBigEndian; if (Magic == "GT1G") { Platform = GT1Platform.Switch; } else { Platform = GT1Platform.WiiU; } string Version = reader.ReadString(4); uint FileSize = reader.ReadUInt32(); uint DataOffset = reader.ReadUInt32(); uint TextureCount = reader.ReadUInt32(); PlatformID = reader.ReadUInt32(); uint unk2 = reader.ReadUInt32(); uint[] unk3s = reader.ReadUInt32s((int)TextureCount); for (int i = 0; i < TextureCount; i++) { reader.SeekBegin(StartPos + DataOffset + (i * 4)); uint InfoOffset = reader.ReadUInt32(); reader.SeekBegin(DataOffset + InfoOffset); byte mipSys = reader.ReadByte(); byte format = reader.ReadByte(); byte texDims = reader.ReadByte(); byte unknown3 = reader.ReadByte(); //1 byte unknown4 = reader.ReadByte(); //0 byte unknown5 = reader.ReadByte(); //1 reader.ReadByte(); byte flags = reader.ReadByte(); if (reader.ByteOrder == Syroot.BinaryData.ByteOrder.LittleEndian) { mipSys = SwapEndianByte(mipSys); texDims = SwapEndianByte(texDims); } uint Width = (uint)Math.Pow(2, texDims / 16); uint Height = (uint)Math.Pow(2, texDims % 16); if (flags > 0) { var extSize = reader.ReadInt32(); if (extSize < 0xC || extSize > 0x14) { throw new Exception("Extra texture data is not between 0xC and 0x14 Bytes!!"); } uint param1 = reader.ReadUInt32(); uint param2 = reader.ReadUInt32(); if (extSize >= 0x10) { Width = reader.ReadUInt32(); } if (extSize >= 0x14) { Height = reader.ReadUInt32(); } } GITextureWrapper tex = new GITextureWrapper(this); tex.ImageKey = "texture"; tex.SelectedImageKey = tex.ImageKey; tex.Text = $"Texture_{i}"; tex.Width = Width; tex.Height = Height; tex.MipCount = (uint)mipSys >> 4; uint texSys = (uint)mipSys & 0xF; uint textureSize = 0; uint mortonWidth = 0; switch (format) { case 0x00: //ABGR case 0x01: //BGRA 32 bit (no mip maps) case 0x09: tex.Format = TEX_FORMAT.R8G8B8A8_UNORM; break; case 0x34: tex.Format = TEX_FORMAT.RGB565; textureSize = Width * Height * 2; break; case 0x02: case 0x06: case 0x3C: case 0x3D: case 0x59: case 0x60: //Swizzled tex.Format = TEX_FORMAT.BC1_UNORM; break; case 0x10: tex.Format = TEX_FORMAT.BC1_UNORM; mortonWidth = 0x4; break; case 0x12: tex.Format = TEX_FORMAT.BC3_UNORM; mortonWidth = 0x8; break; case 0x36: tex.Format = TEX_FORMAT.B4G4R4A4_UNORM; textureSize = Width * Height * 2; break; case 0x3: case 0x7: case 0x8: case 0x5B: case 0x62: //bc1 swizzled tex.Format = TEX_FORMAT.BC3_UNORM; break; case 0x5C: tex.Format = TEX_FORMAT.BC4_UNORM; break; case 0x5D: //DXT5 swizzled or ATI2 tex.Format = TEX_FORMAT.BC5_UNORM; break; case 0x5E: tex.Format = TEX_FORMAT.BC6H_UF16; //Uses cubemaps break; case 0x5F: tex.Format = TEX_FORMAT.BC7_UNORM; break; case 0xF: textureSize = Width * Height; tex.Format = TEX_FORMAT.A8_UNORM; break; case 0x56: tex.Format = TEX_FORMAT.ETC1_UNORM; textureSize = Width * Height % 2; break; case 0x6F: tex.Format = TEX_FORMAT.ETC1_UNORM; textureSize = Width * Height; break; default: throw new Exception("Unsupported format! " + format.ToString("x")); } if (textureSize == 0) { textureSize = (Width * Height * STGenericTexture.GetBytesPerPixel(tex.Format)) / 8; } if (format == 0x09) { textureSize = (Width * Height * 64) / 8; } if (format == 0x01) { textureSize = (Width * Height * 32) / 8; tex.Parameters.DontSwapRG = true; } tex.ImageData = reader.ReadBytes((int)textureSize); Textures.Add(tex); } }
List <byte[]> SwizzleSurfaceMipMaps(STGenericTexture tex, byte[] data, uint MipCount) { int blockHeightShift = 0; int target = 1; uint Pitch = 0; uint SurfaceSize = 0; uint blockHeight = 0; uint blkWidth = tex.GetBlockWidth(); uint blkHeight = tex.GetBlockHeight(); uint blkDepth = tex.GetBlockDepth(); uint bpp = tex.GetBytesPerPixel(); MipOffsets = new uint[MipCount]; uint linesPerBlockHeight = 0; if (LinearMode) { blockHeight = 1; BlockHeightLog2 = 0; Alignment = 1; linesPerBlockHeight = 1; ReadTextureLayout = 0; } else { blockHeight = TegraX1Swizzle.GetBlockHeight(TegraX1Swizzle.DIV_ROUND_UP(tex.Height, blkHeight)); BlockHeightLog2 = (uint)Convert.ToString(blockHeight, 2).Length - 1; Alignment = 512; ReadTextureLayout = 1; linesPerBlockHeight = blockHeight * 8; } List <byte[]> mipmaps = new List <byte[]>(); for (int mipLevel = 0; mipLevel < MipCount; mipLevel++) { var result = WiiU.TextureHelper.GetCurrentMipSize(tex.Width, tex.Height, blkWidth, blkHeight, bpp, mipLevel); uint offset = result.Item1; uint size = result.Item2; byte[] data_ = ByteUtils.SubArray(data, offset, size); uint width_ = Math.Max(1, tex.Width >> mipLevel); uint height_ = Math.Max(1, tex.Height >> mipLevel); uint depth_ = Math.Max(1, tex.Depth >> mipLevel); uint width__ = TegraX1Swizzle.DIV_ROUND_UP(width_, blkWidth); uint height__ = TegraX1Swizzle.DIV_ROUND_UP(height_, blkHeight); uint depth__ = TegraX1Swizzle.DIV_ROUND_UP(depth_, blkDepth); byte[] AlignedData = new byte[(TegraX1Swizzle.round_up(SurfaceSize, Alignment) - SurfaceSize)]; SurfaceSize += (uint)AlignedData.Length; ImageSize = 0; MipOffsets[mipLevel] = SurfaceSize; if (LinearMode) { Pitch = width__ * bpp; if (target == 1) { Pitch = TegraX1Swizzle.round_up(width__ * bpp, 32); } SurfaceSize += Pitch * height__; } else { if (TegraX1Swizzle.pow2_round_up(height__) < linesPerBlockHeight) { blockHeightShift += 1; } Pitch = TegraX1Swizzle.round_up(width__ * bpp, 64); SurfaceSize += Pitch * TegraX1Swizzle.round_up(height__, Math.Max(1, blockHeight >> blockHeightShift) * 8); } byte[] SwizzledData = TegraX1Swizzle.swizzle(width_, height_, depth_, blkWidth, blkHeight, blkDepth, target, bpp, (uint)TileMode, (int)Math.Max(0, BlockHeightLog2 - blockHeightShift), data_); mipmaps.Add(AlignedData.Concat(SwizzledData).ToArray()); } ImageSize = SurfaceSize; return(mipmaps); }
public void Read(FileReader reader) { reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian; Text = FileName; long StartPos = reader.Position; string Magic = reader.ReadString(4); if (Magic == "GT1G") { reader.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian; } else { reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian; } if (Magic == "GT1G") { Platform = GT1Platform.Switch; } else { Platform = GT1Platform.WiiU; } string Version = reader.ReadString(4); uint FileSize = reader.ReadUInt32(); uint DataOffset = reader.ReadUInt32(); uint TextureCount = reader.ReadUInt32(); uint unk = reader.ReadUInt32(); uint unk2 = reader.ReadUInt32(); uint[] unk3s = reader.ReadUInt32s((int)TextureCount); for (int i = 0; i < TextureCount; i++) { reader.SeekBegin(StartPos + DataOffset + (i * 4)); uint InfoOffset = reader.ReadUInt32(); reader.SeekBegin(DataOffset + InfoOffset); byte numMips = reader.ReadByte(); byte format = reader.ReadByte(); byte texDims = reader.ReadByte(); byte unknown3 = reader.ReadByte(); //1 byte unknown4 = reader.ReadByte(); //0 byte unknown5 = reader.ReadByte(); //1 byte unknown6 = reader.ReadByte(); //12 byte extraHeader = reader.ReadByte(); if (reader.ByteOrder == Syroot.BinaryData.ByteOrder.LittleEndian) { numMips = SwapEndianByte(numMips); texDims = SwapEndianByte(texDims); extraHeader = SwapEndianByte(extraHeader); } if (extraHeader == 1) { var extSize = reader.ReadInt32(); var ext = reader.ReadBytes(extSize - 4); } uint Width = (uint)Math.Pow(2, texDims / 16); uint Height = (uint)Math.Pow(2, texDims % 16); GITexture tex = new GITexture(this); tex.ImageKey = "texture"; tex.SelectedImageKey = tex.ImageKey; tex.Text = $"Texture {i} {format.ToString("x")}"; tex.Width = Width; tex.Height = Height; tex.MipCount = numMips; switch (format) { case 0x00: //ABGR case 0x01: //BGRA 32 bit (no mip maps) case 0x02: //RGBA 32 bit case 0x09: tex.Format = TEX_FORMAT.R8G8B8A8_UNORM; break; case 0x06: case 0x10: //PC and xbox (swizzled) case 0x59: case 0x60: //Swizzled tex.Format = TEX_FORMAT.BC1_UNORM; break; case 0x7: case 0x8: case 0x12: //PC and xbox (swizzled) case 0x5B: case 0x62: //bc1 swizzled tex.Format = TEX_FORMAT.BC3_UNORM; break; case 0x5C: tex.Format = TEX_FORMAT.BC4_UNORM; break; case 0x5D: //DXT5 swizzled or ATI2 tex.Format = TEX_FORMAT.BC5_UNORM; break; case 0x5E: tex.Format = TEX_FORMAT.BC6H_UF16; //Uses cubemaps break; case 0x5F: tex.Format = TEX_FORMAT.BC7_UNORM; break; default: throw new Exception("Unsupported format! " + format.ToString("x")); } uint textureSize = (Width * Height * STGenericTexture.GetBytesPerPixel(tex.Format)) / 8; if (format == 0x09) { textureSize = (Width * Height * 64) / 8; } if (format == 0x01) { textureSize = (Width * Height * 32) / 8; tex.Parameters.DontSwapRG = true; } tex.ImageData = reader.ReadBytes((int)textureSize); Nodes.Add(tex); } }