//public static DdsImage Xbm2Dds(CBitmapTexture xbm, byte[] rawimage) //{ // if (xbm == null || rawimage == null) // return null; // return new DdsImage(Xbm2DdsBytes(xbm, rawimage)); //} public static byte[] Xbm2DdsBytes(CBitmapTexture xbm) { if (xbm == null) { return(null); } int residentMipIndex = xbm.GetVariableByName("ResidentMipIndex") == null ? 0 : (int)((CUInt8)xbm.GetVariableByName("ResidentMipIndex")).val; byte[] bytesource; // handle cooked xbms if (xbm.GetVariableByName("SourceData") == null) { bytesource = xbm.Residentmip.Bytes; } // handle imported xbms else { bytesource = xbm.Mips.elements[residentMipIndex].Bytes; } using (var ms = new MemoryStream()) using (var bw = new BinaryWriter(ms)) { DDSUtils.GenerateAndWriteHeader(bw.BaseStream, Xbm2Ddsheader(xbm)); bw.Write(bytesource); ms.Flush(); return(ms.ToArray()); } }
public void TestThreeSingle() { byte[] concat_aab = DDSUtils.ConcatByteArrays(a, a, b); for (int k = 0; k < aab.Length; k++) { Assert.AreEqual(aab[k], concat_aab[k]); } }
private byte[] calculateRampRateWord(double RampRate) { // Calculate the the 8bit word. since it is assumed // to be nonzero a 0 is a one (and a 255 a 256) // that's why we substract one from the result. Cheers int word = (int)(Math.Round(RampRate * rampStep)) - 1; // Int has 4 bytes, but Ramp Rate word only 1 return(DDSUtils.IntToMSByteArray(word, 1).ToArray()); }
static List <MemoryStream> ContainTextureStreams(CR2WFile cr2w, MemoryStream texbuffer) { List <MemoryStream> textureStreams = new List <MemoryStream>(); int Index = int.MaxValue; for (int i = 0; i < cr2w.Chunks.Count; i++) { if (cr2w.Chunks[i].REDType == "rendRenderMorphTargetMeshBlob") { Index = i; } } int Count = (cr2w.Chunks[Index].Data as rendRenderMorphTargetMeshBlob).Header.TargetTextureDiffsData.Count; int texCount = 0; List <UInt32> TargetDiffsDataOffset = new List <UInt32>(); List <UInt32> TargetDiffsDataSize = new List <UInt32>(); List <UInt32> TargetDiffsMipLevelCounts = new List <UInt32>(); List <UInt32> TargetDiffsWidth = new List <UInt32>(); for (int i = 0; i < Count; i++) { if ((cr2w.Chunks[Index].Data as rendRenderMorphTargetMeshBlob).Header.TargetTextureDiffsData[i].TargetDiffsDataSize.Count == 0) { break; } TargetDiffsDataOffset.Add((cr2w.Chunks[Index].Data as rendRenderMorphTargetMeshBlob).Header.TargetTextureDiffsData[i].TargetDiffsDataOffset[0].Value); TargetDiffsDataSize.Add((cr2w.Chunks[Index].Data as rendRenderMorphTargetMeshBlob).Header.TargetTextureDiffsData[i].TargetDiffsDataSize[0].Value); TargetDiffsMipLevelCounts.Add((cr2w.Chunks[Index].Data as rendRenderMorphTargetMeshBlob).Header.TargetTextureDiffsData[i].TargetDiffsMipLevelCounts[0].Value); TargetDiffsWidth.Add((cr2w.Chunks[Index].Data as rendRenderMorphTargetMeshBlob).Header.TargetTextureDiffsData[i].TargetDiffsWidth[0].Value); texCount++; } BinaryReader texbr = new BinaryReader(texbuffer); for (int i = 0; i < texCount; i++) { texbuffer.Position = TargetDiffsDataOffset[i]; byte[] bytes = texbr.ReadBytes((int)TargetDiffsDataSize[i]); MemoryStream ms = new MemoryStream(); DDSMetadata metadata = new DDSMetadata(TargetDiffsWidth[i], TargetDiffsWidth[i], TargetDiffsMipLevelCounts[i], EFormat.BC7_UNORM, 16, false, 0, true); DDSUtils.GenerateAndWriteHeader(ms, metadata); BinaryWriter bw = new BinaryWriter(ms); bw.Write(bytes); textureStreams.Add(ms); } return(textureStreams); }
/// <summary> /// Create a byte array from a Redengine CBitmapTexture with Wkit DDS Utility /// </summary> /// <param name="xbm"></param> /// <returns></returns> public static byte[] Xbm2DdsBytes(CBitmapTexture xbm) { if (xbm == null) { return(null); } using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); DDSUtils.GenerateAndWriteHeader(bw.BaseStream, GetDDSMetadata(xbm)); bw.Write(xbm.GetBytes()); ms.Flush(); return(ms.ToArray()); }
private static List <MemoryStream> ContainTextureStreams(rendRenderMorphTargetMeshBlob blob, MemoryStream texbuffer) { var textureStreams = new List <MemoryStream>(); var Count = blob.Header.TargetTextureDiffsData.Count; var texCount = 0; var TargetDiffsDataOffset = new List <uint>(); var TargetDiffsDataSize = new List <uint>(); var TargetDiffsMipLevelCounts = new List <uint>(); var TargetDiffsWidth = new List <uint>(); for (var i = 0; i < Count; i++) { if (blob.Header.TargetTextureDiffsData[i].TargetDiffsDataSize.Count == 0) { break; } TargetDiffsDataOffset.Add(blob.Header.TargetTextureDiffsData[i].TargetDiffsDataOffset[0]); TargetDiffsDataSize.Add(blob.Header.TargetTextureDiffsData[i].TargetDiffsDataSize[0]); TargetDiffsMipLevelCounts.Add(blob.Header.TargetTextureDiffsData[i].TargetDiffsMipLevelCounts[0]); TargetDiffsWidth.Add(blob.Header.TargetTextureDiffsData[i].TargetDiffsWidth[0]); texCount++; } var texbr = new BinaryReader(texbuffer); for (var i = 0; i < texCount; i++) { texbuffer.Position = TargetDiffsDataOffset[i]; var bytes = texbr.ReadBytes((int)TargetDiffsDataSize[i]); var ms = new MemoryStream(); var metadata = new DDSMetadata( TargetDiffsWidth[i], TargetDiffsWidth[i], 1, 1, TargetDiffsMipLevelCounts[i], 0, 0, DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM, TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D, 16, true); DDSUtils.GenerateAndWriteHeader(ms, metadata); var bw = new BinaryWriter(ms); bw.Write(bytes); textureStreams.Add(ms); } return(textureStreams); }
public static byte[] Xbm2DdsBytes(CBitmapTexture xbm, byte[] bytesource) { if (xbm == null) { return(null); } using (var ms = new MemoryStream()) using (var bw = new BinaryWriter(ms)) { DDSUtils.GenerateAndWriteHeader(bw.BaseStream, Xbm2Ddsheader(xbm)); bw.Write(bytesource); ms.Flush(); return(ms.ToArray()); } }
public Message PhaseAsChannelWord(double Phase) { // The Channel Word Register are 4byte long and the PhaseOffsetWord only 14bit long // the PhaseOffsetWord has to be MSB aligned in the register int PhaseOffsetWord = calculatePhaseOffsetWord(Phase); // First shift the word by two bits to the left to make it MSB aligned // for a two byte register PhaseOffsetWord = PhaseOffsetWord << 2; // Then convert it to MSBytearray Message msg = DDSUtils.IntToMSByteArray(PhaseOffsetWord, 2); // Finally make sure that the two last bits are zero by doing a // bitwise and with 1111 1100 = 0xFC msg[1] = (byte)(msg[1] & 0xFC); // Add to zero bytes for the remaining byte registers msg.AddRange(new byte[] { 0x00, 0x00 }); return(msg); }
/// <summary> /// Send message to EP1. This routine will append a checksum byte at the end of the message. So /// don't include it in the message passed to this routine /// </summary> /// <param name="message">Message to be sent without checksum byte</param> private void sendToEP1(Message message) { if (log.IsDebugEnabled) { log.DebugFormat("Recieved message to send to EP1: {0}", message.ToString()); } byte checksum = DDSUtils.Checksum(message); if (log.IsDebugEnabled) { log.DebugFormat("Appending checksum byte 0x{0} to message", checksum); } message.Add(checksum); if (log.IsDebugEnabled) { log.DebugFormat("Sending message to EP1: {0}", message.ToString()); } device.SendDataToEP1(message.ToArray()); }
public static DdsImage Xbm2Dds(CR2WExportWrapper imagechunk) { try { var image = ((CBitmapTexture)(imagechunk.data)).Image; var compression = imagechunk.GetVariableByName("compression").ToString(); var width = uint.Parse(imagechunk.GetVariableByName("width").ToString()); var height = uint.Parse(imagechunk.GetVariableByName("height").ToString()); var unk2 = uint.Parse(imagechunk.GetVariableByName("unk2").ToString()); var residentMipIndex = imagechunk.GetVariableByName("residentMipIndex") != null?uint.Parse(imagechunk.GetVariableByName("residentMipIndex").ToString()) : 0; var mips = (CBufferUInt32 <CVector3 <CUInt32> >)imagechunk.GetVariableByName("mips"); var tempfile = new MemoryStream(); var format = ETextureFormat.TEXFMT_R8G8B8A8; switch (compression) { case "TCM_DXTNoAlpha": format = ETextureFormat.TEXFMT_BC1; break; case "TCM_DXTAlpha": format = ETextureFormat.TEXFMT_BC3; break; case "TCM_NormalsHigh": format = ETextureFormat.TEXFMT_BC3; break; case "TCM_Normals": format = ETextureFormat.TEXFMT_BC1; break; case "TCM_NormalsGloss": format = ETextureFormat.TEXFMT_BC3; break; case "TCM_QualityControl": format = ETextureFormat.TEXFMT_BC3; break; default: throw new Exception("Invalid compression type! [" + compression + "]"); } using (var bw = new BinaryWriter(tempfile)) { var residentmipwidth = mips.elements[(int)residentMipIndex].x.val; var residentmipheight = mips.elements[(int)residentMipIndex].y.val; var residentmipcount = mips.elements.Count - residentMipIndex; var metadata = new DDSMetadata(residentmipwidth, residentmipheight, (uint)residentmipcount, format); DDSUtils.GenerateAndWriteHeader(bw.BaseStream, metadata); bw.Write(image.Bytes); } tempfile.Flush(); #if DEBUG //File.WriteAllBytes(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\asd.dds",tempfile.ToArray()); #endif return(new DdsImage(tempfile.ToArray())); } catch (Exception e) { string message = e.Message; string caption = "Error!"; MessageBoxButtons buttons = MessageBoxButtons.OK; MessageBox.Show(message, caption, buttons); return(null); } }
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); }
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); }
private byte[] calculateFrequencyTuningWordAsBytes(double frequency) { int FTW = calculateFrequencyTuningWord(frequency); return(DDSUtils.IntToMSByteArray(FTW).ToArray()); }
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); }
public void Import(FileInfo txtimageList, FileInfo outFile) { var files = File.ReadAllLines(txtimageList.FullName); #region InitandVerify mlmask = new MLMaskContainer(); List <RawTexContainer> textures = new List <RawTexContainer>(); RawTexContainer white = new RawTexContainer(); white._pixels = new Byte[16]; Array.Fill <Byte>(white._pixels, 255); white._width = 4; white._height = 4; textures.Add(white); int lineIdx = 1; foreach (var f in files) { if (!File.Exists(f)) { throw new FileNotFoundException($"Line{{lineIdx}}: \"{f}\" Make sure the file path is valid and exists (paths are specified line by line in ascending layer order in masklist)"); } var ms = new MemoryStream(File.ReadAllBytes(f)); var s = Path.GetExtension(f).ToLower(); var euncook = Enum.Parse <EUncookExtension>(Path.GetExtension(f).ToLower().Replace(".", "")); if (euncook != EUncookExtension.dds) { ms = new MemoryStream(DDSUtils.ConvertToDdsMemory(ms, euncook, DXGI_FORMAT.DXGI_FORMAT_R8_UNORM)); } else { // why dds to dds?, to make sure format is r8_unorm ms = new MemoryStream(DDSUtils.ConvertToDdsMemory(new MemoryStream(DDSUtils.ConvertFromDdsMemory(ms, EUncookExtension.tga)), EUncookExtension.tga, DXGI_FORMAT.DXGI_FORMAT_R8_UNORM)); } ms.Seek(0, SeekOrigin.Begin); DDS_HEADER header; DDSUtils.TryReadDdsHeader(ms, out header); if (header.dwWidth != header.dwHeight) { throw new Exception($"Texture {f}: width={header.dwWidth},height={header.dwHeight} must have an aspect ratio of 1:1"); } // One bitset check if ((((header.dwWidth - 1) & header.dwHeight) != 0) || (header.dwWidth == 0)) { throw new Exception($"Texture {f}: width={header.dwWidth},height={header.dwHeight} must have dimensions in powers of 2"); } //if (header.dwMipMapCount > 1) // throw new Exception($"Texture {f}: Mipmaps={header.dwMipMapCount}, mimap count must be equal to 1"); //if ((ms.Length - headerLength) != (header.dwWidth * header.dwHeight)) // throw new Exception("Not R8_UNORM 8bpp image format or more than 1mipmaps or rowstride is not equal to width or its a dx10 dds(unsupported)"); var br = new BinaryReader(ms); ms.Seek(headerLength, SeekOrigin.Begin); Byte[] bytes = br.ReadBytes((int)(header.dwWidth * header.dwHeight)); bool whiteCheck = true; for (int i = 0; i < bytes.Length; i++) { if (bytes[i] != 255) { whiteCheck = false; break; } } if (whiteCheck) { throw new Exception("No need to provide the 1st/any blank white mask layer, tool will generate 1st blank white layer automatically"); } RawTexContainer tex = new RawTexContainer(); tex._width = header.dwWidth; tex._height = header.dwHeight; tex._pixels = bytes; textures.Add(tex); lineIdx++; } mlmask.layers = textures.ToArray(); #endregion Create(); Write(outFile); }
/// <summary> /// Extracts all Files to the specified directory. /// </summary> /// <param name="outDir"></param> /// <returns></returns> public int ExtractAll(DirectoryInfo outDir, bool extract = true, bool uncook = false, EUncookExtension uncookext = EUncookExtension.tga) { var _maincontroller = ServiceLocator.Default.ResolveType <IMainController>(); using var pb = new ProgressBar(); using var p1 = pb.Progress.Fork(); int progress = 0; using var mmf = MemoryMappedFile.CreateFromFile(Filepath, FileMode.Open, Mmfhash, 0, MemoryMappedFileAccess.Read); Parallel.For(0, FileCount, new ParallelOptions { MaxDegreeOfParallelism = 8 }, i => { var info = Files.Values.ToList()[i]; var(file, buffers) = GetFileData(info.NameHash64, mmf); var hash = info.NameHash64; string name = $"{hash:X2}.bin"; if (_maincontroller.Hashdict.ContainsKey(hash)) { name = _maincontroller.Hashdict[hash]; } var outfile = new FileInfo(Path.Combine(outDir.FullName, $"{name}")); if (outfile.Directory == null) { return; } // write main file if (extract) { Directory.CreateDirectory(outfile.Directory.FullName); File.WriteAllBytes(outfile.FullName, file); } // write buffers for (int j = 0; j < buffers.Count; j++) { if (uncook) { #region textures if (Path.GetExtension(name) != ".xbm") { continue; } if (buffers.Count > 1) { //TODO: Log } // read cr2w using var ms = new MemoryStream(file); using var br = new BinaryReader(ms); var cr2w = new CR2WFile(); var result = cr2w.Read(br); if (result != EFileReadErrorCodes.NoError) { continue; } if (!(cr2w.Chunks.FirstOrDefault()?.data is CBitmapTexture xbm) || !(cr2w.Chunks[1]?.data is rendRenderTextureBlobPC blob)) { continue; } // create dds header var width = blob.Header.SizeInfo.Width.val; var height = blob.Header.SizeInfo.Height.val; var mips = blob.Header.TextureInfo.MipCount.val; var slicecount = blob.Header.TextureInfo.SliceCount.val; var alignment = blob.Header.TextureInfo.DataAlignment.val; Directory.CreateDirectory(outfile.Directory.FullName); using (var stream = new FileStream($"{outfile}.dds", FileMode.Create, FileAccess.Write)) { DDSUtils.GenerateAndWriteHeader(stream, new DDSMetadata(width, height, mips, EFormat.BC7_UNORM, alignment, false, slicecount)); var buffer = buffers[j]; stream.Write(buffer); } // convert to texture if (uncookext != EUncookExtension.dds) { try { var di = new FileInfo(outfile.FullName).Directory; TexconvWrapper.Convert(di.FullName, $"{outfile}.dds", uncookext); } catch (Exception e) { Console.WriteLine(e); } } #endregion }
private int ExtractSingleInner(MemoryMappedFile mmf, ulong hash, DirectoryInfo outDir, bool extract = true, bool uncook = false, EUncookExtension uncookext = EUncookExtension.tga) { var maincontroller = ServiceLocator.Default.ResolveType <IMainController>(); var logger = ServiceLocator.Default.ResolveType <ILoggerService>(); var success = false; var(file, buffers) = GetFileData(hash, mmf); string name = $"{hash:X2}.bin"; if (maincontroller.Hashdict.ContainsKey(hash)) { name = maincontroller.Hashdict[hash]; } var outfile = new FileInfo(Path.Combine(outDir.FullName, $"{name}")); if (outfile.Directory == null) { return(0); } // write main file if (extract) { Directory.CreateDirectory(outfile.Directory.FullName); File.WriteAllBytes(outfile.FullName, file); success = true; } // write buffers for (int j = 0; j < buffers.Count; j++) { if (uncook) { #region textures if (Path.GetExtension(name) != ".xbm") { continue; } if (buffers.Count > 1) { //TODO: Log } // read cr2w using var ms = new MemoryStream(file); using var br = new BinaryReader(ms); var cr2w = new CR2WFile(); var result = cr2w.Read(br); if (result != EFileReadErrorCodes.NoError) { continue; } if (!(cr2w.Chunks.FirstOrDefault()?.data is CBitmapTexture xbm) || !(cr2w.Chunks[1]?.data is rendRenderTextureBlobPC blob)) { continue; } // create dds header try { var width = blob.Header.SizeInfo.Width.val; var height = blob.Header.SizeInfo.Height.val; var mips = blob.Header.TextureInfo.MipCount.val; var slicecount = blob.Header.TextureInfo.SliceCount.val; var alignment = blob.Header.TextureInfo.DataAlignment.val; var compression = xbm.Setup.Compression?.WrappedEnum ?? Enums.ETextureCompression.TCM_None; var texformat = CommonFunctions.GetEformatFromCompression(compression); Directory.CreateDirectory(outfile.Directory.FullName); using (var stream = new FileStream($"{outfile}.dds", FileMode.Create, FileAccess.Write)) { DDSUtils.GenerateAndWriteHeader(stream, new DDSMetadata(width, height, mips, texformat, alignment, false, slicecount, false)); var buffer = buffers[j]; stream.Write(buffer); } // success success = true; } catch { success = false; continue; } // convert to texture if (uncookext != EUncookExtension.dds) { try { var di = new FileInfo(outfile.FullName).Directory; TexconvWrapper.Convert(di.FullName, $"{outfile}.dds", uncookext); } catch (Exception e) { // silent } } #endregion } else if (extract) { var buffer = buffers[j]; var bufferpath = $"{outfile}.{j}.buffer"; Directory.CreateDirectory(outfile.Directory.FullName); File.WriteAllBytes(bufferpath, buffer); } } return(success ? 1 : 0); }
private int UncookSingleInner(MemoryMappedFile mmf, ulong hash, DirectoryInfo outDir, EUncookExtension uncookext = EUncookExtension.tga) { var uncooksuccess = false; var(file, buffers) = GetFileData(hash, mmf); if (!Files.ContainsKey(hash)) { return(-1); } string name = Files[hash].NameStr; // checks var outfile = new FileInfo(Path.Combine(outDir.FullName, $"{name}")); if (outfile.Directory == null) { return(-1); } if (buffers.Count > 1) { return(-1); //TODO: can that happen? } var cr2w = new CR2WFile(); using var ms = new MemoryStream(file); using var br = new BinaryReader(ms); cr2w.ReadImportsAndBuffers(br); if (cr2w.StringDictionary[1] != "CBitmapTexture") { return(-1); } br.BaseStream.Seek(0, SeekOrigin.Begin); var result = cr2w.Read(br); if (result != EFileReadErrorCodes.NoError) { return(-1); } if (!(cr2w.Chunks.FirstOrDefault()?.data is CBitmapTexture xbm) || !(cr2w.Chunks[1]?.data is rendRenderTextureBlobPC blob)) { return(-1); } // write buffers foreach (var b in buffers) { #region textures // create dds header var newpath = Path.ChangeExtension(outfile.FullName, "dds"); try { var width = blob.Header.SizeInfo.Width.val; var height = blob.Header.SizeInfo.Height.val; var mips = blob.Header.TextureInfo.MipCount.val; var slicecount = blob.Header.TextureInfo.SliceCount.val; var alignment = blob.Header.TextureInfo.DataAlignment.val; Enums.ETextureRawFormat rawfmt = Enums.ETextureRawFormat.TRF_Invalid; if (xbm.Setup.RawFormat?.WrappedEnum != null) { rawfmt = xbm.Setup.RawFormat.WrappedEnum; } else { } Enums.ETextureCompression compression = Enums.ETextureCompression.TCM_None; if (xbm.Setup.Compression?.WrappedEnum != null) { compression = xbm.Setup.Compression.WrappedEnum; } else { } var texformat = CommonFunctions.GetDXGIFormatFromXBM(compression, rawfmt); Directory.CreateDirectory(outfile.Directory.FullName); using (var stream = new FileStream($"{newpath}", FileMode.Create, FileAccess.Write)) { DDSUtils.GenerateAndWriteHeader(stream, new DDSMetadata(width, height, mips, texformat, alignment, false, slicecount, true)); var buffer = b; stream.Write(buffer); } // success uncooksuccess = true; } catch { uncooksuccess = false; continue; } // convert to texture if (uncookext != EUncookExtension.dds) { try { var di = new FileInfo(outfile.FullName).Directory; TexconvWrapper.Convert(di.FullName, $"{newpath}", uncookext); } catch (Exception e) { // silent } } #endregion } return(uncooksuccess ? 1 : 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); }
/// <summary> /// /// </summary> /// <param name="outpath"></param> public void Write(string outpath, ILoggerService logger = null) { logger?.LogString($"[TextureCache] Writing begun.", Logtype.Important); logger?.LogString($"[TextureCache] Found {Files.Count} files.", Logtype.Important); int page = 0; using (var cacheFileStream = new FileStream(outpath, FileMode.Create, FileAccess.Write)) using (var cacheWriter = new BinaryWriter(cacheFileStream)) { // write files #region Write Compressed Files foreach (var ti in Files) { ti.ParentFile = outpath; var ddsfile = ti.FullName; if (!File.Exists(ddsfile)) { continue; } // checks long ddssize = -1; // get dds file size //TODO: make this better using (var readfs = new FileStream(ddsfile, FileMode.Open, FileAccess.Read)) { ddssize = readfs.Length - 128; } if (ti.IsCube != 0) { if (ddssize % 6 != 0) { throw new CacheWritingException($"{ti.Name} incorrect cubemap (not divisible by 6)."); } } ti.UncompressedSize = (uint)ddssize; var ddsoffset = (uint)128; // dds header size var startoffset = cacheFileStream.Position; // check for alignment if (cacheFileStream.Position % 4096 != 0) { throw new CacheWritingException($"{ti.Name} improperly aligned (pos: {cacheFileStream.Position})."); } // cubemaps if (ti.IsCube != 0) { // dds cube textures are structured in 6 faces (each with mipmaps) // files in the texture cache are structured by mipmaps (each with 6 faces) // sigh... using (var file = MemoryMappedFile.CreateFromFile(ddsfile, FileMode.Open)) { #region Main Image // get main image var imgsize = DDSUtils.CalculateMipMapSize(ti.BaseWidth, ti.BaseHeight, ti.Format); ti.PageOffset = (uint)(cacheFileStream.Position / 4096); // all faces and mipmaps get compressed at once using (var allfacesms = new MemoryStream()) { for (int i = 0; i < 6; i++) { var faceoffset = 128 + ddssize / 6 * i; using (var vs = file.CreateViewStream(faceoffset, imgsize, MemoryMappedFileAccess.Read)) { vs.CopyTo(allfacesms); } } // compress the assembled stream of all faces ti.Size = 6 * imgsize; ti.ZSize = (uint)WriteImgFromStream(allfacesms, cacheWriter, (byte)(ti.NumMipOffsets)).Item1; } #endregion Main Image #region MipMaps // no mipmaps means NumMipOffsets is 0 var totalmipmapsizeuptonow = 0; for (int i = 0; i < ti.NumMipOffsets; i++) { uint mipWidth = (uint)(ti.BaseWidth / (Math.Pow(2, i + 1))); uint mipHeight = (uint)(ti.BaseHeight / (Math.Pow(2, i + 1))); var mipsize = DDSUtils.CalculateMipMapSize(mipWidth, mipHeight, ti.Format); // last 6 mips are concatenated into one compressed buffer // TODO: non-square textures. fml if (mipWidth == 32) { mipsize = 696; } byte idx = (byte)(ti.NumMipOffsets - 1 - i); // all faces and mipmaps get compressed at once using (var allmips = new MemoryStream()) { for (int j = 0; j < 6; j++) { // find correct mip: divide dds by 6 + the main size + the size of all mips before the current one var mipoffset = (ddssize / 6 * j) + (ti.Size / 6) + (totalmipmapsizeuptonow); using (var vs = file.CreateViewStream(mipoffset, mipsize, MemoryMappedFileAccess.Read)) { vs.CopyTo(allmips); } } // compress the assembled stream of all mipmaps totalmipmapsizeuptonow += (int)mipsize; mipsize *= 6; //TODO var(zsize, offset) = WriteImgFromStream(allmips, cacheWriter, idx); ti.MipMapInfo.Add(new MipmapInfo(offset, (uint)zsize, (uint)mipsize, idx)); MipOffsets.Add((uint)offset); } ti.MipOffsetIndex = MipOffsets.Count; ddsoffset += mipsize; } #endregion MipMaps } } else { using (var file = MemoryMappedFile.CreateFromFile(ddsfile, FileMode.Open)) { #region Main Image // get main image ti.Size = DDSUtils.CalculateMipMapSize(ti.BaseWidth, ti.BaseHeight, ti.Format); ti.PageOffset = (uint)(cacheFileStream.Position / 4096); // compress image using (var vs = file.CreateViewStream(ddsoffset, ti.Size, MemoryMappedFileAccess.Read)) { ti.ZSize = (uint)WriteImgFromStream(vs, cacheWriter, (byte)(ti.NumMipOffsets)).Item1; } #endregion Main Image #region MipMaps // no mipmaps means NumMipOffsets is 0 for (int i = 0; i < ti.NumMipOffsets; i++) { uint mipWidth = (uint)(ti.BaseWidth / (Math.Pow(2, i + 1))); uint mipHeight = (uint)(ti.BaseHeight / (Math.Pow(2, i + 1))); var mipsize = DDSUtils.CalculateMipMapSize(mipWidth, mipHeight, ti.Format); // last 6 mips are concatenated into one compressed buffer // TODO: non-square textures. fml if (mipWidth == 32) { mipsize = 696; } byte idx = (byte)(ti.NumMipOffsets - 1 - i); // compress image and write to cache using (var viewstream = file.CreateViewStream(ddsoffset, mipsize, MemoryMappedFileAccess.Read)) { var(zsize, offset) = WriteImgFromStream(viewstream, cacheWriter, idx); ti.MipMapInfo.Add(new MipmapInfo(offset, (uint)zsize, (uint)mipsize, idx)); MipOffsets.Add((uint)offset); } ti.MipOffsetIndex = MipOffsets.Count; ddsoffset += mipsize; } #endregion MipMaps } } //TODO: check if the complete dds file is read // padding to next page (4096 byte pages) var endoffset = cacheFileStream.Position; page = (int)(endoffset / 4096); var paddinglen = (4096 * (page + 1)) - endoffset; var padding = new byte[paddinglen]; cacheWriter.Write(padding); ti.CompressedSize = (uint)(endoffset - startoffset); logger?.LogString($"Written {ti.Name}", Logtype.Normal); } #endregion Write Compressed Files // calculate final stats UsedPages = (uint)page + 1; EntryCount = (uint)Files.Count; MipTableEntryCount = (uint)MipOffsets.Count; // write tables #region Tables foreach (var mipOffset in MipOffsets) { cacheWriter.Write(mipOffset); } using (var ms = new MemoryStream()) using (var bw = new BinaryWriter(ms)) { // mipmaptable var startpos = ms.Position; var endpos = ms.Position; // string table //updates this.StringTableSize and all textureCacheItem.StringTableOffset //startpos = ms.Position; foreach (var textureCacheItem in Files) { var filename = textureCacheItem.Name; bw.WriteCR2WString(filename); textureCacheItem.StringTableOffset = (int)bw.BaseStream.Position; } endpos = ms.Position; StringTableSize = (uint)(endpos - startpos); // entry table startpos = ms.Position; foreach (var textureCacheItem in Files) { textureCacheItem.Write(bw); } endpos = ms.Position; Crc = CalculateChecksum(ms.ToArray()); ms.Seek(0, SeekOrigin.Begin); ms.CopyTo(cacheFileStream); } #endregion Tables // write footer WriteFooter(cacheWriter); logger?.LogString($"[TextureCache] Writing successful.", Logtype.Success); } }
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); }
public void Extract(Stream output) { using (var file = MemoryMappedFile.CreateFromFile(this.ParentFile, FileMode.Open)) { // generate header var metadata = new DDSMetadata( BaseWidth, BaseHeight, (uint)Mipcount, Format, BaseAlignment, IsCube == 1, SliceCount, false ); DDSUtils.GenerateAndWriteHeader(output, metadata); if (IsCube == 0) { using (var viewstream = file.CreateViewStream((PageOffset * 4096) + 9, ZSize, MemoryMappedFileAccess.Read)) { //if ( format != ETextureFormat.TEXFMT_R8G8B8A8) new ZlibStream(viewstream, CompressionMode.Decompress).CopyTo(output); } for (int i = 0; i < NumMipOffsets; i++) { var mippageoffset = MipMapInfo[i].Offset; var mipzsize = MipMapInfo[i].ZSize; using (var viewstream = file.CreateViewStream((mippageoffset), mipzsize, MemoryMappedFileAccess.Read)) { //if ( format != ETextureFormat.TEXFMT_R8G8B8A8) new ZlibStream(viewstream, CompressionMode.Decompress).CopyTo(output); } } } else { using (var imagestream = new MemoryStream()) using (var mipmapstream = new MemoryStream()) using (var imagereader = new BinaryReader(imagestream)) using (var mipmapreader = new BinaryReader(mipmapstream)) { // extract to memory // image using (var vs = file.CreateViewStream((PageOffset * 4096) + 9, ZSize, MemoryMappedFileAccess.Read)) { // extracts all 6 faces after each other new ZlibStream(vs, CompressionMode.Decompress).CopyTo(imagestream); } //mipmaps // mipmap data <offset, size> var mipmapoffsets = new List <Tuple <long, long> >(); foreach (var mipinfo in MipMapInfo) { var beginoffset = mipmapstream.Position; using (var tempvs = file.CreateViewStream(mipinfo.Offset, mipinfo.ZSize, MemoryMappedFileAccess.Read)) { new ZlibStream(tempvs, CompressionMode.Decompress).CopyTo(mipmapstream); } mipmapoffsets.Add(new Tuple <long, long>(beginoffset, mipmapstream.Position - beginoffset)); } //assemble faces for (int i = 0; i < 6; i++) { // get one face var offset = 0 + (i * imagestream.Length / 6); var facesize = (int)imagestream.Length / 6; imagereader.BaseStream.Seek(offset, SeekOrigin.Begin); var face = imagereader.ReadBytes(facesize); output.Write(face, 0, face.Length); // get mipmaps for face foreach (var o in mipmapoffsets) { var mipsize = (o.Item2 / 6); var moffset = o.Item1 + (i * mipsize); mipmapreader.BaseStream.Seek(moffset, SeekOrigin.Begin); var mipmap = mipmapreader.ReadBytes((int)(mipsize)); output.Write(mipmap, 0, mipmap.Length); } } } } } }
/// <summary> /// /// </summary> /// <param name="inputfolder"></param> public void LoadFiles(string inputfolder, ILoggerService logger = null) { var di = new DirectoryInfo(inputfolder); if (!di.Exists) { return; } var inputfiles = di.GetFiles("*.dds", SearchOption.AllDirectories) .Select(_ => _.FullName).ToList(); logger?.LogString($"[TextureCache] Begin caching.", Logtype.Important); logger?.LogString($"[TextureCache] Found {inputfiles.Count} files.", Logtype.Important); // clear data Files.Clear(); Names.Clear(); MipOffsets.Clear(); foreach (var filename in inputfiles) { var ext = Path.GetExtension(filename); switch (ext) { //case ".xbm": // { // // read cr2wfile // var cr2w = new CR2WFile() // { // FileName = filename, // }; // using (var cfs = new FileStream(filename, FileMode.Open, FileAccess.Read)) // using (var reader = new BinaryReader(cfs)) // { // var errorcode = cr2w.Read(reader); // if (errorcode != EFileReadErrorCodes.NoError) // continue; // } // // check if CBitmapTexture // if (!(cr2w.chunks.FirstOrDefault()?.data is CBitmapTexture xbm)) // { // continue; // } // break; // } case ".DDS": case ".dds": { var ddsheader = DDSUtils.ReadHeader(filename); var redpath = Path.ChangeExtension(filename, ddsheader.Iscubemap ? ".w2cube" : ".xbm"); var relativepath = redpath.Substring(di.FullName.Length + 1); #region Create Table Item var(type1, type2) = CommonImageTools.GetREDEngineByteFromEFormat(ddsheader.Format); if (ddsheader.Width % 2 != 0 || ddsheader.Height % 2 != 0) { continue; } var maxSide = Math.Max(ddsheader.Width, ddsheader.Height); //var minSide = Math.Min(ddsheader.Width, ddsheader.Height); var realmipscount = Math.Max(1, Math.Log10(maxSide / 32) / Math.Log10(2)); if (ddsheader.Mipscount == 1) //TODO: fix this { realmipscount = 0; } if (ddsheader.Mipscount == 0) //TODO: fix this { realmipscount = 0; } var ti = new TextureCacheItem(this) { Name = relativepath, FullName = filename, Hash = relativepath.HashStringKey(), /*------------- TextureCache Data ---------------*/ // NOTE: these need to be populated after writing the files ParentFile = "", //done StringTableOffset = -1, //done PageOffset = 0, //done CompressedSize = 0, //done UncompressedSize = 0, //done MipOffsetIndex = 0, //done /*------------- Image data ---------------*/ NumMipOffsets = (int)realmipscount, BaseAlignment = ddsheader.Bpp, BaseWidth = (ushort)ddsheader.Width, BaseHeight = (ushort)ddsheader.Height, Mipcount = (ushort)Math.Max(1, ddsheader.Mipscount), SliceCount = (ushort)ddsheader.Slicecount, //TODO TimeStamp = 0 /*(long)CDateTime.Now.ToUInt64()*/, //NOTE: Not even CDPR could be bothered to use their own Timestamps Type1 = type1, Type2 = type2, IsCube = ddsheader.Iscubemap ? (byte)1 : (byte)0, Unk1 = 0x00, //TODO: figure this out Format = CommonImageTools.GetEFormatFromREDEngineByte(type1) }; #endregion Create Table Item Files.Add(ti); Names.Add(ti.Name); logger?.LogString($"Cached {ti.Name}", Logtype.Normal); break; } } } logger?.LogString($"[TextureCache] Caching successful.", Logtype.Success); }