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;
        }
Beispiel #2
0
        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;
        }