Пример #1
0
 /// <summary>
 /// Loads the buffer into the pointed location.
 /// </summary>
 /// <param name="pDestination">A pointer to the destination of the data.</param>
 public unsafe void CopyTo(byte *pDestination)
 {
     if (_isDeflated)
     {
         if (_weakInflatedBufferRef.TryGetTarget(out var buffer))
         {
             fixed(byte *pBuffer = buffer)
             {
                 Stdlib.MemCpy(pDestination, pBuffer, (UIntPtr)_actualSize);
             }
         }
         else
         {
             // Decompress the buffer of the ClipboardItem into the destination.
             BlockCompression.Inflate(_buffer, _actualSize, pDestination);
         }
     }
     else
     {
         fixed(byte *pBuffer = _buffer)
         {
             Stdlib.MemCpy(pDestination, pBuffer, (UIntPtr)_actualSize);
         }
     }
 }
Пример #2
0
        public void DoWrite(IProgress <string> progress = null)
        {
            var origStream = Stream;

            SetStream(new MemoryStream());
            Write(progress);

            progress?.Report("Post processing: Compressing file...");
            Stream.Position = 0;
            //Stream.CopyTo(origStream);

            MemoryStream ms = (MemoryStream)this.Stream;

            if (_compress)
            {
                BlockCompression.WriteBlockFile(origStream, ms.ToArray());
            }
            else
            {
                ms.CopyTo(origStream);
            }

            this.Stream = origStream;

            //File.WriteAllBytes("GlobalC_gen.bin", ms.ToArray());
            //BlockCompression.WriteBlockFile(origStream, ms.ToArray());
        }
Пример #3
0
        public void TestDecompressionToArray()
        {
            using FileStream compressedFileStream = new FileStream(@"data\\GlobalC.lzc", FileMode.Open);
            byte[] decompressedBytes = BlockCompression.ReadBlockFile(compressedFileStream);

            Assert.AreEqual(9963360, decompressedBytes.Length);
        }
Пример #4
0
        public void TestDecompressionToStream()
        {
            using FileStream compressedFileStream = new FileStream(@"data\\GlobalC.lzc", FileMode.Open);
            Stream decompressedStream = BlockCompression.StreamBlockFile(compressedFileStream);

            Assert.AreEqual(9963360, decompressedStream.Length);
            Assert.AreEqual(0, decompressedStream.Position);
        }
Пример #5
0
        public static void SetUpTests(TestContext testContext)
        {
            using FileStream compressedFileStream = new FileStream(@"data\\GlobalC.lzc", FileMode.Open);
            Stream decompressedStream = BlockCompression.StreamBlockFile(compressedFileStream);

            DatabaseChunkBundle chunkBundle = new DatabaseChunkBundle(decompressedStream);

            chunkBundle.Load();

            _database = chunkBundle.Database;
        }
Пример #6
0
        public void TestCompression()
        {
            MemoryStream ms = new MemoryStream();

            byte[] data =
            {
                0x1, 0x2, 0x3, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x5, 0x5, 0x6, 0x5, 0x5, 0x7,
                0x1, 0x2, 0x3, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x5, 0x5, 0x6, 0x5, 0x5, 0x7,
                0x1, 0x2, 0x3, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x5, 0x5, 0x6, 0x5, 0x5, 0x7,
                0x1, 0x2, 0x3, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x5, 0x5, 0x6, 0x5, 0x5, 0x7,
            };
            BlockCompression.WriteCompressedBlocks(ms, data);
        }
Пример #7
0
        public static ClipboardItem FromBuffer(DataFormat formatId, byte[] buffer, bool cloneBuffer = true, bool optimizeLongTerm = true)
        {
            if (optimizeLongTerm && buffer.Length >= 5 * 1024 * 1024) // 5MiBVV
            {
                byte[] deflatedBuffer = BlockCompression.Deflate(buffer);
                float  deflationRatio = (float)buffer.Length / deflatedBuffer.Length;
                if (deflationRatio > 1.5f)
                {
                    return(new ClipboardItem(formatId, deflatedBuffer, buffer.Length, isDeflated: true));
                }
            }

            return(new ClipboardItem(formatId, cloneBuffer ? (byte[])buffer.Clone() : buffer, buffer.Length, isDeflated: false));
        }
