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 ReadNTP3(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.isDds = true; tex.pixelInternalFormat = PixelInternalFormat.Rgba32ui; int totalSize = d.ReadInt(); d.Skip(4); int dataSize = d.ReadInt(); int headerSize = d.ReadUShort(); d.Skip(2); //It might seem that mipmapCount and pixelFormat would be shorts, but they're bytes because they stay in the same place regardless of endianness d.Skip(1); byte mipmapCount = d.ReadByte(); d.Skip(1); tex.setPixelFormatFromNutFormat(d.ReadByte()); tex.Width = d.ReadUShort(); tex.Height = d.ReadUShort(); d.Skip(4); //0 in dds nuts (like NTP3) and 1 in gtx nuts; texture type? 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 = 0; if (Version < 0x0200) { dataOffset = headerPtr + headerSize; d.ReadInt(); } else if (Version >= 0x0200) { dataOffset = d.ReadInt() + headerPtr; } d.ReadInt(); d.ReadInt(); d.ReadInt(); //The size of a single cubemap face (discounting mipmaps). I don't know why it is repeated. If mipmaps are present, this is also specified in the mipSize section anyway. int cmapSize1 = 0; int cmapSize2 = 0; if (isCubemap) { cmapSize1 = d.ReadInt(); cmapSize2 = d.ReadInt(); d.Skip(8); } int[] mipSizes = new int[mipmapCount]; if (mipmapCount == 1) { if (isCubemap) { mipSizes[0] = cmapSize1; } else { mipSizes[0] = dataSize; } } else { for (byte mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) { mipSizes[mipLevel] = d.ReadInt(); } 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 for (byte surfaceLevel = 0; surfaceLevel < surfaceCount; ++surfaceLevel) { TextureSurface surface = new TextureSurface(); for (byte mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) { byte[] texArray = d.GetSection(dataOffset, mipSizes[mipLevel]); surface.mipmaps.Add(texArray); dataOffset += mipSizes[mipLevel]; } tex.surfaces.Add(surface); } if (tex.getNutFormat() == 14 || tex.getNutFormat() == 17) { tex.SwapChannelOrderUp(); } if (Version < 0x0200) { headerPtr += totalSize; } else if (Version >= 0x0200) { headerPtr += headerSize; } Nodes.Add(tex); } }