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