Пример #8
0
        public static unsafe ClipboardItem FromPointer(DataFormat formatId, byte *bufferPtr, int size)
        {
            if (size > 4096)
            {
                byte[] deflatedBuffer = BlockCompression.Deflate(bufferPtr, size);
                float  deflationRatio = (float)size / deflatedBuffer.Length;
                if (deflationRatio > 1.5f)
                {
                    return(new ClipboardItem(formatId, deflatedBuffer, size, isDeflated: true));
                }
            }

            byte[] newBuffer = new byte[size];
            fixed(byte *dest = newBuffer)
            {
                Stdlib.MemCpy(dest, bufferPtr, (UIntPtr)size);
            }

            return(new ClipboardItem(formatId, newBuffer, newBuffer.Length, isDeflated: false));
        }
Пример #9
0
        /// <summary>
        /// Loads the data as a byte array.
        /// </summary>
        /// <returns>The byte array representation of the data.</returns>
        public byte[] GetDataBuffer()
        {
            if (_buffer == null)
            {
                throw new InvalidOperationException();
            }

            if (_isDeflated)
            {
                if (!_weakInflatedBufferRef.TryGetTarget(out byte[] inflatedBuffer))
                {
                    inflatedBuffer = new byte[_actualSize];
                    BlockCompression.Inflate(_buffer, _actualSize, inflatedBuffer);
                    _weakInflatedBufferRef.SetTarget(inflatedBuffer);
                }
                return(inflatedBuffer);
            }
            else
            {
                return(_buffer);
            }
        }
Пример #10
0
        public static bool Uncook(CR2WFile cr2w, List <byte[]> buffers, FileInfo outfile, EUncookExtension uncookext)
        {
            //We need 2 buffers one for atlas one for tile data
            if (buffers.Count < 2)
            {
                return(false);
            }

            if (!(cr2w.Chunks.FirstOrDefault()?.data is Multilayer_Mask mlmask) ||
                !(cr2w.Chunks[1]?.data is rendRenderMultilayerMaskBlobPC blob))
            {
                return(false);
            }

            string filename = Path.GetFileNameWithoutExtension(outfile.FullName);
            string path     = outfile.Directory.FullName;

            uint atlasWidth  = blob.Header.AtlasWidth.val;
            uint atlasHeight = blob.Header.AtlasHeight.val;

            uint maskWidth  = blob.Header.MaskWidth.val;
            uint maskHeight = blob.Header.MaskHeight.val;

            uint maskWidthLow  = blob.Header.MaskWidthLow.val;
            uint maskHeightLow = blob.Header.MaskHeightLow.val;

            uint maskTileSize = blob.Header.MaskTileSize.val;

            uint maskCount = blob.Header.NumLayers.val;

            byte[] atlas = buffers[0];
            uint[] tiles;

            //Read tilesdata buffer into appropriate variable type
            var b = buffers[1];

            tiles = new uint[b.Length / 4];
            for (int i = 0, j = 0; i < b.Length; i += 4, j++)
            {
                tiles[j] = BitConverter.ToUInt32(b, i);
            }
            byte[] atlasRaw = new byte[atlasWidth * atlasHeight];

            //Decode compressed data into single channel uncompressed
            //Mlmask always BC4?
            if (!BlockCompression.DecodeBC(atlas, ref atlasRaw, atlasWidth, atlasHeight, BlockCompression.BlockCompressionType.BC4))
            {
                return(false);
            }

            //{
            //    var mFilename = filename + $"__.dds";
            //    var newpath = Path.Combine(path, mFilename);
            //    using (var ddsStream = new FileStream($"{newpath}", FileMode.Create, FileAccess.Write))
            //    {
            //        DDSUtils.GenerateAndWriteHeader(ddsStream, new DDSMetadata(atlasWidth, atlasHeight, 0, EFormat.R8_UNORM, 8, false, 0, false));
            //
            //        ddsStream.Write(atlasRaw);
            //    }
            //}


            byte[] maskData = new byte[maskWidth * maskHeight];


            Directory.CreateDirectory(path);
            for (int i = 0; i < maskCount; i++)
            {
                var mFilename = filename + $"_{i}.dds";
                var newpath   = Path.Combine(path, mFilename);

                //Clear instead of allocate new is faster?
                //Mandatory cause decode does not always write to every pixel
                Array.Clear(maskData, 0, maskData.Length);
                Decode(ref maskData, maskWidth, maskHeight, maskWidthLow, maskHeightLow, atlasRaw, atlasWidth, atlasHeight, tiles, maskTileSize, i);

                using (var ddsStream = new FileStream($"{newpath}", FileMode.Create, FileAccess.Write))
                {
                    DDSUtils.GenerateAndWriteHeader(ddsStream, new DDSMetadata(maskWidth, maskHeight, 0, EFormat.R8_UNORM, 8, false, 0, false));

                    ddsStream.Write(maskData);
                }

                //convert texture if neccessary
                if (uncookext != EUncookExtension.dds && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    var di = new FileInfo(outfile.FullName).Directory;
                    TexconvWrapper.Convert(di.FullName, $"{newpath}", uncookext);
                }
            }
            return(true);
        }
