Пример #1
0
        public static void DecodeDdsToPixels <TPixel>(this BcDecoder decoder, DdsFile dds, Span <TPixel> pixels)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            var width  = (int)dds.header.dwWidth;
            var height = (int)dds.header.dwHeight;

            if (decoder.IsHdrFormat(dds))
            {
                var source = decoder.DecodeHdr(dds).AsSpan();
                for (var r = 0; r < height; ++r)
                {
                    var start   = r * width;
                    var destRow = pixels.Slice(start, width);
                    var sorcRow = source.Slice(start, width);
                    for (var c = 0; c < destRow.Length; ++c)
                    {
                        ref var destPixel = ref destRow[c];
                        ref var sorcPixel = ref sorcRow[c];

                        var rgbVal = sorcPixel.ToVector3();
                        destPixel.FromScaledVector4(new Vector4(rgbVal, 1));
                    }
                }
        public static void ToPssgNode(this DdsFile dds, PssgNode node)
        {
            node.Attributes["height"].Value = dds.header.height;
            node.Attributes["width"].Value  = dds.header.width;
            if (node.HasAttribute("numberMipMapLevels") == true)
            {
                if ((int)dds.header.mipMapCount - 1 >= 0)
                {
                    node.Attributes["numberMipMapLevels"].Value = dds.header.mipMapCount - 1;
                }
                else
                {
                    node.Attributes["numberMipMapLevels"].Value = 0u;
                }
            }
            if (dds.header.ddspf.rGBBitCount == 32)
            {
                node.Attributes["texelFormat"].Value = "ui8x4";
            }
            else if (dds.header.ddspf.rGBBitCount == 8)
            {
                node.Attributes["texelFormat"].Value = "u8";
            }
            else if (dds.header.ddspf.fourCC == 808540228) //DX10
            {
                if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC7_TYPELESS ||
                    dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC7_UNORM)
                {
                    node.Attributes["texelFormat"].Value = "BC7";
                }
                else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC7_UNORM_SRGB)
                {
                    node.Attributes["texelFormat"].Value = "BC7_srgb";
                }
                else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC1_TYPELESS ||
                         dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC1_UNORM)
                {
                    node.Attributes["texelFormat"].Value = "dxt1";
                }
                else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC1_UNORM_SRGB)
                {
                    node.Attributes["texelFormat"].Value = "dxt1_srgb";
                }
                else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC2_TYPELESS ||
                         dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC2_UNORM)
                {
                    node.Attributes["texelFormat"].Value = "dxt3";
                }
                else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC2_UNORM_SRGB)
                {
                    node.Attributes["texelFormat"].Value = "dxt3_srgb";
                }
                else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC3_TYPELESS ||
                         dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC3_UNORM)
                {
                    node.Attributes["texelFormat"].Value = "dxt5";
                }
                else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC3_UNORM_SRGB)
                {
                    node.Attributes["texelFormat"].Value = "dxt5_srgb";
                }
                else
                {
                    throw new FormatException("The dds has an invalid or unsupported format type!");
                }
            }
            else
            {
                node.Attributes["texelFormat"].Value = Encoding.UTF8.GetString(BitConverter.GetBytes(dds.header.ddspf.fourCC)).ToLower();
            }
            List <PssgNode> textureImageBlocks = node.FindNodes("TEXTUREIMAGEBLOCK");

            if (dds.bdata2 != null && dds.bdata2.Count > 0)
            {
                for (int i = 0; i < textureImageBlocks.Count; i++)
                {
                    switch (textureImageBlocks[i].Attributes["typename"].ToString())
                    {
                    case "Raw":
                        if (dds.bdata2.ContainsKey(0) == true)
                        {
                            textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value = dds.bdata2[0];
                            textureImageBlocks[i].Attributes["size"].Value = (UInt32)dds.bdata2[0].Length;
                        }
                        else
                        {
                            throw new Exception("Loading cubemap failed because not all blocks were found. (Write)");
                        }
                        break;

                    case "RawNegativeX":
                        if (dds.bdata2.ContainsKey(1) == true)
                        {
                            textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value = dds.bdata2[1];
                            textureImageBlocks[i].Attributes["size"].Value = (UInt32)dds.bdata2[1].Length;
                        }
                        else
                        {
                            throw new Exception("Loading cubemap failed because not all blocks were found. (Write)");
                        }
                        break;

                    case "RawPositiveY":
                        if (dds.bdata2.ContainsKey(2) == true)
                        {
                            textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value = dds.bdata2[2];
                            textureImageBlocks[i].Attributes["size"].Value = (UInt32)dds.bdata2[2].Length;
                        }
                        else
                        {
                            throw new Exception("Loading cubemap failed because not all blocks were found. (Write)");
                        }
                        break;

                    case "RawNegativeY":
                        if (dds.bdata2.ContainsKey(3) == true)
                        {
                            textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value = dds.bdata2[3];
                            textureImageBlocks[i].Attributes["size"].Value = (UInt32)dds.bdata2[3].Length;
                        }
                        else
                        {
                            throw new Exception("Loading cubemap failed because not all blocks were found. (Write)");
                        }
                        break;

                    case "RawPositiveZ":
                        if (dds.bdata2.ContainsKey(4) == true)
                        {
                            textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value = dds.bdata2[4];
                            textureImageBlocks[i].Attributes["size"].Value = (UInt32)dds.bdata2[4].Length;
                        }
                        else
                        {
                            throw new Exception("Loading cubemap failed because not all blocks were found. (Write)");
                        }
                        break;

                    case "RawNegativeZ":
                        if (dds.bdata2.ContainsKey(5) == true)
                        {
                            textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value = dds.bdata2[5];
                            textureImageBlocks[i].Attributes["size"].Value = (UInt32)dds.bdata2[5].Length;
                        }
                        else
                        {
                            throw new Exception("Loading cubemap failed because not all blocks were found. (Write)");
                        }
                        break;
                    }
                }
            }
            else
            {
                if ((uint)node.Attributes["imageBlockCount"].Value > 1)
                {
                    throw new Exception("Loading cubemap failed because not all blocks were found. (Write)");
                }
                textureImageBlocks[0].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value = dds.bdata;
                textureImageBlocks[0].Attributes["size"].Value = (UInt32)dds.bdata.Length;
            }
        }
        public static DdsFile ToDdsFile(this PssgNode node, bool cubePreview)
        {
            DdsFile dds = new DdsFile();

            dds.header.height = (uint)(node.Attributes["height"].Value);
            dds.header.width  = (uint)(node.Attributes["width"].Value);
            switch ((string)node.Attributes["texelFormat"].Value)
            {
            // gimp doesn't like pitch, so we'll go with linear size
            case "dxt1":
                dds.header.flags            |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.pitchOrLinearSize = ((uint)node.Attributes["height"].Value * (uint)node.Attributes["width"].Value) / 2;
                dds.header.ddspf.flags      |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC      = BitConverter.ToUInt32(Encoding.UTF8.GetBytes(((string)node.Attributes["texelFormat"].Value).ToUpper()), 0);
                break;

            case "dxt1_srgb":
                dds.header.flags            |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.pitchOrLinearSize = ((uint)node.Attributes["height"].Value * (uint)node.Attributes["width"].Value) / 2;
                dds.header.ddspf.flags      |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC      = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                dds.header10.dxgiFormat      = DXGI_Format.DXGI_FORMAT_BC1_UNORM_SRGB;
                break;

            case "dxt2":
            case "dxt3":
            case "dxt4":
            case "dxt5":
                dds.header.flags            |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.pitchOrLinearSize = ((uint)node.Attributes["height"].Value * (uint)node.Attributes["width"].Value);
                dds.header.ddspf.flags      |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC      = BitConverter.ToUInt32(Encoding.UTF8.GetBytes(((string)node.Attributes["texelFormat"].Value).ToUpper()), 0);
                break;

            case "dxt3_srgb":
                dds.header.flags            |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.pitchOrLinearSize = ((uint)node.Attributes["height"].Value * (uint)node.Attributes["width"].Value);
                dds.header.ddspf.flags      |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC      = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                dds.header10.dxgiFormat      = DXGI_Format.DXGI_FORMAT_BC2_UNORM_SRGB;
                break;

            case "dxt5_srgb":
                dds.header.flags            |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.pitchOrLinearSize = ((uint)node.Attributes["height"].Value * (uint)node.Attributes["width"].Value);
                dds.header.ddspf.flags      |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC      = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                dds.header10.dxgiFormat      = DXGI_Format.DXGI_FORMAT_BC3_UNORM_SRGB;
                break;

            case "BC7":
                dds.header.flags            |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.pitchOrLinearSize = ((uint)node.Attributes["height"].Value * (uint)node.Attributes["width"].Value);
                dds.header.ddspf.flags      |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC      = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                dds.header10.dxgiFormat      = DXGI_Format.DXGI_FORMAT_BC7_UNORM;
                break;

            case "BC7_srgb":
                dds.header.flags            |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.pitchOrLinearSize = ((uint)node.Attributes["height"].Value * (uint)node.Attributes["width"].Value);
                dds.header.ddspf.flags      |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC      = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                dds.header10.dxgiFormat      = DXGI_Format.DXGI_FORMAT_BC7_UNORM_SRGB;
                break;

            case "ui8x4":
            case "u8x4":
                dds.header.flags            |= DdsHeader.Flags.DDSD_PITCH;
                dds.header.pitchOrLinearSize = ((uint)node.Attributes["height"].Value * (uint)node.Attributes["width"].Value);     // is this right?
                dds.header.ddspf.flags      |= DdsPixelFormat.Flags.DDPF_ALPHAPIXELS | DdsPixelFormat.Flags.DDPF_RGB;
                dds.header.ddspf.fourCC      = 0;
                dds.header.ddspf.rGBBitCount = 32;
                dds.header.ddspf.rBitMask    = 0xFF0000;
                dds.header.ddspf.gBitMask    = 0xFF00;
                dds.header.ddspf.bBitMask    = 0xFF;
                dds.header.ddspf.aBitMask    = 0xFF000000;
                break;

            case "u8":
                dds.header.flags            |= DdsHeader.Flags.DDSD_PITCH;
                dds.header.pitchOrLinearSize = ((uint)node.Attributes["height"].Value * (uint)node.Attributes["width"].Value);     // is this right?
                // Interchanging the commented values will both work, not sure which is better
                dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_LUMINANCE;
                //header.ddspf.flags |= DDS_PIXELFORMAT.Flags.DDPF_ALPHA;
                dds.header.ddspf.fourCC      = 0;
                dds.header.ddspf.rGBBitCount = 8;
                dds.header.ddspf.rBitMask    = 0xFF;
                //header.ddspf.aBitMask = 0xFF;
                break;

            default:
                throw new NotSupportedException("Texel format not supported.");
            }

            // Mip Maps
            if (node.HasAttribute("automipmap") == true && node.HasAttribute("numberMipMapLevels") == true)
            {
                if ((uint)node.Attributes["automipmap"].Value == 0 && (uint)node.Attributes["numberMipMapLevels"].Value > 0)
                {
                    dds.header.flags      |= DdsHeader.Flags.DDSD_MIPMAPCOUNT;
                    dds.header.mipMapCount = (uint)((uint)node.Attributes["numberMipMapLevels"].Value + 1);
                    dds.header.caps       |= DdsHeader.Caps.DDSCAPS_MIPMAP | DdsHeader.Caps.DDSCAPS_COMPLEX;
                }
            }

            // Byte Data
            List <PssgNode> textureImageBlocks = node.FindNodes("TEXTUREIMAGEBLOCK");

            if ((uint)node.Attributes["imageBlockCount"].Value > 1)
            {
                dds.bdata2 = new Dictionary <int, byte[]>();
                for (int i = 0; i < textureImageBlocks.Count; i++)
                {
                    switch (textureImageBlocks[i].Attributes["typename"].ToString())
                    {
                    case "Raw":
                        dds.header.caps2 |= DdsHeader.Caps2.DDSCAPS2_CUBEMAP_POSITIVEX;
                        dds.bdata2.Add(0, (byte[])textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value);
                        break;

                    case "RawNegativeX":
                        dds.header.caps2 |= DdsHeader.Caps2.DDSCAPS2_CUBEMAP_NEGATIVEX;
                        dds.bdata2.Add(1, (byte[])textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value);
                        break;

                    case "RawPositiveY":
                        dds.header.caps2 |= DdsHeader.Caps2.DDSCAPS2_CUBEMAP_POSITIVEY;
                        dds.bdata2.Add(2, (byte[])textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value);
                        break;

                    case "RawNegativeY":
                        dds.header.caps2 |= DdsHeader.Caps2.DDSCAPS2_CUBEMAP_NEGATIVEY;
                        dds.bdata2.Add(3, (byte[])textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value);
                        break;

                    case "RawPositiveZ":
                        dds.header.caps2 |= DdsHeader.Caps2.DDSCAPS2_CUBEMAP_POSITIVEZ;
                        dds.bdata2.Add(4, (byte[])textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value);
                        break;

                    case "RawNegativeZ":
                        dds.header.caps2 |= DdsHeader.Caps2.DDSCAPS2_CUBEMAP_NEGATIVEZ;
                        dds.bdata2.Add(5, (byte[])textureImageBlocks[i].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value);
                        break;
                    }
                }
                if (cubePreview == true)
                {
                    dds.header.caps2 = 0;
                }
                else if (dds.bdata2.Count == (uint)node.Attributes["imageBlockCount"].Value)
                {
                    dds.header.caps2            |= DdsHeader.Caps2.DDSCAPS2_CUBEMAP;
                    dds.header.flags             = dds.header.flags ^ DdsHeader.Flags.DDSD_LINEARSIZE;
                    dds.header.pitchOrLinearSize = 0;
                    dds.header.caps |= DdsHeader.Caps.DDSCAPS_COMPLEX;
                }
                else
                {
                    throw new Exception("Loading cubemap failed because not all blocks were found. (Read)");
                }
            }
            else
            {
                dds.bdata = (byte[])textureImageBlocks[0].FindNodes("TEXTUREIMAGEBLOCKDATA")[0].Value;
            }

            return(dds);
        }
        public static ErpGfxSurfaceFormat GetErpGfxSurfaceFormat(this DdsFile dds)
        {
            ErpGfxSurfaceFormat imageType;

            switch (dds.header.ddspf.fourCC)
            {
            case 0:
                imageType = ErpGfxSurfaceFormat.ABGR8;
                break;

            case 827611204:     // DXT1 aka DXGI_FORMAT_BC1_UNORM
                imageType = ErpGfxSurfaceFormat.DXT1;
                break;

            case 894720068:     // DXT5 aka DXGI_FORMAT_BC3_UNORM
                imageType = ErpGfxSurfaceFormat.DXT5;
                break;

            case 1429488450:    // BC4U from Intel® Texture Works Plugin for Photoshop
            case 826889281:     // ATI1
                imageType = ErpGfxSurfaceFormat.ATI1;
                break;

            case 1429553986:    // BC5U from Intel® Texture Works Plugin for Photoshop
            case 843666497:     // ATI2 aka DXGI_FORMAT_BC5_UNORM
                imageType = ErpGfxSurfaceFormat.ATI2;
                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;
                }
                else if (dds.header10.dxgiFormat == DXGI_Format.DXGI_FORMAT_BC7_UNORM_SRGB)
                {
                    imageType = ErpGfxSurfaceFormat.BC7_SRGB;
                }
                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;
                }
                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;
                }
                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;
                }
                else
                {
                    goto default;
                }
                break;

            default:
                throw new NotSupportedException("Image format not supported");
            }

            return(imageType);
        }
        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 DdsFile ToDdsFile(this ErpGfxSRVResource srvRes, Stream mipMapsStream, bool exportTexArray, uint texArrayIndex)
        {
            DdsFile dds = new DdsFile();

            dds.header10.arraySize = srvRes.SurfaceRes.Fragment0.ArraySize;
            switch (srvRes.SurfaceRes.Fragment0.ImageType)
            {
            //case (ErpGfxSurfaceFormat)14: // gameparticles k_smoke; application
            case ErpGfxSurfaceFormat.ABGR8:
                dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE;

                if (srvRes.SurfaceRes.Fragment0.ArraySize > 1 && exportTexArray)
                {
                    dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC;
                    dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                    dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_R8G8B8A8_UNORM;
                }
                else
                {
                    dds.header.ddspf.flags      |= DdsPixelFormat.Flags.DDPF_ALPHAPIXELS | DdsPixelFormat.Flags.DDPF_RGB;
                    dds.header.ddspf.fourCC      = 0;
                    dds.header.ddspf.rGBBitCount = 32;
                    dds.header.ddspf.rBitMask    = 0xFF;
                    dds.header.ddspf.gBitMask    = 0xFF00;
                    dds.header.ddspf.bBitMask    = 0xFF0000;
                    dds.header.ddspf.aBitMask    = 0xFF000000;
                }
                break;

            case ErpGfxSurfaceFormat.DXT1:     // ferrari_wheel_sfc
                dds.header.flags       |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC;

                if (srvRes.SurfaceRes.Fragment0.ArraySize > 1 && exportTexArray)
                {
                    dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                    dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC1_UNORM;
                }
                else
                {
                    dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DXT1"), 0);
                }
                break;

            case ErpGfxSurfaceFormat.DXT1_SRGB:     // ferrari_wheel_df, ferrari_paint
                dds.header.flags       |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC1_UNORM_SRGB;
                break;

            case ErpGfxSurfaceFormat.DXT5:     // ferrari_sfc
                dds.header.flags       |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC;

                if (srvRes.SurfaceRes.Fragment0.ArraySize > 1 && exportTexArray)
                {
                    dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                    dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC3_UNORM;
                }
                else
                {
                    dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DXT5"), 0);
                }
                break;

            case ErpGfxSurfaceFormat.DXT5_SRGB:     // ferrari_decal
                dds.header.flags       |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC3_UNORM_SRGB;
                break;

            case ErpGfxSurfaceFormat.ATI1:     // gameparticles k_smoke
                dds.header.flags       |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC;

                if (srvRes.SurfaceRes.Fragment0.ArraySize > 1 && exportTexArray)
                {
                    dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                    dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC4_UNORM;
                }
                else
                {
                    dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("ATI1"), 0);
                }
                break;

            case ErpGfxSurfaceFormat.ATI2:     // ferrari_wheel_nm
                dds.header.flags       |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC;

                if (srvRes.SurfaceRes.Fragment0.ArraySize > 1 && exportTexArray)
                {
                    dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                    dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC5_UNORM;
                }
                else
                {
                    dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("ATI2"), 0);
                }
                break;

            case ErpGfxSurfaceFormat.BC6:     // key0_2016; environment abu_dhabi tree_palm_06
                dds.header.flags       |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC6H_UF16;
                break;

            case ErpGfxSurfaceFormat.BC7:
                dds.header.flags       |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC7_UNORM;
                break;

            case ErpGfxSurfaceFormat.BC7_SRGB:     // flow_boot splash_bg_image
                dds.header.flags       |= DdsHeader.Flags.DDSD_LINEARSIZE;
                dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC;
                dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0);
                dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC7_UNORM_SRGB;
                break;

            default:
                throw new NotSupportedException("Image format not supported");
            }

            byte[] imageData       = srvRes.SurfaceRes.Fragment1.Data;
            bool   foundMipMapFile = mipMapsStream != null;

            if (srvRes.SurfaceRes.HasMips && srvRes.SurfaceRes.HasValidMips && foundMipMapFile)
            {
                using (MemoryStream output = new MemoryStream())
                    using (BinaryReader reader = new BinaryReader(mipMapsStream, Encoding.ASCII, true))
                    {
                        foreach (var mip in srvRes.SurfaceRes.Frag2.Mips)
                        {
                            byte[] mipData = reader.ReadBytes((int)mip.PackedSize);
                            if (mip.PackedSize != (ulong)mipData.LongLength)
                            {
                                throw new FileFormatException($"The mipmaps file, and erp data do not match");
                            }

                            switch (mip.Compression)
                            {
                            case ErpCompressionAlgorithm.None:
                                output.Write(mipData, 0, mipData.Length);
                                break;

                            case ErpCompressionAlgorithm.LZ4:
                                mipData = LZ4.LZ4Codec.Decode(mipData, 0, mipData.Length, (int)mip.Size);
                                output.Write(mipData, 0, mipData.Length);
                                break;

                            default:
                                throw new NotSupportedException($"MipMap compression type {mip.Compression} is not supported");
                            }
                        }

                        output.Write(imageData, 0, imageData.Length);
                        dds.bdata = output.ToArray();
                    }

                dds.header.width             = srvRes.SurfaceRes.MipWidth;
                dds.header.height            = srvRes.SurfaceRes.MipHeight;
                dds.header.pitchOrLinearSize = (uint)srvRes.SurfaceRes.MipLinearSize;

                if (srvRes.SurfaceRes.Frag2.Mips.Count > 0)
                {
                    dds.header.flags      |= DdsHeader.Flags.DDSD_MIPMAPCOUNT;
                    dds.header.mipMapCount = srvRes.SurfaceRes.Fragment0.MipMapCount + (uint)srvRes.SurfaceRes.Frag2.Mips.Count;
                    dds.header.caps       |= DdsHeader.Caps.DDSCAPS_MIPMAP | DdsHeader.Caps.DDSCAPS_COMPLEX;
                }
            }
            else
            {
                dds.header.width             = srvRes.SurfaceRes.Width;
                dds.header.height            = srvRes.SurfaceRes.Height;
                dds.header.pitchOrLinearSize = srvRes.SurfaceRes.LinearSize;

                if (srvRes.SurfaceRes.Fragment0.MipMapCount > 0)
                {
                    dds.header.flags      |= DdsHeader.Flags.DDSD_MIPMAPCOUNT;
                    dds.header.mipMapCount = srvRes.SurfaceRes.Fragment0.MipMapCount;
                    dds.header.caps       |= DdsHeader.Caps.DDSCAPS_MIPMAP | DdsHeader.Caps.DDSCAPS_COMPLEX;
                }

                if (srvRes.SurfaceRes.Fragment0.ArraySize > 1)
                {
                    uint   bytesPerArrayImage = (uint)imageData.Length / srvRes.SurfaceRes.Fragment0.ArraySize;
                    byte[] data = new byte[bytesPerArrayImage];

                    if (!exportTexArray)
                    {
                        dds.header10.arraySize = 1;
                        Buffer.BlockCopy(imageData, (int)(bytesPerArrayImage * texArrayIndex), data, 0, (int)bytesPerArrayImage);
                        dds.bdata = data;
                    }
                    else
                    {
                        dds.bdata = imageData;

                        // TODO: Add support for exporting individual tex array slices
                        //string output = 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
                {
                    dds.bdata = imageData;
                }
            }

            return(dds);
        }
Пример #7
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;
        }