public void ReadNTWU(FileData d) { d.Seek(0x6); ushort count = d.ReadUShort(); d.Skip(0x8); int headerPtr = 0x10; for (ushort i = 0; i < count; ++i) { d.Seek(headerPtr); NutTexture tex = new NutTexture(); tex.pixelInternalFormat = PixelInternalFormat.Rgba32ui; int totalSize = d.ReadInt(); d.Skip(4); int dataSize = d.ReadInt(); int headerSize = d.ReadUShort(); d.Skip(2); d.Skip(1); byte mipmapCount = d.ReadByte(); d.Skip(1); tex.setPixelFormatFromNutFormat(d.ReadByte()); tex.Width = d.ReadUShort(); tex.Height = d.ReadUShort(); d.ReadInt(); //Always 1? uint caps2 = d.ReadUInt(); bool isCubemap = false; byte surfaceCount = 1; if ((caps2 & (uint)Dds.Ddscaps2.Cubemap) == (uint)Dds.Ddscaps2.Cubemap) { //Only supporting all six faces if ((caps2 & (uint)Dds.Ddscaps2.CubemapAllfaces) == (uint)Dds.Ddscaps2.CubemapAllfaces) { isCubemap = true; surfaceCount = 6; } else { throw new NotImplementedException($"Unsupported cubemap face amount for texture {i} with hash 0x{tex.HashId:X}. Six faces are required."); } } int dataOffset = d.ReadInt() + headerPtr; int mipDataOffset = d.ReadInt() + headerPtr; int gtxHeaderOffset = d.ReadInt() + headerPtr; d.ReadInt(); int cmapSize1 = 0; int cmapSize2 = 0; if (isCubemap) { cmapSize1 = d.ReadInt(); cmapSize2 = d.ReadInt(); d.Skip(8); } int imageSize = 0; //Total size of first mipmap of every surface int mipSize = 0; //Total size of mipmaps other than the first of every surface if (mipmapCount == 1) { if (isCubemap) { imageSize = cmapSize1; } else { imageSize = dataSize; } } else { imageSize = d.ReadInt(); mipSize = d.ReadInt(); d.Skip((mipmapCount - 2) * 4); d.Align(0x10); } d.Skip(0x10); //eXt data - always the same d.Skip(4); //GIDX d.ReadInt(); //Always 0x10 tex.HashId = d.ReadInt(); d.Skip(4); // padding align 8 d.Seek(gtxHeaderOffset); Gtx.Gx2Surface gtxHeader = new Gtx.Gx2Surface(); gtxHeader.dim = d.ReadInt(); gtxHeader.width = d.ReadInt(); gtxHeader.height = d.ReadInt(); gtxHeader.depth = d.ReadInt(); gtxHeader.numMips = d.ReadInt(); gtxHeader.format = d.ReadInt(); gtxHeader.aa = d.ReadInt(); gtxHeader.use = d.ReadInt(); gtxHeader.imageSize = d.ReadInt(); gtxHeader.imagePtr = d.ReadInt(); gtxHeader.mipSize = d.ReadInt(); gtxHeader.mipPtr = d.ReadInt(); gtxHeader.tileMode = d.ReadInt(); gtxHeader.swizzle = d.ReadInt(); gtxHeader.alignment = d.ReadInt(); gtxHeader.pitch = d.ReadInt(); //mipOffsets[0] is not in this list and is simply the start of the data (dataOffset) //mipOffsets[1] is relative to the start of the data (dataOffset + mipOffsets[1]) //Other mipOffsets are relative to mipOffset[1] (dataOffset + mipOffsets[1] + mipOffsets[i]) int[] mipOffsets = new int[mipmapCount]; mipOffsets[0] = 0; for (byte mipLevel = 1; mipLevel < mipmapCount; ++mipLevel) { mipOffsets[mipLevel] = 0; mipOffsets[mipLevel] = mipOffsets[1] + d.ReadInt(); } for (byte surfaceLevel = 0; surfaceLevel < surfaceCount; ++surfaceLevel) { tex.surfaces.Add(new TextureSurface()); } int w = tex.Width, h = tex.Height; for (byte mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) { int p = gtxHeader.pitch / (gtxHeader.width / w); int size; if (mipmapCount == 1) { size = imageSize; } else if (mipLevel + 1 == mipmapCount) { size = (mipSize + mipOffsets[1]) - mipOffsets[mipLevel]; } else { size = mipOffsets[mipLevel + 1] - mipOffsets[mipLevel]; } size /= surfaceCount; for (byte surfaceLevel = 0; surfaceLevel < surfaceCount; ++surfaceLevel) { gtxHeader.data = d.GetSection(dataOffset + mipOffsets[mipLevel] + (size * surfaceLevel), size); //Real size //Leave the below line commented for now because it breaks RGBA textures //size = ((w + 3) >> 2) * ((h + 3) >> 2) * (GTX.getBPP(gtxHeader.format) / 8); if (size < (Gtx.GetBpp(gtxHeader.format) / 8)) { size = (Gtx.GetBpp(gtxHeader.format) / 8); } byte[] deswiz = Gtx.SwizzleBc( gtxHeader.data, w, h, gtxHeader.format, gtxHeader.tileMode, p, gtxHeader.swizzle ); tex.surfaces[surfaceLevel].mipmaps.Add(new FileData(deswiz).GetSection(0, size)); } w /= 2; h /= 2; if (w < 1) { w = 1; } if (h < 1) { h = 1; } } headerPtr += headerSize; Nodes.Add(tex); } }
public void ReadFTEX(Texture tex) { ImageKey = "texture"; SelectedImageKey = "texture"; reserve = tex.Data; texture.width = (int)tex.Width; texture.height = (int)tex.Height; format = (int)tex.Format; int swizzle = (int)tex.Swizzle; int pitch = (int)tex.Pitch; texture.data = Gtx.SwizzleBc(tex.Data, texture.width, texture.height, format, (int)tex.TileMode, pitch, swizzle); Text = tex.Name; //Setup variables for treenode data width = texture.width; height = texture.height; texture.mipMapCount = (int)tex.MipCount; FileData f = new FileData(tex.MipData); for (int level = 0; level < tex.MipCount; level++) { if (level != 0) { } // byte[] mip = f.getSection((int)tex.MipOffsets[level - 1], (int)tex.MipOffsets[level + 1]); // texture.mipMapData.Add(mip); } switch (format) { case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTBc1Unorm): texture.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt1Ext; break; case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTBc1Srgb): texture.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt1Ext; break; case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTBc2Unorm): texture.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt3Ext; break; case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTBc2Srgb): texture.pixelInternalFormat = PixelInternalFormat.CompressedSrgbAlphaS3tcDxt3Ext; break; case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTBc3Unorm): texture.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt5Ext; break; case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTBc3Srgb): texture.pixelInternalFormat = PixelInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext; break; case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTBc4Unorm): texture.pixelInternalFormat = PixelInternalFormat.CompressedRedRgtc1; break; case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTBc4Snorm): texture.pixelInternalFormat = PixelInternalFormat.CompressedSignedRedRgtc1; break; case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTBc5Unorm): texture.pixelInternalFormat = PixelInternalFormat.CompressedRgRgtc2; break; case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTBc5Snorm): //OpenTK doesn't load BC5 SNORM textures right so I'll use the same decompress method bntx has byte[] fixBC5 = DDS_Decompress.DecompressBC5(texture.data, texture.width, texture.height, true); texture.data = fixBC5; texture.pixelInternalFormat = PixelInternalFormat.Rgba; texture.pixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Rgba; break; case ((int)Gtx.Gx2SurfaceFormat.Gx2SurfaceFormatTcsR8G8B8A8Unorm): texture.pixelInternalFormat = PixelInternalFormat.Rgba; texture.pixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Rgba; break; } }