Пример #11
0
        public static bool Uncook(Stream cr2wStream, CR2WFile cr2w, EUncookExtension uncookext)
        {
            //We need 2 buffers one for atlas one for tile data

            if (!(cr2w.Chunks.FirstOrDefault()?.data is Multilayer_Mask mlmask) ||
                !(cr2w.Chunks[1]?.data is rendRenderMultilayerMaskBlobPC blob))
            {
                return(false);
            }

            var outfile = new FileInfo(cr2w.FileName);

            if (outfile.Directory == null)
            {
                return(false);
            }

            Directory.CreateDirectory(outfile.Directory.FullName);
            string filename = Path.GetFileNameWithoutExtension(outfile.FullName);
            string path     = outfile.Directory.FullName;

            uint atlasWidth  = blob.Header.AtlasWidth.Value;
            uint atlasHeight = blob.Header.AtlasHeight.Value;

            uint maskWidth  = blob.Header.MaskWidth.Value;
            uint maskHeight = blob.Header.MaskHeight.Value;

            uint maskWidthLow  = blob.Header.MaskWidthLow.Value;
            uint maskHeightLow = blob.Header.MaskHeightLow.Value;

            uint maskTileSize = blob.Header.MaskTileSize.Value;

            uint maskCount = blob.Header.NumLayers.Value;

            byte[] atlas;
            var    atlasRaw    = new byte[atlasWidth * atlasHeight];
            var    atlasBuffer = cr2w.Buffers[0];

            cr2wStream.Seek(atlasBuffer.Offset, SeekOrigin.Begin);
            using (var ms = new MemoryStream())
            {
                cr2wStream.DecompressAndCopySegment(ms, atlasBuffer.DiskSize, atlasBuffer.MemSize);
                atlas = ms.ToArray();
            }

            //Decode compressed data into single channel uncompressed
            //Mlmask always BC4?
            if (!BlockCompression.DecodeBC(atlas, ref atlasRaw, atlasWidth, atlasHeight, BlockCompression.BlockCompressionType.BC4))
            {
                return(false);
            }

            //{
            //    var mFilename = filename + $"__.dds";
            //    var newpath = Path.Combine(path, mFilename);
            //    using (var ddsStream = new FileStream($"{newpath}", FileMode.Create, FileAccess.Write))
            //    {
            //        DDSUtils.GenerateAndWriteHeader(ddsStream, new DDSMetadata(atlasWidth, atlasHeight, 0, EFormat.R8_UNORM, 8, false, 0, false));
            //
            //        ddsStream.Write(atlasRaw);
            //    }
            //}

            //Read tilesdata buffer into appropriate variable type
            var tileBuffer = cr2w.Buffers[0];
            var tiles      = new uint[tileBuffer.MemSize / 4];

            cr2wStream.Seek(tileBuffer.Offset, SeekOrigin.Begin);
            using (var ms = new MemoryStream())
                using (var br = new BinaryReader(ms))
                {
                    cr2wStream.DecompressAndCopySegment(ms, tileBuffer.DiskSize, tileBuffer.MemSize);
                    ms.Seek(0, SeekOrigin.Begin);

                    for (var i = 0; i < tiles.Length; i++)
                    {
                        tiles[i] = br.ReadUInt32();
                    }
                }

            byte[] maskData = new byte[maskWidth * maskHeight];

            Directory.CreateDirectory(path);
            for (int i = 0; i < maskCount; i++)
            {
                var mFilename = filename + $"_{i}.dds";
                var newpath   = Path.Combine(path, mFilename);

                //Clear instead of allocate new is faster?
                //Mandatory cause decode does not always write to every pixel
                Array.Clear(maskData, 0, maskData.Length);
                Decode(ref maskData, maskWidth, maskHeight, maskWidthLow, maskHeightLow, atlasRaw, atlasWidth, atlasHeight, tiles, maskTileSize, i);

                using (var ddsStream = new FileStream($"{newpath}", FileMode.Create, FileAccess.Write))
                {
                    DDSUtils.GenerateAndWriteHeader(ddsStream, new DDSMetadata(maskWidth, maskHeight, 0, EFormat.R8_UNORM, 8, false, 0, false));

                    ddsStream.Write(maskData);
                }

                //convert texture if neccessary
                if (uncookext != EUncookExtension.dds && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    var di = new FileInfo(outfile.FullName).Directory;
                    TexconvWrapper.Convert(di.FullName, $"{newpath}", uncookext);
                }
            }
            return(true);
        }
