public void ImportDDS(string fileName, string mipMapSaveLocation, bool importTexArray) { DdsFile dds = new DdsFile(File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)); ErpGfxSRVResource srvRes = new ErpGfxSRVResource(); srvRes.FromResource(Texture); ErpGfxSurfaceFormat imageType = 0; uint mipLinearSize; switch (dds.header.ddspf.fourCC) { case 0: imageType = ErpGfxSurfaceFormat.ABGR8; mipLinearSize = (dds.header.width * dds.header.height); break; case 827611204: // DXT1 aka DXGI_FORMAT_BC1_UNORM imageType = ErpGfxSurfaceFormat.DXT1; mipLinearSize = (dds.header.width * dds.header.height) / 2; break; case 894720068: // DXT5 aka DXGI_FORMAT_BC3_UNORM imageType = ErpGfxSurfaceFormat.DXT5; mipLinearSize = (dds.header.width * dds.header.height); break; case 826889281: // ATI1 imageType = ErpGfxSurfaceFormat.ATI1; mipLinearSize = (dds.header.width * dds.header.height) / 2; break; case 843666497: // ATI2 aka DXGI_FORMAT_BC5_UNORM imageType = ErpGfxSurfaceFormat.ATI2; mipLinearSize = (dds.header.width * dds.header.height); break; case 808540228: // DX10 if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC7_TYPELESS || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC7_UNORM) { imageType = ErpGfxSurfaceFormat.BC7; mipLinearSize = (dds.header.width * dds.header.height); } else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC7_UNORM_SRGB) { imageType = ErpGfxSurfaceFormat.BC7_SRGB; mipLinearSize = (dds.header.width * dds.header.height); } else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_R8G8B8A8_TYPELESS || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_R8G8B8A8_UNORM || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_R8G8B8A8_SNORM || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_R8G8B8A8_UINT || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_R8G8B8A8_SINT) { goto case 0; } else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC1_TYPELESS || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC1_UNORM) { goto case 827611204; } else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC1_UNORM_SRGB) { imageType = ErpGfxSurfaceFormat.DXT1_SRGB; mipLinearSize = (dds.header.width * dds.header.height) / 2; } else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC3_TYPELESS || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC3_UNORM) { goto case 894720068; } else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC3_UNORM_SRGB) { imageType = ErpGfxSurfaceFormat.DXT5_SRGB; mipLinearSize = (dds.header.width * dds.header.height); } else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC4_TYPELESS || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC4_UNORM || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC4_SNORM) { goto case 826889281; } else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC5_TYPELESS || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC5_UNORM || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC5_SNORM) { goto case 843666497; } else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC6H_TYPELESS || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC6H_UF16 || dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC6H_SF16) { imageType = ErpGfxSurfaceFormat.BC6; mipLinearSize = (dds.header.width * dds.header.height); } else { goto default; } break; default: throw new Exception("Image type not supported!"); } byte[] imageByteData; if (srvRes.SurfaceRes.HasMips) { int mipCount = (int)dds.header.mipMapCount / 4; if (srvRes.SurfaceRes.Frag2.Mips.Count < dds.header.mipMapCount) { mipCount = srvRes.SurfaceRes.Frag2.Mips.Count; } bool foundMipMapSaveLocation = false; if (string.IsNullOrEmpty(mipMapSaveLocation)) { SaveFileDialog sdialog = new SaveFileDialog(); sdialog.Filter = "Mipmaps files|*.mipmaps|All files|*.*"; sdialog.Title = "Select the mipmaps save location and file name"; sdialog.FileName = Path.GetFileName(srvRes.SurfaceRes.Frag2.MipMapFileName); string mipFullPath = Path.GetDirectoryName(Path.Combine(Properties.Settings.Default.F12016Dir, srvRes.SurfaceRes.Frag2.MipMapFileName)); if (Directory.Exists(mipFullPath)) { sdialog.InitialDirectory = mipFullPath; } foundMipMapSaveLocation = sdialog.ShowDialog() == true; mipMapSaveLocation = sdialog.FileName; } else if (Directory.Exists(Path.GetDirectoryName(mipMapSaveLocation))) { foundMipMapSaveLocation = true; } if (foundMipMapSaveLocation) { dds.header.mipMapCount -= (uint)mipCount; uint div = (uint)Math.Pow(2.0, mipCount); dds.header.width /= div; dds.header.height /= div; srvRes.SurfaceRes.Frag2.Mips = new List <ErpGfxSurfaceRes2Mips>(mipCount); UInt64 offset = 0; for (int i = 0; i < mipCount; ++i) { ErpGfxSurfaceRes2Mips mip = new ErpGfxSurfaceRes2Mips(); mip.Unknown = 0; mip.Offset = offset; mip.Width = mipLinearSize; mip.Height = mipLinearSize; offset += mipLinearSize; mipLinearSize /= 4; srvRes.SurfaceRes.Frag2.Mips.Add(mip); } using (ErpBinaryWriter writer = new ErpBinaryWriter(EndianBitConverter.Little, File.Open(mipMapSaveLocation, FileMode.Create, FileAccess.Write, FileShare.Read))) { byte[] mipImageData = new byte[offset]; Buffer.BlockCopy(dds.bdata, 0, mipImageData, 0, (int)offset); writer.Write(mipImageData); } int remainingBytes = dds.bdata.Length - (int)offset; imageByteData = new byte[remainingBytes]; Buffer.BlockCopy(dds.bdata, (int)offset, imageByteData, 0, remainingBytes); srvRes.SurfaceRes.Fragment1.Data = imageByteData; } else { return; } } else { if (srvRes.SurfaceRes.Fragment0.ArraySize > 1) { uint bytesPerArrayImage = (uint)srvRes.SurfaceRes.Fragment1.Data.Length / srvRes.SurfaceRes.Fragment0.ArraySize; if (!importTexArray) { Buffer.BlockCopy(dds.bdata, 0, srvRes.SurfaceRes.Fragment1.Data, (int)(bytesPerArrayImage * _texArrayIndex), (int)bytesPerArrayImage); } else { if (dds.header10.arraySize <= 1) { throw new Exception("The texture array size must be greater than 1."); } imageByteData = dds.bdata; srvRes.SurfaceRes.Fragment1.Data = imageByteData; srvRes.SurfaceRes.Fragment0.ArraySize = dds.header10.arraySize; // TODO: Add support for importing individual tex array slices //imageByteData = new byte[bytesPerArrayImage]; //string input = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName)); //for (int i = 0; i < srvRes.SurfaceRes.Fragment0.ArraySize; ++i) //{ // Buffer.BlockCopy(imageData, (int)(bytesPerArrayImage * i), data, 0, (int)bytesPerArrayImage); // dds.bdata = data; // dds.Write(File.Open(output + "!!!" + i.ToString("000") + ".dds", FileMode.Create, FileAccess.Write, FileShare.Read), -1); //} } } else { imageByteData = dds.bdata; srvRes.SurfaceRes.Fragment1.Data = imageByteData; } } srvRes.Fragment0.ImageType = imageType; srvRes.Fragment0.MipMapCount = dds.header.mipMapCount; srvRes.SurfaceRes.Fragment0.ImageType = imageType; srvRes.SurfaceRes.Fragment0.Width = dds.header.width; srvRes.SurfaceRes.Fragment0.Height = dds.header.height; srvRes.SurfaceRes.Fragment0.MipMapCount = dds.header.mipMapCount; srvRes.ToResource(Texture); }
public static void ToErpGfxSRVResource(this DdsFile dds, ErpGfxSRVResource srvRes, Stream mipMapsStream, bool importTexArray, uint texArrayIndex) { if (srvRes.SurfaceRes.HasMips && mipMapsStream == null) { throw new ArgumentNullException(nameof(mipMapsStream)); } ErpGfxSurfaceFormat imageType = dds.GetErpGfxSurfaceFormat(); uint mipLinearSize = dds.GetLinearSize(); if (srvRes.SurfaceRes.HasMips) { int mipCount = (int)dds.header.mipMapCount / 4; if (srvRes.SurfaceRes.Frag2.Mips.Count < dds.header.mipMapCount) { mipCount = srvRes.SurfaceRes.Frag2.Mips.Count; } bool identicalMipCount = mipCount == srvRes.SurfaceRes.Frag2.Mips.Count; dds.header.mipMapCount -= (uint)mipCount; uint div = (uint)Math.Pow(2.0, mipCount); dds.header.width /= div; dds.header.height /= div; UInt64 offset = 0; bool compress2017 = srvRes.SurfaceRes.Frag2.Mips.Exists(x => x.Compression != ErpCompressionAlgorithm.None); List <ErpGfxSurfaceRes2Mips> newMips = new List <ErpGfxSurfaceRes2Mips>(mipCount); using (MemoryStream ddsDataStream = new MemoryStream(dds.bdata, false)) using (BinaryReader reader = new BinaryReader(ddsDataStream)) using (BinaryWriter writer = new BinaryWriter(mipMapsStream, Encoding.ASCII, true)) { for (int i = 0; i < mipCount; ++i) { ErpGfxSurfaceRes2Mips mip = new ErpGfxSurfaceRes2Mips(); if (identicalMipCount) { mip.Compression = srvRes.SurfaceRes.Frag2.Mips[i].Compression; } else if (compress2017) { mip.Compression = i % 2 == 0 ? ErpCompressionAlgorithm.LZ4 : ErpCompressionAlgorithm.None; } else { mip.Compression = ErpCompressionAlgorithm.None; } mip.Offset = offset; byte[] mipData = reader.ReadBytes((int)mipLinearSize); switch (mip.Compression) { case ErpCompressionAlgorithm.LZ4: mipData = LZ4.LZ4Codec.EncodeHC(mipData, 0, mipData.Length); break; case ErpCompressionAlgorithm.None: default: break; } writer.Write(mipData); mip.PackedSize = (UInt64)mipData.LongLength; mip.Size = mipLinearSize; offset += (UInt64)mipData.LongLength; mipLinearSize /= 4; newMips.Add(mip); } srvRes.SurfaceRes.Fragment1.Data = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); } srvRes.SurfaceRes.Frag2.Mips = newMips; } else { byte[] imageByteData; if (srvRes.SurfaceRes.Fragment0.ArraySize > 1) { uint bytesPerArrayImage = (uint)srvRes.SurfaceRes.Fragment1.Data.Length / srvRes.SurfaceRes.Fragment0.ArraySize; if (!importTexArray) { Buffer.BlockCopy(dds.bdata, 0, srvRes.SurfaceRes.Fragment1.Data, (int)(bytesPerArrayImage * texArrayIndex), (int)bytesPerArrayImage); } else { if (dds.header10.arraySize <= 1) { throw new FileFormatException("The texture array size must be greater than 1"); } imageByteData = dds.bdata; srvRes.SurfaceRes.Fragment1.Data = imageByteData; srvRes.SurfaceRes.Fragment0.ArraySize = dds.header10.arraySize; // TODO: Add support for importing individual tex array slices //imageByteData = new byte[bytesPerArrayImage]; //string input = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName)); //for (int i = 0; i < srvRes.SurfaceRes.Fragment0.ArraySize; ++i) //{ // Buffer.BlockCopy(imageData, (int)(bytesPerArrayImage * i), data, 0, (int)bytesPerArrayImage); // dds.bdata = data; // dds.Write(File.Open(output + "!!!" + i.ToString("000") + ".dds", FileMode.Create, FileAccess.Write, FileShare.Read), -1); //} } } else { imageByteData = dds.bdata; srvRes.SurfaceRes.Fragment1.Data = imageByteData; } } srvRes.Fragment0.ImageType = imageType; srvRes.Fragment0.MipMapCount = dds.header.mipMapCount; srvRes.SurfaceRes.Fragment0.ImageType = imageType; srvRes.SurfaceRes.Fragment0.Width = dds.header.width; srvRes.SurfaceRes.Fragment0.Height = dds.header.height; srvRes.SurfaceRes.Fragment0.MipMapCount = dds.header.mipMapCount; }
public static void ToErpGfxSRVResource(this DdsFile dds, ErpGfxSRVResource srvRes, Stream mipMapsStream, bool importTexArray, uint texArrayIndex) { if (srvRes.SurfaceRes.HasMips && mipMapsStream == null) { throw new ArgumentNullException(nameof(mipMapsStream)); } ErpGfxSurfaceFormat imageType = dds.GetErpGfxSurfaceFormat(); uint mipLinearSize = dds.GetLinearSize(); if (srvRes.SurfaceRes.HasMips) { int mipCount = (int)dds.header.mipMapCount / 4; if (srvRes.SurfaceRes.Frag2.Mips.Count < dds.header.mipMapCount) { mipCount = srvRes.SurfaceRes.Frag2.Mips.Count; } bool identicalMipCount = mipCount == srvRes.SurfaceRes.Frag2.Mips.Count; dds.header.mipMapCount -= (uint)mipCount; uint div = (uint)Math.Pow(2.0, mipCount); dds.header.width /= div; dds.header.height /= div; UInt64 offset = 0; var compressZStd = srvRes.SurfaceRes.Frag2.Mips.Any(x => x.Compression == ErpGfxSurfaceResMipCompressionAlgorithm.ZStandard); bool compress2017 = !compressZStd && srvRes.SurfaceRes.Frag2.Mips.Exists(x => x.Compression != ErpGfxSurfaceResMipCompressionAlgorithm.None); List <ErpGfxSurfaceRes2Mips> newMips = new List <ErpGfxSurfaceRes2Mips>(mipCount); using (MemoryStream ddsDataStream = new MemoryStream(dds.bdata, false)) using (BinaryReader reader = new BinaryReader(ddsDataStream)) using (BinaryWriter writer = new BinaryWriter(mipMapsStream, Encoding.ASCII, true)) { for (int i = 0; i < mipCount; ++i) { ErpGfxSurfaceRes2Mips mip = new ErpGfxSurfaceRes2Mips(); if (identicalMipCount) { mip.Compression = srvRes.SurfaceRes.Frag2.Mips[i].Compression; } else if (compressZStd) { // ZStd for mips introduced in Grid Legends // if it's used in any mip, just assume we need to apply to all (for no good reason) mip.Compression = ErpGfxSurfaceResMipCompressionAlgorithm.ZStandard; } else if (compress2017) { // Not sure how to decide which mip levels should be compressed or not // I can't remember why I chose this scheme mip.Compression = i % 2 == 0 ? ErpGfxSurfaceResMipCompressionAlgorithm.LZ4 : ErpGfxSurfaceResMipCompressionAlgorithm.None; } else { mip.Compression = ErpGfxSurfaceResMipCompressionAlgorithm.None; } mip.Offset = offset; byte[] mipData = reader.ReadBytes((int)mipLinearSize); ulong mipPackedSize = (ulong)mipData.Length; switch (mip.Compression) { case ErpGfxSurfaceResMipCompressionAlgorithm.ZStandard: { using var zs = new Compressor(ZstdSharp.Unsafe.Methods.ZSTD_defaultCLevel()); var compData = zs.Wrap(mipData); writer.Write(compData); mipPackedSize = (ulong)compData.Length; break; } case ErpGfxSurfaceResMipCompressionAlgorithm.LZ4: byte[] compressedMipData = new byte[LZ4Codec.MaximumOutputSize(mipData.Length)]; int compSize = LZ4Codec.Encode(mipData, 0, mipData.Length, compressedMipData, 0, compressedMipData.Length, LZ4Level.L12_MAX); writer.Write(compressedMipData, 0, compSize); mipPackedSize = (ulong)compSize; break; case ErpGfxSurfaceResMipCompressionAlgorithm.None: writer.Write(mipData); break; default: throw new NotSupportedException($"MipMap compression type {mip.Compression} is not supported"); } mip.PackedSize = mipPackedSize; mip.Size = mipLinearSize; offset += mipPackedSize; mipLinearSize /= 4; newMips.Add(mip); } srvRes.SurfaceRes.Fragment1.Data = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); } srvRes.SurfaceRes.Frag2.Mips = newMips; } else { byte[] imageByteData; if (srvRes.SurfaceRes.Fragment0.ArraySize > 1) { uint bytesPerArrayImage = (uint)srvRes.SurfaceRes.Fragment1.Data.Length / srvRes.SurfaceRes.Fragment0.ArraySize; if (!importTexArray) { Buffer.BlockCopy(dds.bdata, 0, srvRes.SurfaceRes.Fragment1.Data, (int)(bytesPerArrayImage * texArrayIndex), (int)bytesPerArrayImage); } else { if (dds.header10.arraySize <= 1) { throw new FileFormatException("The texture array size must be greater than 1"); } imageByteData = dds.bdata; srvRes.SurfaceRes.Fragment1.Data = imageByteData; srvRes.SurfaceRes.Fragment0.ArraySize = dds.header10.arraySize; // TODO: Add support for importing individual tex array slices //imageByteData = new byte[bytesPerArrayImage]; //string input = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName)); //for (int i = 0; i < srvRes.SurfaceRes.Fragment0.ArraySize; ++i) //{ // Buffer.BlockCopy(imageData, (int)(bytesPerArrayImage * i), data, 0, (int)bytesPerArrayImage); // dds.bdata = data; // dds.Write(File.Open(output + "!!!" + i.ToString("000") + ".dds", FileMode.Create, FileAccess.Write, FileShare.Read), -1); //} } } else { imageByteData = dds.bdata; srvRes.SurfaceRes.Fragment1.Data = imageByteData; } } srvRes.Fragment0.ImageType = imageType; srvRes.Fragment0.MipMapCount = dds.header.mipMapCount; srvRes.SurfaceRes.Fragment0.ImageType = imageType; srvRes.SurfaceRes.Fragment0.Width = dds.header.width; srvRes.SurfaceRes.Fragment0.Height = dds.header.height; srvRes.SurfaceRes.Fragment0.MipMapCount = dds.header.mipMapCount; }