Example #1
0
        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);
            }
        }
Example #2
0
        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.CubemapAllfaces) == (uint)Ddscaps2.CubemapAllfaces)
                {
                    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);
        }