Пример #12
0
 protected override void PrepareStream()
 {
     SetStream(new MemoryStream(BlockCompression.ReadBlockFile(Stream)));
 }
Пример #13
0
        public static bool ConvertMultilayerMaskToDdsStreams(Multilayer_Mask mask, out List <Stream> streams)
        {
            streams = new List <Stream>();
            if (mask.RenderResourceBlob.RenderResourceBlobPC.GetValue() is not rendRenderMultilayerMaskBlobPC blob)
            {
                return(false);
            }

            uint atlasWidth  = blob.Header.AtlasWidth;
            uint atlasHeight = blob.Header.AtlasHeight;

            uint maskWidth  = blob.Header.MaskWidth;
            uint maskHeight = blob.Header.MaskHeight;

            uint maskWidthLow  = blob.Header.MaskWidthLow;
            uint maskHeightLow = blob.Header.MaskHeightLow;

            uint maskTileSize = blob.Header.MaskTileSize;

            uint maskCount = blob.Header.NumLayers;

            var atlasRaw = new byte[atlasWidth * atlasHeight];

            //Decode compressed data into single channel uncompressed
            //Mlmask always BC4?
            if (!BlockCompression.DecodeBC(blob.AtlasData.Buffer.GetBytes(), ref atlasRaw, atlasWidth, atlasHeight, BlockCompression.BlockCompressionType.BC4))
            {
                return(false);
            }

            //atlasRaw = blob.AtlasData.Buffer.GetBytes();

            //Read tilesdata buffer into appropriate variable type
            var tileBuffer = blob.TilesData.Buffer;
            var tiles      = new uint[tileBuffer.MemSize / 4];

            using (var ms = new MemoryStream(tileBuffer.GetBytes()))
                using (var br = new BinaryReader(ms))
                {
                    ms.Seek(0, SeekOrigin.Begin);

                    for (var i = 0; i < tiles.Length; i++)
                    {
                        tiles[i] = br.ReadUInt32();
                    }
                }

            var maskData = new byte[maskWidth * maskHeight];

            for (var i = 0; i < maskCount; i++)
            {
                //Clear instead of allocate new is faster?
                //Mandatory cause decode does not always write to every pixel
                Array.Clear(maskData, 0, maskData.Length);
                try
                {
                    Decode(ref maskData, maskWidth, maskHeight, maskWidthLow, maskHeightLow, atlasRaw, atlasWidth,
                           atlasHeight, tiles, maskTileSize, i);
                }
                catch
                {
                    throw;
                }

                if (WolvenTesting.IsTesting)
                {
                    continue;
                }

                var ms = new MemoryStream();
                DDSUtils.GenerateAndWriteHeader(ms, new DDSMetadata(maskWidth, maskHeight,
                                                                    1, 1, 0, 0, 0, DXGI_FORMAT.DXGI_FORMAT_R8_UNORM, TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D, 8, true));
                ms.Write(maskData);
                ms.Seek(0, SeekOrigin.Begin);
                //var stream = new MemoryStream(DDSUtils.ConvertToDdsMemory(ms, EUncookExtension.tga, DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM, false, false));
                ms = new MemoryStream(
                    Texconv.ConvertToDds(
                        new MemoryStream(Texconv.ConvertFromDds(ms, EUncookExtension.tga)),
                        EUncookExtension.tga, DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM));
                streams.Add(ms);
            }
            return(true);
        }
