public void ReadNTWU(FileData d) { d.seek(0x4); Version = d.readUShort(); 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.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 = 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 ReadNTWU(FileData d) { d.seek(0x4); Version = d.readUShort(); 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.type = 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.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 = 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); } RefreshGlTexturesByHashId(); //Console.WriteLine("\tMIP: " + size.ToString("x") + " " + dataOffset.ToString("x") + " " + mipSize.ToString("x") + " " + p + " " + (size == 0 ? ds + dataSize - dataOffset : size)); //Console.WriteLine(tex.id.ToString("x") + " " + dataOffset.ToString("x") + " " + mipSize.ToString("x") + " " + p + " " + swizzle); //Console.WriteLine((tex.width >> mipLevel) + " " + (tex.height >> mipLevel)); //File.WriteAllBytes("C:\\s\\Smash\\extract\\data\\fighter\\duckhunt\\model\\body\\mip1.bin", bytearray); //Console.WriteLine(GL.GetError()); /*int j = 0; * foreach(byte[] b in textures[0].mipmaps) * { * if (j == 3) * { * for(int w = 3; w < 8; w++) * { * for (int p = 3; p < 6; p++) * { * byte[] deswiz = GTX.swizzleBC( * b, * (int)Math.Pow(2, w), * 64, * 51, * 4, * (int)Math.Pow(2, p), * 197632 * ); * File.WriteAllBytes("C:\\s\\Smash\\extract\\data\\fighter\\duckhunt\\model\\body\\chunk_" + (int)Math.Pow(2, p) + "_" + (int)Math.Pow(2, w), deswiz); * } * } * * } * j++; * }*/ }