public static bool ExtractTextureMap(string fileName, Forge forge) { using (Stream stream = new FileStream($"{fileName}-combined", FileMode.Open, FileAccess.Read, FileShare.Read)) { using (BinaryReader reader = new BinaryReader(stream)) { // this is omnipresent DatafileHeader header = new DatafileHeader { ResourceType = reader.ReadInt32(), FileSize = reader.ReadInt32(), FileNameSize = reader.ReadInt32() }; header.FileName = reader.ReadChars(header.FileNameSize); // ignore the 2 bytes, file ID, and resource type identifier reader.BaseStream.Seek(14, SeekOrigin.Current); // toppmip 0 TopMip mip0 = new TopMip { Width = reader.ReadInt32(), Height = reader.ReadInt32() }; reader.BaseStream.Seek(8, SeekOrigin.Current); mip0.DXTType = DXTExtensions.GetDXT(reader.ReadInt32()); reader.BaseStream.Seek(4, SeekOrigin.Current); mip0.Mipmaps = reader.ReadInt32(); reader.BaseStream.Seek(81, SeekOrigin.Current); // topmip 1 TopMip mip1 = new TopMip { Width = reader.ReadInt32(), Height = reader.ReadInt32() }; reader.BaseStream.Seek(8, SeekOrigin.Current); mip1.DXTType = DXTExtensions.GetDXT(reader.ReadInt32()); reader.BaseStream.Seek(4, SeekOrigin.Current); mip1.Mipmaps = reader.ReadInt32(); // locate the two topmips, if they exist if (forge.FileEntries.Where(x => x.NameTable.Name.Contains(Path.GetFileName(fileName) + "_TopMip")).Count() == 2) { Forge.FileEntry topMipEntry = forge.FileEntries.Where(x => x.NameTable.Name == Path.GetFileName(fileName) + "_TopMip_0").First(); // extract, read, and create DDS images with the first topmips byte[] rawData = forge.GetRawData(topMipEntry); Helpers.WriteToTempFile(topMipEntry.NameTable.Name, rawData); ReadFile(Helpers.GetTempPath(topMipEntry.NameTable.Name)); ExtractTopMip(Helpers.GetTempPath(topMipEntry.NameTable.Name), mip0); } else // topmips do not exist. fear not! there is still image data found here. let us use that. { reader.BaseStream.Seek(25, SeekOrigin.Current); TextureMap map = new TextureMap { DataSize = reader.ReadInt32() }; byte[] mipmapData = reader.ReadBytes(map.DataSize); Helpers.WriteTempDDS(fileName, mipmapData, mip0.Width, mip0.Height, mip0.Mipmaps, mip0.DXTType); } } } return(true); }
public static void ExtractTextureMap(string fileName, EntryTreeNode node, Action <string> completionAction) { string name = Path.GetFileNameWithoutExtension(fileName); using (Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { if (stream.Length == 0) { return; } using (BinaryReader reader = new BinaryReader(stream)) { DatafileHeader header = new DatafileHeader { ResourceType = reader.ReadInt32(), FileSize = reader.ReadInt32(), FileNameSize = reader.ReadInt32() }; header.FileName = reader.ReadChars(header.FileNameSize); // ignore the 1 byte, file ID, resource type, and 1 extra byte reader.BaseStream.Seek(14, SeekOrigin.Current); // mip 0 Mip mip0 = new Mip { Width = reader.ReadInt32(), Height = reader.ReadInt32() }; reader.BaseStream.Seek(8, SeekOrigin.Current); mip0.DXTType = DXTExtensions.GetDXT(reader.ReadInt32()); reader.BaseStream.Seek(4, SeekOrigin.Current); mip0.Mipmaps = reader.ReadInt32(); reader.BaseStream.Seek(39, SeekOrigin.Current); // go to next mip // mip 1 Mip mip1 = new Mip { Width = reader.ReadInt32(), Height = reader.ReadInt32() }; reader.BaseStream.Seek(8, SeekOrigin.Current); mip1.DXTType = DXTExtensions.GetDXT(reader.ReadInt32()); reader.BaseStream.Seek(4, SeekOrigin.Current); mip1.Mipmaps = reader.ReadInt32(); // locate the two mips, if they exist if (node.GetForge().FileEntries.Where(x => x.NameTable.Name.Contains(Path.GetFileNameWithoutExtension(fileName) + "_Mip")).Count() == 2) { Forge.FileEntry[] mipEntries = node.GetForge().FileEntries.Where(x => x.NameTable.Name == Path.GetFileNameWithoutExtension(fileName) + "_Mip0").ToArray(); if (mipEntries.Length > 0) { Forge.FileEntry mipEntry = mipEntries[0]; // extract, read, and create a DDS image with the first mip byte[] rawTopMipData = node.GetForge().GetRawData(mipEntry); //Helpers.WriteToFile(mipEntry.NameTable.Name, rawData, true); // read //ReadFile(Helpers.GetTempPath(mipEntry.NameTable.Name)); byte[] topMipData = ReadFile(rawTopMipData); // extract //ExtractTopMip(Helpers.GetTempPath(mipEntry.NameTable.Name), mip0, completionAction); ExtractTopMip(topMipData, mipEntry.NameTable.Name, mip0, completionAction); } } else // mips do not exist. fear not! there is still image data found here. let us use that. { reader.BaseStream.Seek(12, SeekOrigin.Current); TextureMap map = new TextureMap { DataSize = reader.ReadInt32() }; byte[] mipmapData = reader.ReadBytes(map.DataSize); // write DDS file Helpers.WriteTempDDS(name, mipmapData, mip1.Width, mip1.Height, mip1.Mipmaps, mip1.DXTType, () => { Helpers.ConvertDDS($"{Helpers.GetTempPath(name)}.dds", (bool error) => { if (error) { completionAction("FAILED"); } else { completionAction($"{Helpers.GetTempPath(name)}.png"); } }); }); } } } }
/// <summary> /// Writes an entire .dds file to the temporary path using the fileName /// </summary> /// <param name="fileName"></param> /// <param name="imageData"></param> /// <param name="width"></param> /// <param name="height"></param> /// <param name="mipmapCount"></param> /// <param name="dxtType"></param> /// <param name="completedAction"></param> public static void WriteTempDDS(string fileName, byte[] imageData, int width, int height, int mipmapCount, DXT dxtType, Action completedAction) { Console.WriteLine("DXT: " + dxtType.ToString()); char[] dxtArr = { 'D', 'X', '\x0', '\x0' }; int pls = imageData.Length; //Math.Max(1, (width + 3) / 4) * 8 try { using (FileStream stream = new FileStream($"{GetTempPath(fileName)}.dds", FileMode.Create, FileAccess.Write, FileShare.None)) { using (BinaryWriter writer = new BinaryWriter(stream)) { foreach (char c in new char[] { 'D', 'D', 'S', ' ' }) // DDS magic { writer.Write(c); } writer.Write(124); // size of DDS header writer.Write(659463); // flags writer.Write(height); // height writer.Write(width); // width writer.Write(pls); // pitch or linear size writer.Write(0); // depth writer.Write(1); // mipmap count, "1" for now for (int i = 0; i < 11; i++) // reserved { writer.Write(0); } writer.Write(32); // size of PIXELFORMAT chunk if (dxtType == 0 ? false : (int)dxtType != 7) // flags { writer.Write(4); } else { writer.Write(64); } foreach (char c in DXTExtensions.GetDXTAsChars((int)dxtType)) // DXT type/four CC { writer.Write(c); } for (int n = 0; n < 5; n++) // RGBBitCount, RBitMask, GBitMask, BBitMask, ABitMask { writer.Write(0); } writer.Write(4198408); // caps for (int i = 0; i < 4; i++) // caps2, caps3, caps4, reserved2 { writer.Write(0); } if (dxtType.ToString().Contains("DX10")) // add the DX10 header, if necessary { string fileNameNoExt = Path.GetFileNameWithoutExtension(fileName); if (fileNameNoExt.Contains("NormalMap") || fileNameNoExt.EndsWith("_Map")) // this stays until I devise a better tactic { writer.Write(98); // normal maps } else { writer.Write(72); // all others use BC1_UNORM_SRGB } writer.Write(3); // resourceDimension writer.Write(0); // miscFlags writer.Write(1); // array size writer.Write(0); // miscFlags2 } writer.Write(imageData); // image data } } } catch (IOException e) { MessageBox.Show($"Could not create a dds file due an error:\n{e.Message}", "Failure"); } completedAction(); }
/// <summary> /// Writes an entire .dds file to the temporary path using the fileName /// </summary> /// <param name="fileName"></param> /// <param name="imageData"></param> /// <param name="width"></param> /// <param name="height"></param> /// <param name="mipmapCount"></param> /// <param name="dxtType"></param> /// <param name="completedAction"></param> public static void WriteTempDDS(string fileName, byte[] imageData, int width, int height, int mipmapCount, DXT dxtType, Action completedAction) { Console.WriteLine("DXT: " + dxtType.ToString()); char[] dxtArr = { 'D', 'X', '\x0', '\x0' }; try { using (FileStream stream = new FileStream($"{GetTempPath(fileName)}.dds", FileMode.Create, FileAccess.Write, FileShare.None)) { using (BinaryWriter writer = new BinaryWriter(stream)) { foreach (char c in new char[] { 'D', 'D', 'S', ' ' }) // DDS magic { writer.Write(c); } writer.Write(BitConverter.GetBytes(124)); // size of DDS header writer.Write(new byte[] { 0x7, 0x10, 0x8, 0x0 }); // flags writer.Write(height); // height writer.Write(width); // width writer.Write(BitConverter.GetBytes(2048)); // pitch or linear size writer.Write(1); // depth writer.Write(1); // mipmap count, "1" for now for (int i = 0; i < 11; i++) // reserved { writer.Write(0); } // DDS_PIXELFORMAT writer.Write(32); // size of PIXELFORMAT if (dxtType == 0 ? false : (int)dxtType != 7) // flags { writer.Write(BitConverter.GetBytes(4)); } else { writer.Write(BitConverter.GetBytes(64)); } foreach (char c in DXTExtensions.GetDXTAsChars((int)dxtType)) // DXT type/four CC { writer.Write(c); } for (int n = 0; n < 5; n++) // RGBBitCount, RBitMask, GBitMask, BBitMask, ABitMask { writer.Write(0); } writer.Write(4198408); // caps for (int i = 0; i < 4; i++) // caps2, caps3, caps4, reserved2 { writer.Write(0); } // DDS_HEADER_DX10 if (dxtType.ToString().Contains("DX10")) // add the DX10 header, if necessary { string fileNameNoExt = Path.GetFileNameWithoutExtension(fileName); if (fileNameNoExt.Contains("NormalMap") || fileNameNoExt.EndsWith("_Map")) // this stays until I devise a better tactic { writer.Write(BitConverter.GetBytes(98)); // BC7_UNORM (normal maps) } else if (false) { writer.Write(BitConverter.GetBytes(71)); // BC1_UNORM (mask maps) } else { writer.Write(BitConverter.GetBytes(72)); // BC1_UNORM_SRGB } writer.Write(BitConverter.GetBytes(3)); // resource dimension writer.Write(0); // misc flags writer.Write(BitConverter.GetBytes(1)); // array size writer.Write(0); // misc flags 2 } // image data writer.Write(imageData); } } } catch (IOException e) { Message.Fail("Could not create a DDS file. " + e.Message + e.StackTrace); } completedAction(); }