Пример #14
0
        public bool UncookMlmask(Stream cr2wStream, FileInfo outfile, MlmaskExportArgs args)
        {
            // read the cr2wfile
            var cr2w = _wolvenkitFileService.ReadRed4File(cr2wStream);

            if (cr2w == null || cr2w.RootChunk is not Multilayer_Mask mlmask || mlmask.RenderResourceBlob.RenderResourceBlobPC.Chunk is not rendRenderMultilayerMaskBlobPC blob)
            {
                return(false);
            }

            uint atlasWidth  = blob.Header.AtlasWidth;
            uint atlasHeight = blob.Header.AtlasHeight;

            uint maskWidth  = blob.Header.MaskWidth;
            uint maskHeight = blob.Header.MaskHeight;

            uint maskWidthLow  = blob.Header.MaskWidthLow;
            uint maskHeightLow = blob.Header.MaskHeightLow;

            uint maskTileSize = blob.Header.MaskTileSize;

            uint maskCount = blob.Header.NumLayers;

            var atlasRaw = new byte[atlasWidth * atlasHeight];

            //Decode compressed data into single channel uncompressed
            //Mlmask always BC4?
            if (!BlockCompression.DecodeBC(blob.AtlasData.Buffer.GetBytes(), ref atlasRaw, atlasWidth, atlasHeight, BlockCompression.BlockCompressionType.BC4))
            {
                return(false);
            }

            //{
            //    var mFilename = filename + $"__.dds";
            //    var newpath = Path.Combine(path, mFilename);
            //    using (var ddsStream = new FileStream($"{newpath}", FileMode.Create, FileAccess.Write))
            //    {
            //        DDSUtils.GenerateAndWriteHeader(ddsStream, new DDSMetadata(atlasWidth, atlasHeight, 0, EFormat.R8_UNORM, 8, false, 0, false));
            //
            //        ddsStream.Write(atlasRaw);
            //    }
            //}

            //Read tilesdata buffer into appropriate variable type
            var tileBuffer = blob.TilesData.Buffer;
            var tiles      = new uint[tileBuffer.MemSize / 4];

            using (var ms = new MemoryStream(tileBuffer.GetBytes()))
                using (var br = new BinaryReader(ms))
                {
                    ms.Seek(0, SeekOrigin.Begin);

                    for (var i = 0; i < tiles.Length; i++)
                    {
                        tiles[i] = br.ReadUInt32();
                    }
                }

            // write texture to file
            var subdir = new DirectoryInfo(Path.GetFullPath(outfile.FullName));

            if (!subdir.Exists)
            {
                Directory.CreateDirectory(subdir.FullName);
            }

            var maskData = new byte[maskWidth * maskHeight];
            var masks    = new List <string>();

            for (var i = 0; i < maskCount; i++)
            {
                //Clear instead of allocate new is faster?
                //Mandatory cause decode does not always write to every pixel
                Array.Clear(maskData, 0, maskData.Length);
                try
                {
                    Decode(ref maskData, maskWidth, maskHeight, maskWidthLow, maskHeightLow, atlasRaw, atlasWidth,
                           atlasHeight, tiles, maskTileSize, i);
                }
                catch
                {
                    return(false);
                }

                if (WolvenTesting.IsTesting)
                {
                    continue;
                }

                // create dds stream
                using var ms = new MemoryStream();
                DDSUtils.GenerateAndWriteHeader(ms, new DDSMetadata(
                                                    maskWidth, maskHeight,
                                                    1, 1, 0, 0, 0, DXGI_FORMAT.DXGI_FORMAT_R8_UNORM, TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D, 8, true));
                ms.Write(maskData);


                var newpath = Path.Combine(subdir.FullName, $"{i}.dds");
                if (args.UncookExtension == EMlmaskUncookExtension.dds)
                {
                    using var ddsStream = new FileStream($"{newpath}", FileMode.Create, FileAccess.Write);
                    ms.Seek(0, SeekOrigin.Begin);
                    ms.CopyTo(ddsStream);

                    masks.Add($"{subdir.Name}/{i}.dds");
                }
                //else if (args.UncookExtension == EUncookExtension.tga)
                //{
                //    using (var ddsStream = new FileStream($"{newpath}.tga", FileMode.Create, FileAccess.Write))
                //    {
                //        ms.Seek(0, SeekOrigin.Begin);
                //        using (var ms2 = new MemoryStream(DDSUtils.ConvertFromDdsMemory(ms, EUncookExtension.tga)))
                //        {
                //            var br = new BinaryReader(ms2);
                //            br.BaseStream.Seek(14, SeekOrigin.Begin);
                //            ushort height = br.ReadUInt16();
                //            br.BaseStream.Seek(17, SeekOrigin.Begin);
                //            byte descriptor = br.ReadByte();

                //            var bw = new BinaryWriter(ms2);
                //            bw.BaseStream.Seek(10, SeekOrigin.Begin);
                //            bw.Write(height);

                //            bw.BaseStream.Seek(17, SeekOrigin.Begin);
                //            bw.Write(descriptor ^ 0b00001000);

                //            bw.Flush();

                //            ms2.Position = 0;
                //            ms2.CopyTo(ddsStream);
                //        }
                //    }
                //}
                else
                {
                    // convert
                    ms.Seek(0, SeekOrigin.Begin);
                    if (!Texconv.ConvertFromDdsAndSave(ms, newpath, args))
                    {
                        return(false);
                    }

                    {
                        masks.Add($"{subdir.Name}/{i}.png");
                    }
                }
            }

            // write metadata
            var masklist = Path.ChangeExtension(outfile.FullName, "masklist");

            File.WriteAllLines(masklist, masks.ToArray());

            return(true);
        }
