/// <summary> /// Converts a DDS file into a TEX file then returns the raw data /// </summary> /// <param name="xivTex">The texture data</param> /// <param name="item">The item who's texture we are importing</param> /// <param name="ddsFileDirectory">The directory of the dds file being imported</param> /// <returns>The offset to the new imported data</returns> public byte[] DDStoTexData(XivTex xivTex, IItem item, DirectoryInfo ddsFileDirectory) { var offset = 0; var dat = new Dat(_gameDirectory); if (File.Exists(ddsFileDirectory.FullName)) { using (var br = new BinaryReader(File.OpenRead(ddsFileDirectory.FullName))) { br.BaseStream.Seek(12, SeekOrigin.Begin); var newHeight = br.ReadInt32(); var newWidth = br.ReadInt32(); br.ReadBytes(8); var newMipCount = br.ReadInt32(); if (newHeight % 2 != 0 || newWidth % 2 != 0) { throw new Exception("Resolution must be a multiple of 2"); } br.BaseStream.Seek(80, SeekOrigin.Begin); var textureFlags = br.ReadInt32(); var texType = br.ReadInt32(); XivTexFormat textureType; if (DDSType.ContainsKey(texType)) { textureType = DDSType[texType]; } else { throw new Exception($"DDS Type ({texType}) not recognized."); } switch (textureFlags) { case 2 when textureType == XivTexFormat.A8R8G8B8: textureType = XivTexFormat.A8; break; case 65 when textureType == XivTexFormat.A8R8G8B8: var bpp = br.ReadInt32(); if (bpp == 32) { textureType = XivTexFormat.A8R8G8B8; } else { var red = br.ReadInt32(); switch (red) { case 31744: textureType = XivTexFormat.A1R5G5B5; break; case 3840: textureType = XivTexFormat.A4R4G4B4; break; } } break; } if (textureType == xivTex.TextureFormat) { var uncompressedLength = (int)new FileInfo(ddsFileDirectory.FullName).Length - 128; var newTex = new List <byte>(); if (!xivTex.TextureTypeAndPath.Path.Contains(".atex")) { var DDSInfo = DDS.ReadDDS(br, xivTex, newWidth, newHeight, newMipCount); newTex.AddRange(dat.MakeType4DatHeader(xivTex, DDSInfo.mipPartOffsets, DDSInfo.mipPartCounts, uncompressedLength, newMipCount, newWidth, newHeight)); newTex.AddRange(MakeTextureInfoHeader(xivTex, newWidth, newHeight, newMipCount)); newTex.AddRange(DDSInfo.compressedDDS); return(newTex.ToArray()); } else { br.BaseStream.Seek(128, SeekOrigin.Begin); newTex.AddRange(MakeTextureInfoHeader(xivTex, newWidth, newHeight, newMipCount)); newTex.AddRange(br.ReadBytes(uncompressedLength)); return(newTex.ToArray()); } } else { throw new Exception($"Incorrect file type. Expected: {xivTex.TextureFormat} Given: {textureType}"); } } } else { throw new IOException($"Could not find file: {ddsFileDirectory.FullName}"); } }
public async Task <int> TexBMPImporter(XivTex xivTex, IItem item, DirectoryInfo bmpFileDirectory, string source) { var offset = 0; var modding = new Modding(_gameDirectory); if (File.Exists(bmpFileDirectory.FullName)) { // Check if the texture being imported has been imported before var modEntry = await modding.TryGetModEntry(xivTex.TextureTypeAndPath.Path); using (var magickImage = new MagickImage(bmpFileDirectory.FullName)) { switch (xivTex.TextureFormat) { case XivTexFormat.DXT1: magickImage.Format = MagickFormat.Dxt1; break; case XivTexFormat.DXT5: magickImage.Format = MagickFormat.Dxt5; break; case XivTexFormat.A8R8G8B8: magickImage.Format = MagickFormat.Dds; magickImage.Settings.SetDefines(new DdsWriteDefines { Compression = DdsCompression.None }); break; default: throw new Exception($"Format {xivTex.TextureFormat} is not currently supported for BMP import\n\nPlease use the DDS import option instead."); } var data = magickImage.ToByteArray(); using (var br = new BinaryReader(new MemoryStream(data))) { br.BaseStream.Seek(12, SeekOrigin.Begin); var newHeight = br.ReadInt32(); var newWidth = br.ReadInt32(); br.ReadBytes(8); var newMipCount = br.ReadInt32(); if (newHeight % 2 != 0 || newWidth % 2 != 0) { throw new Exception("Resolution must be a multiple of 2"); } br.BaseStream.Seek(80, SeekOrigin.Begin); var textureFlags = br.ReadInt32(); var texType = br.ReadInt32(); XivTexFormat textureType; if (DDSType.ContainsKey(texType)) { textureType = DDSType[texType]; } else { throw new Exception($"DDS Type ({texType}) not recognized."); } switch (textureFlags) { case 2 when textureType == XivTexFormat.A8R8G8B8: textureType = XivTexFormat.A8; break; case 65 when textureType == XivTexFormat.A8R8G8B8: var bpp = br.ReadInt32(); if (bpp == 32) { textureType = XivTexFormat.A8R8G8B8; } else { var red = br.ReadInt32(); switch (red) { case 31744: textureType = XivTexFormat.A1R5G5B5; break; case 3840: textureType = XivTexFormat.A4R4G4B4; break; } } break; } if (textureType == xivTex.TextureFormat) { var uncompressedLength = data.Length; var newTex = new List <byte>(); if (!xivTex.TextureTypeAndPath.Path.Contains(".atex")) { var DDSInfo = await DDS.ReadDDS(br, xivTex, newWidth, newHeight, newMipCount); newTex.AddRange(_dat.MakeType4DatHeader(xivTex, DDSInfo.mipPartOffsets, DDSInfo.mipPartCounts, uncompressedLength, newMipCount, newWidth, newHeight)); newTex.AddRange(MakeTextureInfoHeader(xivTex, newWidth, newHeight, newMipCount)); newTex.AddRange(DDSInfo.compressedDDS); offset = await _dat.WriteToDat(newTex, modEntry, xivTex.TextureTypeAndPath.Path, item.ItemCategory, item.Name, xivTex.TextureTypeAndPath.DataFile, source, 4); } else { br.BaseStream.Seek(128, SeekOrigin.Begin); newTex.AddRange(MakeTextureInfoHeader(xivTex, newWidth, newHeight, newMipCount)); newTex.AddRange(br.ReadBytes(uncompressedLength)); offset = await _dat.ImportType2Data(newTex.ToArray(), item.Name, xivTex.TextureTypeAndPath.Path, item.ItemCategory, source); } } else { throw new Exception($"Incorrect file type. Expected: {xivTex.TextureFormat} Given: {textureType}"); } } } } else { throw new IOException($"Could not find file: {bmpFileDirectory.FullName}"); } return(offset); }
/// <summary> /// Creates texture data ready to be imported into the DATs from an external file. /// If format is not specified, either the incoming file's DDS format is used (DDS files), /// or the existing internal file's DDS format is used. /// </summary> /// <param name="internalPath"></param> /// <param name="externalPath"></param> /// <param name="texFormat"></param> /// <returns></returns> public async Task <byte[]> MakeTexData(string internalPath, string externalPath, XivTexFormat texFormat = XivTexFormat.INVALID) { // Ensure file exists. if (!File.Exists(externalPath)) { throw new IOException($"Could not find file: {externalPath}"); } var root = await XivCache.GetFirstRoot(internalPath); bool isDds = Path.GetExtension(externalPath).ToLower() == ".dds"; var ddsContainer = new DDSContainer(); try { // If no format was specified... if (texFormat == XivTexFormat.INVALID) { if (isDds) { // If we're importing a DDS file, get the format from the incoming DDS file using (var fs = new FileStream(externalPath, FileMode.Open)) { using (var sr = new BinaryReader(fs)) { texFormat = GetDDSTexFormat(sr); } } } else { // Otherwise use the current internal format. var xivt = await _dat.GetType4Data(internalPath, false); texFormat = xivt.TextureFormat; } } // Check if the texture being imported has been imported before CompressionFormat compressionFormat = CompressionFormat.BGRA; switch (texFormat) { case XivTexFormat.DXT1: compressionFormat = CompressionFormat.BC1a; break; case XivTexFormat.DXT5: compressionFormat = CompressionFormat.BC3; break; case XivTexFormat.A8R8G8B8: compressionFormat = CompressionFormat.BGRA; break; default: if (!isDds) { throw new Exception($"Format {texFormat} is not currently supported for BMP import\n\nPlease use the DDS import option instead."); } break; } if (!isDds) { using (var surface = Surface.LoadFromFile(externalPath)) { if (surface == null) { throw new FormatException($"Unsupported texture format"); } surface.FlipVertically(); var maxMipCount = 1; if (root != null) { // For things that have real roots (things that have actual models/aren't UI textures), we always want mipMaps, even if the existing texture only has one. // (Ex. The Default Mat-Add textures) maxMipCount = -1; } using (var compressor = new Compressor()) { // UI/Paintings only have a single mipmap and will crash if more are generated, for everything else generate max levels compressor.Input.SetMipmapGeneration(true, maxMipCount); compressor.Input.SetData(surface); compressor.Compression.Format = compressionFormat; compressor.Compression.SetBGRAPixelFormat(); compressor.Process(out ddsContainer); } } } // If we're not a DDS, write the DDS to file temporarily. var ddsFilePath = externalPath; if (!isDds) { var tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".dds"); ddsContainer.Write(tempFile, DDSFlags.None); ddsFilePath = tempFile; } using (var br = new BinaryReader(File.OpenRead(ddsFilePath))) { br.BaseStream.Seek(12, SeekOrigin.Begin); var newHeight = br.ReadInt32(); var newWidth = br.ReadInt32(); br.ReadBytes(8); var newMipCount = br.ReadInt32(); if (newHeight % 2 != 0 || newWidth % 2 != 0) { throw new Exception("Resolution must be a multiple of 2"); } br.BaseStream.Seek(80, SeekOrigin.Begin); var textureFlags = br.ReadInt32(); var texType = br.ReadInt32(); var uncompressedLength = (int)new FileInfo(ddsFilePath).Length - 128; var newTex = new List <byte>(); if (!internalPath.Contains(".atex")) { var DDSInfo = await DDS.ReadDDS(br, texFormat, newWidth, newHeight, newMipCount); newTex.AddRange(_dat.MakeType4DatHeader(texFormat, DDSInfo.mipPartOffsets, DDSInfo.mipPartCounts, (int)uncompressedLength, newMipCount, newWidth, newHeight)); newTex.AddRange(MakeTextureInfoHeader(texFormat, newWidth, newHeight, newMipCount)); newTex.AddRange(DDSInfo.compressedDDS); return(newTex.ToArray()); } else { br.BaseStream.Seek(128, SeekOrigin.Begin); newTex.AddRange(MakeTextureInfoHeader(texFormat, newWidth, newHeight, newMipCount)); newTex.AddRange(br.ReadBytes((int)uncompressedLength)); var data = await _dat.CreateType2Data(newTex.ToArray()); return(data); } } } finally { ddsContainer.Dispose(); } }
/// <summary> /// Converts a DDS file into a TEX file then imports it /// </summary> /// <param name="xivTex">The texture data</param> /// <param name="item">The item who's texture we are importing</param> /// <param name="ddsFileDirectory">The directory of the dds file being imported</param> /// <returns>The offset to the new imported data</returns> public async Task <int> TexDDSImporter(XivTex xivTex, IItem item, DirectoryInfo ddsFileDirectory, string source) { var offset = 0; var modding = new Modding(_gameDirectory); if (File.Exists(ddsFileDirectory.FullName)) { // Check if the texture being imported has been imported before var modEntry = await modding.TryGetModEntry(xivTex.TextureTypeAndPath.Path); using (var br = new BinaryReader(File.OpenRead(ddsFileDirectory.FullName))) { br.BaseStream.Seek(12, SeekOrigin.Begin); var newHeight = br.ReadInt32(); var newWidth = br.ReadInt32(); br.ReadBytes(8); var newMipCount = br.ReadInt32(); if (newHeight % 2 != 0 || newWidth % 2 != 0) { throw new Exception("Resolution must be a multiple of 2"); } br.BaseStream.Seek(80, SeekOrigin.Begin); var textureFlags = br.ReadInt32(); var texType = br.ReadInt32(); XivTexFormat textureType; if (DDSType.ContainsKey(texType)) { textureType = DDSType[texType]; } else { throw new Exception($"DDS Type ({texType}) not recognized."); } switch (textureFlags) { case 2 when textureType == XivTexFormat.A8R8G8B8: textureType = XivTexFormat.A8; break; case 65 when textureType == XivTexFormat.A8R8G8B8: var bpp = br.ReadInt32(); if (bpp == 32) { textureType = XivTexFormat.A8R8G8B8; } else { var red = br.ReadInt32(); switch (red) { case 31744: textureType = XivTexFormat.A1R5G5B5; break; case 3840: textureType = XivTexFormat.A4R4G4B4; break; } } break; } if (textureType == xivTex.TextureFormat) { var uncompressedLength = (int)new FileInfo(ddsFileDirectory.FullName).Length - 128; var newTex = new List <byte>(); if (!xivTex.TextureTypeAndPath.Path.Contains(".atex")) { var DDSInfo = await DDS.ReadDDS(br, xivTex, newWidth, newHeight, newMipCount); newTex.AddRange(_dat.MakeType4DatHeader(xivTex, DDSInfo.mipPartOffsets, DDSInfo.mipPartCounts, uncompressedLength, newMipCount, newWidth, newHeight)); newTex.AddRange(MakeTextureInfoHeader(xivTex, newWidth, newHeight, newMipCount)); newTex.AddRange(DDSInfo.compressedDDS); offset = await _dat.WriteToDat(newTex, modEntry, xivTex.TextureTypeAndPath.Path, item.ItemCategory, item.Name, xivTex.TextureTypeAndPath.DataFile, source, 4); } else { br.BaseStream.Seek(128, SeekOrigin.Begin); newTex.AddRange(MakeTextureInfoHeader(xivTex, newWidth, newHeight, newMipCount)); newTex.AddRange(br.ReadBytes(uncompressedLength)); offset = await _dat.ImportType2Data(newTex.ToArray(), item.Name, xivTex.TextureTypeAndPath.Path, item.ItemCategory, source); } } else { throw new Exception($"Incorrect file type. Expected: {xivTex.TextureFormat} Given: {textureType}"); } } } else { throw new IOException($"Could not find file: {ddsFileDirectory.FullName}"); } return(offset); }
public async Task <int> TexBMPImporter(XivTex xivTex, IItem item, DirectoryInfo bmpFileDirectory, string source) { var offset = 0; var modding = new Modding(_gameDirectory); if (File.Exists(bmpFileDirectory.FullName)) { // Check if the texture being imported has been imported before var modEntry = await modding.TryGetModEntry(xivTex.TextureTypeAndPath.Path); var ddsContainer = new DDSContainer(); CompressionFormat compressionFormat; switch (xivTex.TextureFormat) { case XivTexFormat.DXT1: compressionFormat = CompressionFormat.BC1; break; case XivTexFormat.DXT5: compressionFormat = CompressionFormat.BC3; break; case XivTexFormat.A8R8G8B8: compressionFormat = CompressionFormat.BGRA; break; default: throw new Exception($"Format {xivTex.TextureFormat} is not currently supported for BMP import\n\nPlease use the DDS import option instead."); } using (var surface = Surface.LoadFromFile(bmpFileDirectory.FullName)) { surface.FlipVertically(); using (var compressor = new Compressor()) { compressor.Input.GenerateMipmaps = true; compressor.Input.SetData(surface); compressor.Compression.Format = compressionFormat; compressor.Compression.SetBGRAPixelFormat(); compressor.Process(out ddsContainer); } } using (var ddsMemoryStream = new MemoryStream()) { ddsContainer.Write(ddsMemoryStream, DDSFlags.None); using (var br = new BinaryReader(ddsMemoryStream)) { br.BaseStream.Seek(12, SeekOrigin.Begin); var newHeight = br.ReadInt32(); var newWidth = br.ReadInt32(); br.ReadBytes(8); var newMipCount = br.ReadInt32(); if (newHeight % 2 != 0 || newWidth % 2 != 0) { throw new Exception("Resolution must be a multiple of 2"); } br.BaseStream.Seek(80, SeekOrigin.Begin); var textureFlags = br.ReadInt32(); var texType = br.ReadInt32(); XivTexFormat textureType; if (DDSType.ContainsKey(texType)) { textureType = DDSType[texType]; } else { throw new Exception($"DDS Type ({texType}) not recognized."); } switch (textureFlags) { case 2 when textureType == XivTexFormat.A8R8G8B8: textureType = XivTexFormat.A8; break; case 65 when textureType == XivTexFormat.A8R8G8B8: var bpp = br.ReadInt32(); if (bpp == 32) { textureType = XivTexFormat.A8R8G8B8; } else { var red = br.ReadInt32(); switch (red) { case 31744: textureType = XivTexFormat.A1R5G5B5; break; case 3840: textureType = XivTexFormat.A4R4G4B4; break; } } break; } if (textureType == xivTex.TextureFormat) { var uncompressedLength = ddsMemoryStream.Length; var newTex = new List <byte>(); if (!xivTex.TextureTypeAndPath.Path.Contains(".atex")) { var DDSInfo = await DDS.ReadDDS(br, xivTex, newWidth, newHeight, newMipCount); newTex.AddRange(_dat.MakeType4DatHeader(xivTex, DDSInfo.mipPartOffsets, DDSInfo.mipPartCounts, (int)uncompressedLength, newMipCount, newWidth, newHeight)); newTex.AddRange(MakeTextureInfoHeader(xivTex, newWidth, newHeight, newMipCount)); newTex.AddRange(DDSInfo.compressedDDS); offset = await _dat.WriteToDat(newTex, modEntry, xivTex.TextureTypeAndPath.Path, item.ItemCategory, item.Name, xivTex.TextureTypeAndPath.DataFile, source, 4); } else { br.BaseStream.Seek(128, SeekOrigin.Begin); newTex.AddRange(MakeTextureInfoHeader(xivTex, newWidth, newHeight, newMipCount)); newTex.AddRange(br.ReadBytes((int)uncompressedLength)); offset = await _dat.ImportType2Data(newTex.ToArray(), item.Name, xivTex.TextureTypeAndPath.Path, item.ItemCategory, source); } } else { throw new Exception($"Incorrect file type. Expected: {xivTex.TextureFormat} Given: {textureType}"); } } } ddsContainer.Dispose(); } else { throw new IOException($"Could not find file: {bmpFileDirectory.FullName}"); } return(offset); }