public NutTexture ToNutTexture() { NutTexture tex = new NutTexture(); tex.isDds = true; tex.HashId = 0x48415348; tex.Height = (int)header.height; tex.Width = (int)header.width; byte surfaceCount = 1; bool isCubemap = (header.caps2 & (uint)DDSCAPS2.CUBEMAP) == (uint)DDSCAPS2.CUBEMAP; if (isCubemap) { if ((header.caps2 & (uint)DDSCAPS2.CUBEMAP_ALLFACES) == (uint)DDSCAPS2.CUBEMAP_ALLFACES) { surfaceCount = 6; } else { throw new NotImplementedException($"Unsupported cubemap face amount for texture. Six faces are required."); } } bool isBlock = true; switch (header.ddspf.fourCC) { case 0x00000000: //RGBA isBlock = false; tex.pixelInternalFormat = PixelInternalFormat.Rgba; tex.pixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Rgba; break; case 0x31545844: //DXT1 tex.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt1Ext; break; case 0x33545844: //DXT3 tex.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt3Ext; break; case 0x35545844: //DXT5 tex.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt5Ext; break; case 0x31495441: //ATI1 case 0x55344342: //BC4U tex.pixelInternalFormat = PixelInternalFormat.CompressedRedRgtc1; break; case 0x32495441: //ATI2 case 0x55354342: //BC5U tex.pixelInternalFormat = PixelInternalFormat.CompressedRgRgtc2; break; default: MessageBox.Show("Unsupported DDS format - 0x" + header.ddspf.fourCC.ToString("x")); break; } uint formatSize = getFormatSize(header.ddspf.fourCC); FileData d = new FileData(bdata); if (header.mipmapCount == 0) { header.mipmapCount = 1; } uint off = 0; for (byte i = 0; i < surfaceCount; ++i) { TextureSurface surface = new TextureSurface(); uint w = header.width, h = header.height; for (int j = 0; j < header.mipmapCount; ++j) { //If texture is DXT5 and isn't square, limit the mipmaps to an amount such that width and height are each always >= 4 if (tex.pixelInternalFormat == PixelInternalFormat.CompressedRgbaS3tcDxt5Ext && tex.Width != tex.Height && (w < 4 || h < 4)) { break; } uint s = (w * h); //Total pixels if (isBlock) { s = (uint)(s * ((float)formatSize / 0x10)); //Bytes per 16 pixels if (s < formatSize) //Make sure it's at least one block { s = formatSize; } } else { s = (uint)(s * (formatSize)); //Bytes per pixel } w /= 2; h /= 2; surface.mipmaps.Add(d.getSection((int)off, (int)s)); off += s; } tex.surfaces.Add(surface); } return(tex); }
public void ReadNTP3(FileData d, bool skipTextures = true) { 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.CUBEMAP_ALLFACES) == (uint)DDS.DDSCAPS2.CUBEMAP_ALLFACES) { 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.HashOffset = d.pos(); tex.HashId = d.readInt(); d.skip(4); // padding align 8 if (!skipTextures) { 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; } Textures.Add(tex); } }