Пример #15
0
        public bool UncookMlmask(Stream cr2wStream, FileInfo outfile, MlmaskExportArgs args)
        {
            // read the cr2wfile
            var cr2w = _wolvenkitFileService.TryReadCr2WFile(cr2wStream);

            if (cr2w == null)
            {
                return(false);
            }

            //We need 2 buffers one for atlas one for tile data

            if (!(cr2w.Chunks.FirstOrDefault()?.Data is Multilayer_Mask mlmask) ||
                !(cr2w.Chunks[1]?.Data is rendRenderMultilayerMaskBlobPC blob))
            {
                return(false);
            }

            uint atlasWidth  = blob.Header.AtlasWidth.Value;
            uint atlasHeight = blob.Header.AtlasHeight.Value;

            uint maskWidth  = blob.Header.MaskWidth.Value;
            uint maskHeight = blob.Header.MaskHeight.Value;

            uint maskWidthLow  = blob.Header.MaskWidthLow.Value;
            uint maskHeightLow = blob.Header.MaskHeightLow.Value;

            uint maskTileSize = blob.Header.MaskTileSize.Value;

            uint maskCount = blob.Header.NumLayers.Value;

            byte[] atlas;
            var    atlasRaw    = new byte[atlasWidth * atlasHeight];
            var    atlasBuffer = cr2w.Buffers[0];

            cr2wStream.Seek(atlasBuffer.Offset, SeekOrigin.Begin);
            using (var ms = new MemoryStream())
            {
                cr2wStream.DecompressAndCopySegment(ms, atlasBuffer.DiskSize, atlasBuffer.MemSize);
                atlas = ms.ToArray();
            }

            //Decode compressed data into single channel uncompressed
            //Mlmask always BC4?
            if (!BlockCompression.DecodeBC(atlas, ref atlasRaw, atlasWidth, atlasHeight, BlockCompression.BlockCompressionType.BC4))
            {
                return(false);
            }

            //{
            //    var mFilename = filename + $"__.dds";
            //    var newpath = Path.Combine(path, mFilename);
            //    using (var ddsStream = new FileStream($"{newpath}", FileMode.Create, FileAccess.Write))
            //    {
            //        DDSUtils.GenerateAndWriteHeader(ddsStream, new DDSMetadata(atlasWidth, atlasHeight, 0, EFormat.R8_UNORM, 8, false, 0, false));
            //
            //        ddsStream.Write(atlasRaw);
            //    }
            //}

            //Read tilesdata buffer into appropriate variable type
            var tileBuffer = cr2w.Buffers[1];
            var tiles      = new uint[tileBuffer.MemSize / 4];

            cr2wStream.Seek(tileBuffer.Offset, SeekOrigin.Begin);
            using (var ms = new MemoryStream())
                using (var br = new BinaryReader(ms))
                {
                    cr2wStream.DecompressAndCopySegment(ms, tileBuffer.DiskSize, tileBuffer.MemSize);
                    ms.Seek(0, SeekOrigin.Begin);

                    for (var i = 0; i < tiles.Length; i++)
                    {
                        tiles[i] = br.ReadUInt32();
                    }
                }

            byte[] maskData = new byte[maskWidth * maskHeight];

            for (int i = 0; i < maskCount; i++)
            {
                var mFilename = Path.GetFileNameWithoutExtension(outfile.FullName) + $"_{i}.dds";
                //var mFilename = Path.GetFileName(outfile.FullName) + $".{i}.dds"; // TODO:we should use this at some point
                var newpath = Path.Combine(outfile.Directory.FullName, mFilename);

                //Clear instead of allocate new is faster?
                //Mandatory cause decode does not always write to every pixel
                Array.Clear(maskData, 0, maskData.Length);
                try
                {
                    Decode(ref maskData, maskWidth, maskHeight, maskWidthLow, maskHeightLow, atlasRaw, atlasWidth,
                           atlasHeight, tiles, maskTileSize, i);
                }
                catch
                {
                    return(false);
                }

                if (WolvenTesting.IsTesting)
                {
                    continue;
                }

                // write texture to file
                using (var ms = new MemoryStream())
                {
                    // create dds stream
                    DDSUtils.GenerateAndWriteHeader(ms, new DDSMetadata(
                                                        maskWidth, maskHeight,
                                                        1, 1, 0, 0, 0, DXGI_FORMAT.DXGI_FORMAT_R8_UNORM, TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D, 8, true));
                    ms.Write(maskData);

                    if (args.UncookExtension == EUncookExtension.dds)
                    {
                        using (var ddsStream = new FileStream($"{newpath}", FileMode.Create, FileAccess.Write))
                        {
                            ms.Seek(0, SeekOrigin.Begin);
                            ms.CopyTo(ddsStream);
                        }
                    }
                    else
                    {
                        // convert
                        ms.Seek(0, SeekOrigin.Begin);
                        if (!DDSUtils.ConvertFromDdsAndSave(ms, newpath, args))
                        {
                            return(false);
                        }
                    }
                }
            }
            return(true);
        }