From 069a083ac022606e51923656d3a9ec567e484e60 Mon Sep 17 00:00:00 2001 From: DarkShadow Date: Thu, 21 Mar 2024 12:38:53 +0200 Subject: [PATCH] Finish initial support for other surface formats --- .../DDS_D3DTX_Converter/DirectX/DDS.cs | 59 +++++++++++++++---- .../DDS_D3DTX_Converter/ImageProperties.cs | 2 +- .../DDS_D3DTX_Converter/Main/D3DTX_Master.cs | 20 ++++++- .../DDS_D3DTX_Converter/Main/DDS_Master.cs | 33 +++++++++-- .../Utilities/ImageUtilities.cs | 35 +++++------ 5 files changed, 114 insertions(+), 35 deletions(-) diff --git a/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/DirectX/DDS.cs b/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/DirectX/DDS.cs index 18f9cb4..0c403c4 100644 --- a/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/DirectX/DDS.cs +++ b/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/DirectX/DDS.cs @@ -301,17 +301,28 @@ public static DDS_HEADER GetHeaderFromBytes(byte[] byteArray) Marshal.Copy(byteArray, 0, ptr, size); - - //Initialize DDS_HEADER_DXT10 - DDS_HEADER_DXT10 dxt10HeaderObject = new(); - // dxt10HeaderObject.dxgiFormat = ; - headerObject = (DDS_HEADER)Marshal.PtrToStructure(ptr, headerObject.GetType()); Marshal.FreeHGlobal(ptr); return headerObject; } + public static DDS_HEADER_DXT10 GetDX10HeaderFromBytes(byte[] byteArray) + { + DDS_HEADER_DXT10 dxt10HeaderObject = new(); + + int dx10_size = Marshal.SizeOf(dxt10HeaderObject); + IntPtr dx10_ptr = Marshal.AllocHGlobal(dx10_size); + + Marshal.Copy(byteArray, 0, dx10_ptr, dx10_size); + + dxt10HeaderObject = (DDS_HEADER_DXT10)Marshal.PtrToStructure(dx10_ptr, dxt10HeaderObject.GetType()); + Marshal.FreeHGlobal(dx10_ptr); + + Console.WriteLine("DXGI FORMAT: " + dxt10HeaderObject.dxgiFormat); + return dxt10HeaderObject; + } + /// /// Converts a DDS_HEADER object into a byte array. /// @@ -330,6 +341,24 @@ public static byte[] GetHeaderBytes(DDS_HEADER header) return arr; } + /// + /// Converts a DDS_HEADER_DXT10 object into a byte array. + /// + /// + /// + public static byte[] GetDXT10HeaderBytes(DDS_HEADER_DXT10 header) + { + int size = Marshal.SizeOf(header); + byte[] arr = new byte[size]; + + IntPtr ptr = Marshal.AllocHGlobal(size); + Marshal.StructureToPtr(header, ptr, true); + Marshal.Copy(ptr, arr, 0, size); + Marshal.FreeHGlobal(ptr); + + return arr; + } + public static DDS_HEADER GetBlankHeader() { return new() @@ -368,6 +397,15 @@ public static DDS_HEADER GetPresetHeader() }; } + public static DDS_HEADER_DXT10 GetPresetDXT10Header() + { + return new() + { + dxgiFormat = DXGI_FORMAT.R32G32B32_SINT, + resourceDimension = D3D10_RESOURCE_DIMENSION.D3D10_RESOURCE_DIMENSION_TEXTURE2D + }; + } + public static uint Get_FourCC_FromTellale(T3SurfaceFormat format) { return format switch @@ -433,17 +471,14 @@ public static T3SurfaceFormat Get_T3Format_FromFourCC(uint fourCC, DDS_Master dd else if (fourCC == ByteFunctions.Convert_String_To_UInt32("ATI1")) return T3SurfaceFormat.eSurface_DXT5A; else if (fourCC == ByteFunctions.Convert_String_To_UInt32("BC4S")) return T3SurfaceFormat.eSurface_BC4; else if (fourCC == ByteFunctions.Convert_String_To_UInt32("BC5S")) return T3SurfaceFormat.eSurface_BC5; - else if (fourCC == ByteFunctions.Convert_String_To_UInt32("DX10")) return Parse_T3Format_FromDX10(dds.sourceFileData); + else if (fourCC == ByteFunctions.Convert_String_To_UInt32("DX10")) return Parse_T3Format_FromDX10(dds.dxt10_header.dxgiFormat); else return T3SurfaceFormat.eSurface_DXT1; } - public static T3SurfaceFormat Parse_T3Format_FromDX10(byte[] data) + public static T3SurfaceFormat Parse_T3Format_FromDX10(DXGI_FORMAT dxgi_format) { - int startIndex = 128; - - int dxgi_format = BitConverter.ToInt32(data, startIndex); - - return dxgi_format switch + Console.WriteLine((int)dxgi_format); + return (int)dxgi_format switch { (int)DXGI_FORMAT.R8G8B8A8_UNORM_SRGB => T3SurfaceFormat.eSurface_ARGB8, (int)DXGI_FORMAT.R8G8B8A8_UNORM => T3SurfaceFormat.eSurface_ARGB8, diff --git a/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/ImageProperties.cs b/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/ImageProperties.cs index f9f1abf..149da00 100644 --- a/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/ImageProperties.cs +++ b/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/ImageProperties.cs @@ -38,7 +38,7 @@ public static ImageProperties GetImagePropertiesFromD3DTX(string filePath) return new ImageProperties() { Name = master.GetTextureName(), - CompressionType = master.GetCompressionType(), + CompressionType = master.GetStringCompressionType(), Width = master.GetWidth().ToString(), Height = master.GetHeight().ToString(), HasAlpha = master.GetHasAlpha(), diff --git a/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Main/D3DTX_Master.cs b/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Main/D3DTX_Master.cs index 9685a2a..3c58f5e 100644 --- a/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Main/D3DTX_Master.cs +++ b/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Main/D3DTX_Master.cs @@ -409,7 +409,7 @@ public uint GetWidth() return 0; } - public string GetCompressionType() + public string GetStringCompressionType() { if (d3dtx4 != null) return Enum.GetName(d3dtx4.mSurfaceFormat).Remove(0, 9); @@ -427,6 +427,24 @@ public string GetCompressionType() return "Not Available"; } + public T3SurfaceFormat GetCompressionType() + { + if (d3dtx4 != null) + return d3dtx4.mSurfaceFormat; + else if (d3dtx5 != null) + return d3dtx5.mSurfaceFormat; + else if (d3dtx6 != null) + return d3dtx6.mSurfaceFormat; + else if (d3dtx7 != null) + return d3dtx7.mSurfaceFormat; + else if (d3dtx8 != null) + return d3dtx8.mSurfaceFormat; + else if (d3dtx9 != null) + return d3dtx9.mSurfaceFormat; + else + return T3SurfaceFormat.eSurface_Unknown; + } + public string GetChannelCount() { if (d3dtx4 != null) diff --git a/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Main/DDS_Master.cs b/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Main/DDS_Master.cs index b1c09c4..c44971c 100644 --- a/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Main/DDS_Master.cs +++ b/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Main/DDS_Master.cs @@ -4,6 +4,8 @@ using D3DTX_Converter.Utilities; using D3DTX_Converter.DirectX; using D3DTX_Converter.TelltaleEnums; +using DirectXTexNet; +using System.ComponentModel; /* * DXT1 - DXGI_FORMAT_BC1_UNORM / D3DFMT_DXT1 @@ -51,6 +53,8 @@ public class DDS_Master public DDS_HEADER header; + public DDS_HEADER_DXT10 dxt10_header; + /// /// A struct used when matching a DDS with a D3DTX. /// @@ -105,7 +109,6 @@ public DDS_Master(D3DTX_Master d3dtx) header = DDS.GetPresetHeader(); T3SurfaceFormat surfaceFormat = T3SurfaceFormat.eSurface_DXT1; - //header.dwCaps = DDSCAPS.DDSCAPS_TEXTURE | DDSCAPS.DDSCAPS_MIPMAP; if (d3dtx.d3dtx4 != null) @@ -144,6 +147,7 @@ public DDS_Master(D3DTX_Master d3dtx) header.dwMipMapCount = d3dtx.d3dtx8.mNumMipLevels; header.dwDepth = d3dtx.d3dtx8.mDepth; surfaceFormat = d3dtx.d3dtx8.mSurfaceFormat; + } else if (d3dtx.d3dtx9 != null) { @@ -152,10 +156,19 @@ public DDS_Master(D3DTX_Master d3dtx) header.dwMipMapCount = d3dtx.d3dtx9.mNumMipLevels; header.dwDepth = d3dtx.d3dtx9.mDepth; surfaceFormat = d3dtx.d3dtx9.mSurfaceFormat; + } header.ddspf.dwFourCC = DDS.Get_FourCC_FromTellale(surfaceFormat); + if (header.ddspf.dwFourCC == ByteFunctions.Convert_String_To_UInt32("DX10")) + { + dxt10_header = DDS.GetPresetDXT10Header(); + dxt10_header.dxgiFormat = DDS.GetSurfaceFormatAsDXGI(surfaceFormat); + dxt10_header.resourceDimension = d3dtx.IsCubeTexture() ? D3D10_RESOURCE_DIMENSION.D3D10_RESOURCE_DIMENSION_TEXTURE3D : D3D10_RESOURCE_DIMENSION.D3D10_RESOURCE_DIMENSION_TEXTURE2D; + dxt10_header.arraySize = 1; //TODO NEEDS TESTING + } + switch (surfaceFormat) { case T3SurfaceFormat.eSurface_A8: @@ -193,6 +206,13 @@ private void GetData(byte[] fileData, bool headerOnly) Console.WriteLine("DDS Mip Map Count = {0}", header.dwMipMapCount); Console.WriteLine("DDS Compression = {0}", header.ddspf.dwFourCC); + //get dxt10 header if it exists + if (header.ddspf.dwFourCC == ByteFunctions.Convert_String_To_UInt32("DX10")) + { + byte[] dxt10headerBytes = ByteFunctions.AllocateBytes(20, fileData, 128); //skip the main header + dxt10_header = DDS.GetDX10HeaderFromBytes(dxt10headerBytes); + } + if (headerOnly) return; @@ -200,14 +220,17 @@ private void GetData(byte[] fileData, bool headerOnly) //calculate dds header length (we add 4 because we skipped the 4 bytes which contain the ddsPrefix, it isn't necessary to parse this data) uint ddsHeaderLength = 4 + header.dwSize; + //if dxt10Header is present, add additional 20 bytes + uint dxt10HeaderLength = (uint)((header.ddspf.dwFourCC == ByteFunctions.Convert_String_To_UInt32("DX10")) ? 20 : 0); + //calculate the length of just the dds texture data - uint ddsTextureDataLength = (uint)sourceFileData.Length - ddsHeaderLength; + uint ddsTextureDataLength = (uint)sourceFileData.Length - ddsHeaderLength - dxt10HeaderLength; //allocate a byte array of dds texture length byte[] ddsTextureData = new byte[ddsTextureDataLength]; //copy the data from the source byte array past the header (so we are only getting texture data) - Array.Copy(sourceFileData, ddsHeaderLength, ddsTextureData, 0, ddsTextureData.Length); + Array.Copy(sourceFileData, ddsHeaderLength + dxt10HeaderLength, ddsTextureData, 0, ddsTextureData.Length); textureData = new(); @@ -341,6 +364,7 @@ public void Write_D3DTX_AsDDS(D3DTX_Master d3dtx, string destinationDirectory) { //turn our header data into bytes to be written into a file byte[] dds_header = ByteFunctions.Combine(ByteFunctions.GetBytes("DDS "), DDS.GetHeaderBytes(header)); + dds_header = ByteFunctions.Combine(dds_header, DDS.GetDXT10HeaderBytes(dxt10_header)); //copy the dds header to the file byte[] finalData = Array.Empty(); @@ -371,7 +395,7 @@ public void Match_DDS_With_D3DTX(string ddsPath, D3DTX_Master d3dtx, DDS_Matchin { } - + public byte[] GetData(D3DTX_Master d3dtx) { @@ -410,6 +434,7 @@ public byte[] GetData(D3DTX_Master d3dtx) { //turn our header data into bytes to be written into a file byte[] dds_header = ByteFunctions.Combine(ByteFunctions.GetBytes("DDS "), DDS.GetHeaderBytes(header)); + dds_header = ByteFunctions.Combine(dds_header, DDS.GetDXT10HeaderBytes(dxt10_header)); //copy the dds header to the file byte[] finalData = Array.Empty(); diff --git a/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Utilities/ImageUtilities.cs b/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Utilities/ImageUtilities.cs index fb5177f..ed3e854 100644 --- a/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Utilities/ImageUtilities.cs +++ b/DDS_D3DTX_Converter_GUI/DDS_D3DTX_Converter/Utilities/ImageUtilities.cs @@ -15,7 +15,7 @@ namespace D3DTX_Converter.Utilities { public static class ImageUtilities { - + /// /// Checks if the image from a file path is transparent. /// @@ -59,7 +59,7 @@ public static bool IsImageOpaque(string imageFilePath) /// public static bool IsImageOpaque(Image image) { - + bool hasAlpha = false; image.ProcessPixelRows(pixelAccessor => @@ -86,7 +86,7 @@ public static bool IsImageOpaque(Image image) return hasAlpha; } - + /// /// Converts .dds (and .tga) files to a bitmap. This is only used in the image preview. /// @@ -97,12 +97,12 @@ public static WriteableBitmap ConvertFileFromDdsToBitmap(string filePath) { //load the image using var image = Pfimage.FromFile(filePath); - + //get the data var newData = image.Data; var newDataLen = image.DataLen; var stride = image.Stride; - + //get the color type SKColorType colorType; switch (image.Format) @@ -138,7 +138,7 @@ public static WriteableBitmap ConvertFileFromDdsToBitmap(string filePath) default: throw new ArgumentException($"Skia unable to interpret pfim format: {image.Format}"); } - + //Converts the data into writeableBitmap. (TODO Insert a link to the code) var imageInfo = new SKImageInfo(image.Width, image.Height, colorType); var handle = GCHandle.Alloc(newData, GCHandleType.Pinned); @@ -170,7 +170,7 @@ public static WriteableBitmap ConvertTiffToBitmap(string filePath) // read the dimensions var width = tifImg.GetField(TiffTag.IMAGEWIDTH)[0].ToInt(); var height = tifImg.GetField(TiffTag.IMAGELENGTH)[0].ToInt(); - + //Experimentation, ignore this //var smth = tifImg.GetField(TiffTag.COMPRESSION)[0].ToInt(); @@ -212,7 +212,7 @@ public static WriteableBitmap ConvertTiffToBitmap(string filePath) } /// - /// Converts .d3dtx files to a bitmap. This is only used in the image preview. + /// Converts .d3dtx files to a bitmap (by converting to .dds first). This is only used in the image preview. /// /// /// @@ -221,20 +221,21 @@ public static WriteableBitmap ConvertD3dtxToBitmap(string filePath) var d3dtx = new D3DTX_Master(); d3dtx.Read_D3DTX_File(filePath); DDS_Master ddsFile = new(d3dtx); + //implement get without header (to see) var array = ddsFile.GetData(d3dtx); Stream stream = new MemoryStream(array); - var image = Pfimage.FromStream(stream); - WriteableBitmap writeableBitmap = new WriteableBitmap( - new PixelSize(image.Width, image.Height), - new Vector(96, 96), - PixelFormat.Bgra8888, - AlphaFormat.Premul); + var image = Pfimage.FromStream(stream); + WriteableBitmap writeableBitmap = new WriteableBitmap( + new PixelSize(image.Width, image.Height), + new Vector(96, 96), + PixelFormat.Bgra8888, + AlphaFormat.Premul); - using var lockedBitmap = writeableBitmap.Lock(); - Marshal.Copy(image.Data, 0, lockedBitmap.Address, image.DataLen); + using var lockedBitmap = writeableBitmap.Lock(); + Marshal.Copy(image.Data, 0, lockedBitmap.Address, image.DataLen); - return writeableBitmap; + return writeableBitmap; } } } \ No newline at end of file