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; }