public override void Open() { using (BinaryReader reader = new BinaryReader(new FileStream(AbsolutePath, FileMode.Open))) { Mipmaps = new List <byte[]>(); // TODO: Why are there empty streams? if (reader.BaseStream.Length == 0) { return; } reader.BaseStream.Position = reader.BaseStream.Length - 0xB0; int[] mipmapSizes = new int[16]; for (int i = 0; i < mipmapSizes.Length; i++) { mipmapSizes[i] = reader.ReadInt32(); } reader.ReadChars(4); // TNX magic TexName = ReadTexName(reader); Width = reader.ReadInt32(); Height = reader.ReadInt32(); Depth = reader.ReadInt32(); Format = (NUTEX_FORMAT)reader.ReadByte(); reader.ReadByte(); ushort Padding = reader.ReadUInt16(); reader.ReadUInt32(); int MipCount = reader.ReadInt32(); int Alignment = reader.ReadInt32(); int ArrayCount = reader.ReadInt32(); int ImageSize = reader.ReadInt32(); char[] Magic = reader.ReadChars(4); int MajorVersion = reader.ReadInt16(); int MinorVersion = reader.ReadInt16(); uint blkWidth = (uint)blkDims[Format].X; uint blkHeight = (uint)blkDims[Format].Y; uint blockHeight = SwitchSwizzler.GetBlockHeight(SwitchSwizzler.DivRoundUp((uint)Height, blkHeight)); uint BlockHeightLog2 = (uint)Convert.ToString(blockHeight, 2).Length - 1; uint tileMode = 0; uint bpp = GetBpps(Format); reader.BaseStream.Position = 0; int blockHeightShift = 0; for (int i = 0; i < 1; i++) { int size = mipmapSizes[i]; if (i == 0 && size % Alignment != 0) { size += Alignment - (size % Alignment); } byte[] deswiz = SwitchSwizzler.Deswizzle((uint)Width, (uint)Height, blkWidth, blkHeight, 0, bpp, tileMode, (int)Math.Max(0, BlockHeightLog2 - blockHeightShift), reader.ReadBytes(ImageSize)); byte[] trimmed = new byte[mipmapSizes[0]]; Array.Copy(deswiz, 0, trimmed, 0, trimmed.Length); Mipmaps.Add(trimmed); } } }
private byte[] Deswizzle(uint width, uint height, uint numMipmaps, byte[] image) { uint bpp = 8; uint blkWidth = 4; uint blkHeight = 4; uint dataAlignment = 512; uint blockHeight = SwitchSwizzler.GetBlockHeight(SwitchSwizzler.DivRoundUp(height, blkHeight)); uint blockHeightLog2 = (uint)System.Convert.ToString(blockHeight, 2).Length - 1; int linesPerBlockHeight = (1 << (int)blockHeightLog2) * 8; uint totalSize = 0; for (var i = 0; i < numMipmaps; i++) { uint mipmapWidth = Math.Max(1, width >> i); uint mipmapHeight = Math.Max(1, height >> i); uint mipmapSize = SwitchSwizzler.DivRoundUp(mipmapWidth, blkWidth) * SwitchSwizzler.DivRoundUp(mipmapHeight, blkHeight) * bpp; totalSize += mipmapSize; } var result = new byte[totalSize]; uint surfaceSize = 0; var blockHeightShift = 0; List <uint> mipOffsets = new List <uint>(); uint resultOffset = 0; for (var i = 0; i < numMipmaps; i++) { uint mipmapWidth = Math.Max(1, width >> i); uint mipmapHeight = Math.Max(1, height >> i); uint mipmapSize = SwitchSwizzler.DivRoundUp(mipmapWidth, blkWidth) * SwitchSwizzler.DivRoundUp(mipmapHeight, blkHeight) * bpp; if (SwitchSwizzler.Pow2RoundUp(SwitchSwizzler.DivRoundUp(mipmapHeight, blkWidth)) < linesPerBlockHeight) { blockHeightShift += 1; } uint roundWidth = SwitchSwizzler.DivRoundUp(mipmapWidth, blkWidth); uint roundHeight = SwitchSwizzler.DivRoundUp(mipmapHeight, blkHeight); surfaceSize += SwitchSwizzler.RoundUp(surfaceSize, dataAlignment) - surfaceSize; mipOffsets.Add(surfaceSize); var msize = (int)(mipOffsets[0] + image.Length - mipOffsets[i]); var mipmap = new byte[msize]; Array.Copy(image, mipOffsets[i], mipmap, 0, msize); var info = new SwizzleInfo { Width = mipmapWidth, Height = mipmapHeight, Depth = 1, BlkWidth = blkWidth, BlkHeight = blkHeight, BlkDepth = 1, RoundPitch = 1, Bpp = bpp, TileMode = 0, BlockHeightLog2 = (int)Math.Max(0, blockHeightLog2 - blockHeightShift), }; uint pitch = SwitchSwizzler.RoundUp(roundWidth * bpp, 64); surfaceSize += pitch * SwitchSwizzler.RoundUp(roundHeight, Math.Max(1, blockHeight >> blockHeightShift) * 8); byte[] deswizzled = SwitchSwizzler.Deswizzle(info, mipmap); Array.Copy(deswizzled, 0, result, resultOffset, mipmapSize); resultOffset += mipmapSize; } return(result); }
/// <summary> /// Write the swizzled mipmaps to a DataStream. /// </summary> /// <param name="height">Image height (in pixels).</param> /// <param name="width">Image width (in pixels).</param> /// <param name="mipmaps">The list of mipmaps.</param> /// <returns>The DataStream.</returns> protected override DataStream Write(uint height, uint width, List <byte[]> mipmaps) { if (mipmaps == null) { throw new ArgumentNullException(nameof(mipmaps)); } DataStream outputDataStream = DataStreamFactory.FromMemory(); var writer = new DataWriter(outputDataStream) { DefaultEncoding = Encoding.ASCII, Endianness = EndiannessMode.LittleEndian, }; writer.Write(0x20L); writer.Write(0x10L); writer.Write(0x20L); writer.Write(0x00L); // Size writer.Write(0x00L); writer.Write(0x00L); writer.Write(0x20L); writer.Write(0x18L); writer.Write(0x28L); writer.Write(0x00L); // Size - 0x38 writer.Write(width); writer.Write(height); writer.Write(0x01); writer.Write(0x01); writer.Write(0x49); writer.Write(mipmaps.Count); var blockHeightShift = 0; const uint blkWidth = 4; const uint blkHeight = 4; const uint bpp = 8; uint blockHeight = SwitchSwizzler.GetBlockHeight(SwitchSwizzler.DivRoundUp(height, blkHeight)); uint blockHeightLog2 = (uint)System.Convert.ToString(blockHeight, 2).Length - 1; uint linesPerBlockHeight = blockHeight * 8; for (var mipLevel = 0; mipLevel < mipmaps.Count; mipLevel++) { byte[] mipmap = mipmaps[mipLevel]; uint mipmapWidth = Math.Max(1, width >> mipLevel); uint mipmapHeight = Math.Max(1, height >> mipLevel); uint roundedHeight = SwitchSwizzler.DivRoundUp(mipmapHeight, blkHeight); if (SwitchSwizzler.Pow2RoundUp(roundedHeight) < linesPerBlockHeight) { blockHeightShift += 1; } var info = new SwizzleInfo { Width = mipmapWidth, Height = mipmapHeight, Depth = 1, BlkWidth = blkWidth, BlkHeight = blkHeight, BlkDepth = 1, RoundPitch = 1, Bpp = bpp, TileMode = 0, BlockHeightLog2 = (int)Math.Max(0, blockHeightLog2 - blockHeightShift), }; byte[] swizzled = SwitchSwizzler.Swizzle(info, mipmap); writer.Write(swizzled); } writer.Stream.Seek(0x18, System.IO.SeekOrigin.Begin); writer.Write(outputDataStream.Length - 0x30); writer.Stream.Seek(0x48, System.IO.SeekOrigin.Begin); writer.Write(outputDataStream.Length - 0x68); return(outputDataStream); }