/// <summary> /// Creates a DDS file for the given Texture /// </summary> /// <param name="saveDirectory">The directory to save the dds file to</param> /// <param name="xivTex">The Texture information</param> public static void MakeDDS(XivTex xivTex, string savePath) { var DDS = new List <byte>(); switch (xivTex.TextureTypeAndPath.Type) { case XivTexType.ColorSet: DDS.AddRange(CreateColorDDSHeader()); DDS.AddRange(xivTex.TexData); break; case XivTexType.Vfx: case XivTexType.Diffuse: case XivTexType.Specular: case XivTexType.Normal: case XivTexType.Multi: case XivTexType.Mask: case XivTexType.Skin: case XivTexType.Map: case XivTexType.Icon: default: DDS.AddRange(CreateDDSHeader(xivTex)); var data = xivTex.TexData; if (xivTex.TextureFormat == XivTexFormat.A8R8G8B8 && xivTex.Layers > 1) { data = ShiftLayers(data); } DDS.AddRange(data); break; } File.WriteAllBytes(savePath, DDS.ToArray()); }
public async Task SetMaterial(XivMtrl mtrl, StainingTemplateFile dyeFile) { _mtrl = mtrl; DyeTemplateFile = dyeFile; _viewport = _view.ColorsetRowViewport; _viewport.BackgroundColor = System.Windows.Media.Colors.Gray; _viewport.Background = Brushes.Gray; if (_NeedLights) { _NeedLights = false; } _viewport.Camera.UpDirection = new System.Windows.Media.Media3D.Vector3D(0, 1, 0); _viewport.Camera.LookDirection = new System.Windows.Media.Media3D.Vector3D(0, 0, -1); _viewport.Camera.Position = new System.Windows.Media.Media3D.Point3D(0, 0, 3); if (TileTextureNormal == null) { var _tex = new Tex(XivCache.GameInfo.GameDirectory); TileTextureNormal = await _tex.GetTexData("chara/common/texture/-tile_n.tex"); TileTextureDiffuse = await _tex.GetTexData("chara/common/texture/-tile_d.tex"); } }
/// <summary> /// Gets the ATex data /// </summary> /// <param name="offset">The offset to the ATex file</param> /// <returns>An XivTex with all the texture data</returns> public XivTex GetATexData(int offset) { var dat = new Dat(_gameDirectory); var atexData = dat.GetType2Data(offset, _dataFile); var xivTex = new XivTex(); using (var br = new BinaryReader(new MemoryStream(atexData))) { var signature = br.ReadInt32(); xivTex.TextureFormat = Dat.TextureTypeDictionary[br.ReadInt32()]; xivTex.Width = br.ReadInt16(); xivTex.Height = br.ReadInt16(); br.ReadBytes(2); xivTex.MipMapCount = br.ReadInt16(); br.ReadBytes(64); xivTex.TexData = br.ReadBytes(atexData.Length - 80); } return(xivTex); }
public XivTex GetTexData(TexTypePath ttp) { var xivTex = new XivTex { TextureTypeAndPath = ttp }; var folder = Path.GetDirectoryName(ttp.Path); folder = folder.Replace("\\", "/"); var file = Path.GetFileName(ttp.Path); var index = new Index(_gameDirectory); var dat = new Dat(_gameDirectory); var offset = index.GetDataOffset(HashGenerator.GetHash(folder), HashGenerator.GetHash(file), ttp.DataFile); if (offset == 0) { throw new Exception($"Could not find offest for {ttp.Path}"); } dat.GetType4Data(offset, ttp.DataFile, xivTex); return(xivTex); }
public void SaveTexAsDDS(IItem item, XivTex xivTex, DirectoryInfo saveDirectory, XivRace race = XivRace.All_Races) { var path = IOUtil.MakeItemSavePath(item, saveDirectory, race); Directory.CreateDirectory(path); var savePath = Path.Combine(path, Path.GetFileNameWithoutExtension(xivTex.TextureTypeAndPath.Path) + ".dds"); DDS.MakeDDS(xivTex, savePath); }
/// <summary> /// Gets the original or modded data for type 4 files based on the path specified. /// </summary> /// <remarks> /// Type 4 files are used for Textures /// </remarks> /// <param name="internalPath">The internal file path of the item</param> /// <param name="forceOriginal">Flag used to get original game data</param> /// <param name="xivTex">The XivTex container to fill</param> public void GetType4Data(string internalPath, bool forceOriginal, XivTex xivTex) { var index = new Index(_gameDirectory); ModInfo modInfo = null; var inModList = false; var dataFile = GetDataFileFromPath(internalPath); if (forceOriginal) { // Checks if the item being imported already exists in the modlist using (var streamReader = new StreamReader(_modListDirectory.FullName)) { string line; while ((line = streamReader.ReadLine()) != null) { modInfo = JsonConvert.DeserializeObject <ModInfo>(line); if (modInfo.fullPath.Equals(internalPath)) { inModList = true; break; } } } // If the file exists in the modlist, get the data from the original data if (inModList) { GetType4Data(modInfo.originalOffset, dataFile, xivTex); return; } } // If it doesn't exist in the modlist(the item is not modded) or force original is false, // grab the data directly from them index file. var folder = Path.GetDirectoryName(internalPath); folder = folder.Replace("\\", "/"); var file = Path.GetFileName(internalPath); var offset = index.GetDataOffset(HashGenerator.GetHash(folder), HashGenerator.GetHash(file), dataFile); if (offset == 0) { throw new Exception($"Could not find offest for {internalPath}"); } GetType4Data(offset, dataFile, xivTex); }
/// <summary> /// Gets the raw pixel data for the texture /// </summary> /// <param name="xivTex">The texture data</param> /// <returns>A byte array with the image data</returns> public Task <byte[]> GetImageData(XivTex xivTex) { return(Task.Run(async() => { byte[] imageData = null; switch (xivTex.TextureFormat) { case XivTexFormat.DXT1: imageData = DxtUtil.DecompressDxt1(xivTex.TexData, xivTex.Width, xivTex.Height); break; case XivTexFormat.DXT3: imageData = DxtUtil.DecompressDxt3(xivTex.TexData, xivTex.Width, xivTex.Height); break; case XivTexFormat.DXT5: imageData = DxtUtil.DecompressDxt5(xivTex.TexData, xivTex.Width, xivTex.Height); break; case XivTexFormat.A4R4G4B4: imageData = await Read4444Image(xivTex.TexData, xivTex.Width, xivTex.Height); break; case XivTexFormat.A1R5G5B5: imageData = await Read5551Image(xivTex.TexData, xivTex.Width, xivTex.Height); break; case XivTexFormat.A8R8G8B8: imageData = await SwapRBColors(xivTex.TexData, xivTex.Width, xivTex.Height); break; case XivTexFormat.L8: case XivTexFormat.A8: imageData = await Read8bitImage(xivTex.TexData, xivTex.Width, xivTex.Height); break; case XivTexFormat.X8R8G8B8: case XivTexFormat.R32F: case XivTexFormat.G16R16F: case XivTexFormat.G32R32F: case XivTexFormat.A16B16G16R16F: case XivTexFormat.A32B32G32R32F: case XivTexFormat.D16: default: imageData = xivTex.TexData; break; } return imageData; })); }
/// <summary> /// Gets the raw pixel data for the texture /// </summary> /// <param name="xivTex">The texture data</param> /// <returns>A byte array with the image data</returns> public byte[] GetImageData(XivTex xivTex) { byte[] imageData = null; switch (xivTex.TextureFormat) { case XivTexFormat.DXT1: imageData = DxtUtil.DecompressDxt1(xivTex.TexData, xivTex.Width, xivTex.Heigth); break; case XivTexFormat.DXT3: imageData = DxtUtil.DecompressDxt3(xivTex.TexData, xivTex.Width, xivTex.Heigth); break; case XivTexFormat.DXT5: imageData = DxtUtil.DecompressDxt5(xivTex.TexData, xivTex.Width, xivTex.Heigth); break; case XivTexFormat.A4R4G4B4: imageData = Read4444Image(xivTex.TexData, xivTex.Width, xivTex.Heigth); break; case XivTexFormat.A1R5G5B5: imageData = Read5551Image(xivTex.TexData, xivTex.Width, xivTex.Heigth); break; case XivTexFormat.A8R8G8B8: imageData = SwapRBColors(xivTex.TexData, xivTex.Width, xivTex.Heigth); break; case XivTexFormat.L8: case XivTexFormat.A8: imageData = Read8bitImage(xivTex.TexData, xivTex.Width, xivTex.Heigth); break; case XivTexFormat.X8R8G8B8: case XivTexFormat.R32F: case XivTexFormat.G16R16F: case XivTexFormat.G32R32F: case XivTexFormat.A16B16G16R16F: case XivTexFormat.A32B32G32R32F: case XivTexFormat.D16: default: imageData = xivTex.TexData; break; } return(imageData); }
/// <summary> /// Gets the raw pixel data for the texture /// </summary> /// <param name="xivTex">The texture data</param> /// <returns>A byte array with the image data</returns> public Task <byte[]> GetImageData(XivTex xivTex, int layer = -1) { return(Task.Run(async() => { byte[] imageData = null; var layers = xivTex.Layers; if (layers == 0) { layers = 1; } switch (xivTex.TextureFormat) { case XivTexFormat.DXT1: imageData = DxtUtil.DecompressDxt1(xivTex.TexData, xivTex.Width, xivTex.Height * layers); break; case XivTexFormat.DXT3: imageData = DxtUtil.DecompressDxt3(xivTex.TexData, xivTex.Width, xivTex.Height * layers); break; case XivTexFormat.DXT5: imageData = DxtUtil.DecompressDxt5(xivTex.TexData, xivTex.Width, xivTex.Height * layers); break; case XivTexFormat.A4R4G4B4: imageData = await Read4444Image(xivTex.TexData, xivTex.Width, xivTex.Height * layers); break; case XivTexFormat.A1R5G5B5: imageData = await Read5551Image(xivTex.TexData, xivTex.Width, xivTex.Height * layers); break; case XivTexFormat.A8R8G8B8: imageData = await SwapRBColors(xivTex.TexData, xivTex.Width, xivTex.Height * layers); break; case XivTexFormat.L8: case XivTexFormat.A8: imageData = await Read8bitImage(xivTex.TexData, xivTex.Width, xivTex.Height * layers); break; case XivTexFormat.X8R8G8B8: case XivTexFormat.R32F: case XivTexFormat.G16R16F: case XivTexFormat.G32R32F: case XivTexFormat.A16B16G16R16F: case XivTexFormat.A32B32G32R32F: case XivTexFormat.D16: default: imageData = xivTex.TexData; break; } if (layer >= 0) { var bytesPerLayer = imageData.Length / xivTex.Layers; var offset = bytesPerLayer * layer; byte[] nData = new byte[bytesPerLayer]; Array.Copy(imageData, offset, nData, 0, bytesPerLayer); imageData = nData; } return imageData; })); }
/// <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 async Task <byte[]> DDStoTexData(XivTex xivTex, IItem item, DirectoryInfo ddsFileDirectory) { 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 = 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); 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); 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); }
/// <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); 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 the header for the texture info from the data to be imported. /// </summary> /// <param name="xivTex">Data for the currently displayed texture.</param> /// <param name="newWidth">The width of the DDS texture to be imported.</param> /// <param name="newHeight">The height of the DDS texture to be imported.</param> /// <param name="newMipCount">The number of mipmaps the DDS texture to be imported contains.</param> /// <returns>The created header data.</returns> private static List <byte> MakeTextureInfoHeader(XivTex xivTex, int newWidth, int newHeight, int newMipCount) { var headerData = new List <byte>(); headerData.AddRange(BitConverter.GetBytes((short)0)); headerData.AddRange(BitConverter.GetBytes((short)128)); headerData.AddRange(BitConverter.GetBytes(short.Parse(xivTex.TextureFormat.GetTexFormatCode()))); headerData.AddRange(BitConverter.GetBytes((short)0)); headerData.AddRange(BitConverter.GetBytes((short)newWidth)); headerData.AddRange(BitConverter.GetBytes((short)newHeight)); headerData.AddRange(BitConverter.GetBytes((short)1)); headerData.AddRange(BitConverter.GetBytes((short)newMipCount)); headerData.AddRange(BitConverter.GetBytes(0)); headerData.AddRange(BitConverter.GetBytes(1)); headerData.AddRange(BitConverter.GetBytes(2)); int mipLength; switch (xivTex.TextureFormat) { case XivTexFormat.DXT1: mipLength = (newWidth * newHeight) / 2; break; case XivTexFormat.DXT5: case XivTexFormat.A8: mipLength = newWidth * newHeight; break; case XivTexFormat.A1R5G5B5: case XivTexFormat.A4R4G4B4: mipLength = (newWidth * newHeight) * 2; break; case XivTexFormat.L8: case XivTexFormat.A8R8G8B8: case XivTexFormat.X8R8G8B8: case XivTexFormat.R32F: case XivTexFormat.G16R16F: case XivTexFormat.G32R32F: case XivTexFormat.A16B16G16R16F: case XivTexFormat.A32B32G32R32F: case XivTexFormat.DXT3: case XivTexFormat.D16: default: mipLength = (newWidth * newHeight) * 4; break; } var combinedLength = 80; for (var i = 0; i < newMipCount; i++) { headerData.AddRange(BitConverter.GetBytes(combinedLength)); combinedLength = combinedLength + mipLength; if (mipLength > 16) { mipLength = mipLength / 4; } else { mipLength = 16; } } var padding = 80 - headerData.Count; headerData.AddRange(new byte[padding]); return(headerData); }
/// <summary> /// Reads and parses data from the DDS file to be imported. /// </summary> /// <param name="br">The currently active BinaryReader.</param> /// <param name="xivTex">The Texture data.</param> /// <param name="newWidth">The width of the DDS texture to be imported.</param> /// <param name="newHeight">The height of the DDS texture to be imported.</param> /// <param name="newMipCount">The number of mipmaps the DDS texture to be imported contains.</param> /// <returns>A tuple containing the compressed DDS data, a list of offsets to the mipmap parts, a list with the number of parts per mipmap.</returns> public static (List <byte> compressedDDS, List <short> mipPartOffsets, List <short> mipPartCounts) ReadDDS(BinaryReader br, XivTex xivTex, int newWidth, int newHeight, int newMipCount) { var compressedDDS = new List <byte>(); var mipPartOffsets = new List <short>(); var mipPartCount = new List <short>(); int mipLength; switch (xivTex.TextureFormat) { case XivTexFormat.DXT1: mipLength = (newWidth * newHeight) / 2; break; case XivTexFormat.DXT5: case XivTexFormat.A8: mipLength = newWidth * newHeight; break; case XivTexFormat.A1R5G5B5: case XivTexFormat.A4R4G4B4: mipLength = (newWidth * newHeight) * 2; break; case XivTexFormat.L8: case XivTexFormat.A8R8G8B8: case XivTexFormat.X8R8G8B8: case XivTexFormat.R32F: case XivTexFormat.G16R16F: case XivTexFormat.G32R32F: case XivTexFormat.A16B16G16R16F: case XivTexFormat.A32B32G32R32F: case XivTexFormat.DXT3: case XivTexFormat.D16: default: mipLength = (newWidth * newHeight) * 4; break; } br.BaseStream.Seek(128, SeekOrigin.Begin); for (var i = 0; i < newMipCount; i++) { var mipParts = (int)Math.Ceiling(mipLength / 16000f); mipPartCount.Add((short)mipParts); if (mipParts > 1) { for (var j = 0; j < mipParts; j++) { int uncompLength; var comp = true; if (j == mipParts - 1) { uncompLength = mipLength % 16000; } else { uncompLength = 16000; } var uncompBytes = br.ReadBytes(uncompLength); var compressed = IOUtil.Compressor(uncompBytes); if (compressed.Length > uncompLength) { compressed = uncompBytes; comp = false; } compressedDDS.AddRange(BitConverter.GetBytes(16)); compressedDDS.AddRange(BitConverter.GetBytes(0)); compressedDDS.AddRange(!comp ? BitConverter.GetBytes(32000) : BitConverter.GetBytes(compressed.Length)); compressedDDS.AddRange(BitConverter.GetBytes(uncompLength)); compressedDDS.AddRange(compressed); var padding = 128 - (compressed.Length % 128); compressedDDS.AddRange(new byte[padding]); mipPartOffsets.Add((short)(compressed.Length + padding + 16)); } } else { int uncompLength; var comp = true; if (mipLength != 16000) { uncompLength = mipLength % 16000; } else { uncompLength = 16000; } var uncompBytes = br.ReadBytes(uncompLength); var compressed = IOUtil.Compressor(uncompBytes); if (compressed.Length > uncompLength) { compressed = uncompBytes; comp = false; } compressedDDS.AddRange(BitConverter.GetBytes(16)); compressedDDS.AddRange(BitConverter.GetBytes(0)); compressedDDS.AddRange(!comp ? BitConverter.GetBytes(32000) : BitConverter.GetBytes(compressed.Length)); compressedDDS.AddRange(BitConverter.GetBytes(uncompLength)); compressedDDS.AddRange(compressed); var padding = 128 - (compressed.Length % 128); compressedDDS.AddRange(new byte[padding]); mipPartOffsets.Add((short)(compressed.Length + padding + 16)); } if (mipLength > 32) { mipLength = mipLength / 4; } else { mipLength = 8; } } return(compressedDDS, mipPartOffsets, mipPartCount); }
/// <summary> /// Gets the data for Type 4 (Texture) files. /// </summary> /// <remarks> /// Type 4 files are used for Textures /// </remarks> /// <param name="offset">Offset to the texture data.</param> /// <param name="dataFile">The data file that contains the data.</param> /// <param name="xivTex">The XivTex container to fill</param> public void GetType4Data(int offset, XivDataFile dataFile, XivTex xivTex) { var decompressedData = new List <byte>(); // This formula is used to obtain the dat number in which the offset is located var datNum = ((offset / 8) & 0x0F) / 2; offset = OffsetCorrection(datNum, offset); var datPath = _gameDirectory + "\\" + dataFile.GetDataFileName() + DatExtension + datNum; using (var br = new BinaryReader(File.OpenRead(datPath))) { br.BaseStream.Seek(offset, SeekOrigin.Begin); var headerLength = br.ReadInt32(); var fileType = br.ReadInt32(); var uncompressedFileSize = br.ReadInt32(); br.ReadBytes(8); xivTex.MipMapCount = br.ReadInt32(); var endOfHeader = offset + headerLength; var mipMapInfoOffset = offset + 24; br.BaseStream.Seek(endOfHeader + 4, SeekOrigin.Begin); xivTex.TextureFormat = TextureTypeDictionary[br.ReadInt32()]; xivTex.Width = br.ReadInt16(); xivTex.Heigth = br.ReadInt16(); for (int i = 0, j = 0; i < xivTex.MipMapCount; i++) { br.BaseStream.Seek(mipMapInfoOffset + j, SeekOrigin.Begin); var offsetFromHeaderEnd = br.ReadInt32(); var mipMapLength = br.ReadInt32(); var mipMapSize = br.ReadInt32(); var mipMapStart = br.ReadInt32(); var mipMapParts = br.ReadInt32(); var mipMapPartOffset = endOfHeader + offsetFromHeaderEnd; br.BaseStream.Seek(mipMapPartOffset, SeekOrigin.Begin); br.ReadBytes(8); var compressedSize = br.ReadInt32(); var uncompressedSize = br.ReadInt32(); if (mipMapParts > 1) { var compressedData = br.ReadBytes(compressedSize); var decompressedPartData = IOUtil.Decompressor(compressedData, uncompressedSize); decompressedData.AddRange(decompressedPartData); for (var k = 1; k < mipMapParts; k++) { var check = br.ReadByte(); while (check != 0x10) { check = br.ReadByte(); } br.ReadBytes(7); compressedSize = br.ReadInt32(); uncompressedSize = br.ReadInt32(); // When the compressed size of a data block shows 32000, it is uncompressed. if (compressedSize != 32000) { compressedData = br.ReadBytes(compressedSize); decompressedPartData = IOUtil.Decompressor(compressedData, uncompressedSize); decompressedData.AddRange(decompressedPartData); } else { decompressedPartData = br.ReadBytes(uncompressedSize); decompressedData.AddRange(decompressedPartData); } } } else { // When the compressed size of a data block shows 32000, it is uncompressed. if (compressedSize != 32000) { var compressedData = br.ReadBytes(compressedSize); var uncompressedData = IOUtil.Decompressor(compressedData, uncompressedSize); decompressedData.AddRange(uncompressedData); } else { var decompressedPartData = br.ReadBytes(uncompressedSize); decompressedData.AddRange(decompressedPartData); } } j = j + 20; } if (decompressedData.Count >= uncompressedFileSize) { return; } var difference = uncompressedFileSize - decompressedData.Count; var padding = new byte[difference]; Array.Clear(padding, 0, difference); decompressedData.AddRange(padding); } xivTex.TexData = decompressedData.ToArray(); }
/// <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 long TexDDSImporter(XivTex xivTex, IItem item, DirectoryInfo ddsFileDirectory) { int lineNum = 0, offset = 0; var inModList = false; ModInfo modInfo = null; var dat = new Dat(_gameDirectory); if (File.Exists(ddsFileDirectory.FullName)) { // Check if the texture being imported has been imported before using (var sr = new StreamReader(_modListDirectory.FullName)) { string line; while ((line = sr.ReadLine()) != null) { modInfo = JsonConvert.DeserializeObject <ModInfo>(line); if (modInfo.fullPath.Equals(xivTex.TextureTypeAndPath.Path)) { inModList = true; break; } lineNum++; } } 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 newTex = new List <byte>(); var uncompressedLength = (int)new FileInfo(ddsFileDirectory.FullName).Length - 128; 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); offset = dat.WriteToDat(newTex, modInfo, inModList, xivTex.TextureTypeAndPath.Path, item.ItemCategory, item.Name, lineNum, xivTex.TextureTypeAndPath.DataFile); } else { throw new Exception($"Incorrect file type. Expected: {xivTex.TextureFormat} Given: {textureType}"); } } } else { throw new IOException($"Could not find file: {ddsFileDirectory.FullName}"); } return(offset); }
/// <summary> /// Creates the DDS header for given texture data. /// <see cref="https://msdn.microsoft.com/en-us/library/windows/desktop/bb943982(v=vs.85).aspx"/> /// </summary> /// <returns>Byte array containing DDS header</returns> private static byte[] CreateDDSHeader(XivTex xivTex) { uint dwPitchOrLinearSize, pfFlags, dwFourCC; var header = new List <byte>(); // DDS header magic number const uint dwMagic = 0x20534444; header.AddRange(BitConverter.GetBytes(dwMagic)); // Size of structure. This member must be set to 124. const uint dwSize = 124; header.AddRange(BitConverter.GetBytes(dwSize)); // Flags to indicate which members contain valid data. uint dwFlags = 528391; if (xivTex.Layers > 1) { dwFlags = 0x00000004; } header.AddRange(BitConverter.GetBytes(dwFlags)); // Surface height (in pixels). var dwHeight = (uint)xivTex.Height; header.AddRange(BitConverter.GetBytes(dwHeight)); // Surface width (in pixels). var dwWidth = (uint)xivTex.Width; header.AddRange(BitConverter.GetBytes(dwWidth)); // The pitch or number of bytes per scan line in an uncompressed texture; the total number of bytes in the top level texture for a compressed texture. if (xivTex.TextureFormat == XivTexFormat.A16B16G16R16F) { dwPitchOrLinearSize = 512; } else if (xivTex.TextureFormat == XivTexFormat.A8R8G8B8) { dwPitchOrLinearSize = (dwHeight * dwWidth) * 4; } else if (xivTex.TextureFormat == XivTexFormat.DXT1) { dwPitchOrLinearSize = (dwHeight * dwWidth) / 2; } else if (xivTex.TextureFormat == XivTexFormat.A4R4G4B4 || xivTex.TextureFormat == XivTexFormat.A1R5G5B5) { dwPitchOrLinearSize = (dwHeight * dwWidth) * 2; } else { dwPitchOrLinearSize = dwHeight * dwWidth; } header.AddRange(BitConverter.GetBytes(dwPitchOrLinearSize)); // Depth of a volume texture (in pixels), otherwise unused. const uint dwDepth = 0; header.AddRange(BitConverter.GetBytes(dwDepth)); // Number of mipmap levels, otherwise unused. var dwMipMapCount = (uint)xivTex.MipMapCount; header.AddRange(BitConverter.GetBytes(dwMipMapCount)); // Unused. var dwReserved1 = new byte[44]; Array.Clear(dwReserved1, 0, 44); header.AddRange(dwReserved1); // DDS_PIXELFORMAT start // Structure size; set to 32 (bytes). const uint pfSize = 32; header.AddRange(BitConverter.GetBytes(pfSize)); switch (xivTex.TextureFormat) { // Values which indicate what type of data is in the surface. case XivTexFormat.A8R8G8B8: case XivTexFormat.A4R4G4B4: case XivTexFormat.A1R5G5B5: pfFlags = 65; break; case XivTexFormat.A8: pfFlags = 2; break; default: pfFlags = 4; break; } header.AddRange(BitConverter.GetBytes(pfFlags)); switch (xivTex.TextureFormat) { // Four-character codes for specifying compressed or custom formats. case XivTexFormat.DXT1: dwFourCC = 0x31545844; break; case XivTexFormat.DXT5: dwFourCC = 0x35545844; break; case XivTexFormat.DXT3: dwFourCC = 0x33545844; break; case XivTexFormat.A16B16G16R16F: dwFourCC = 0x71; break; case XivTexFormat.A8R8G8B8: case XivTexFormat.A8: case XivTexFormat.A4R4G4B4: case XivTexFormat.A1R5G5B5: dwFourCC = 0; break; default: return(null); } if (xivTex.Layers > 1) { var bytes = System.Text.Encoding.UTF8.GetBytes("DX10"); dwFourCC = BitConverter.ToUInt32(bytes, 0); } header.AddRange(BitConverter.GetBytes(dwFourCC)); switch (xivTex.TextureFormat) { case XivTexFormat.A8R8G8B8: { // Number of bits in an RGB (possibly including alpha) format. const uint dwRGBBitCount = 32; header.AddRange(BitConverter.GetBytes(dwRGBBitCount)); // Red (or lumiannce or Y) mask for reading color data. const uint dwRBitMask = 16711680; header.AddRange(BitConverter.GetBytes(dwRBitMask)); // Green (or U) mask for reading color data. const uint dwGBitMask = 65280; header.AddRange(BitConverter.GetBytes(dwGBitMask)); // Blue (or V) mask for reading color data. const uint dwBBitMask = 255; header.AddRange(BitConverter.GetBytes(dwBBitMask)); // Alpha mask for reading alpha data. const uint dwABitMask = 4278190080; header.AddRange(BitConverter.GetBytes(dwABitMask)); // DDS_PIXELFORMAT End // Specifies the complexity of the surfaces stored. const uint dwCaps = 4096; header.AddRange(BitConverter.GetBytes(dwCaps)); // dwCaps2, dwCaps3, dwCaps4, dwReserved2. // Unused. var blank1 = new byte[16]; header.AddRange(blank1); break; } case XivTexFormat.A8: { // Number of bits in an RGB (possibly including alpha) format. const uint dwRGBBitCount = 8; header.AddRange(BitConverter.GetBytes(dwRGBBitCount)); // Red (or lumiannce or Y) mask for reading color data. const uint dwRBitMask = 0; header.AddRange(BitConverter.GetBytes(dwRBitMask)); // Green (or U) mask for reading color data. const uint dwGBitMask = 0; header.AddRange(BitConverter.GetBytes(dwGBitMask)); // Blue (or V) mask for reading color data. const uint dwBBitMask = 0; header.AddRange(BitConverter.GetBytes(dwBBitMask)); // Alpha mask for reading alpha data. const uint dwABitMask = 255; header.AddRange(BitConverter.GetBytes(dwABitMask)); // DDS_PIXELFORMAT End // Specifies the complexity of the surfaces stored. const uint dwCaps = 4096; header.AddRange(BitConverter.GetBytes(dwCaps)); // dwCaps2, dwCaps3, dwCaps4, dwReserved2. // Unused. var blank1 = new byte[16]; header.AddRange(blank1); break; } case XivTexFormat.A1R5G5B5: { // Number of bits in an RGB (possibly including alpha) format. const uint dwRGBBitCount = 16; header.AddRange(BitConverter.GetBytes(dwRGBBitCount)); // Red (or lumiannce or Y) mask for reading color data. const uint dwRBitMask = 31744; header.AddRange(BitConverter.GetBytes(dwRBitMask)); // Green (or U) mask for reading color data. const uint dwGBitMask = 992; header.AddRange(BitConverter.GetBytes(dwGBitMask)); // Blue (or V) mask for reading color data. const uint dwBBitMask = 31; header.AddRange(BitConverter.GetBytes(dwBBitMask)); // Alpha mask for reading alpha data. const uint dwABitMask = 32768; header.AddRange(BitConverter.GetBytes(dwABitMask)); // DDS_PIXELFORMAT End // Specifies the complexity of the surfaces stored. const uint dwCaps = 4096; header.AddRange(BitConverter.GetBytes(dwCaps)); // dwCaps2, dwCaps3, dwCaps4, dwReserved2. // Unused. var blank1 = new byte[16]; header.AddRange(blank1); break; } case XivTexFormat.A4R4G4B4: { // Number of bits in an RGB (possibly including alpha) format. const uint dwRGBBitCount = 16; header.AddRange(BitConverter.GetBytes(dwRGBBitCount)); // Red (or lumiannce or Y) mask for reading color data. const uint dwRBitMask = 3840; header.AddRange(BitConverter.GetBytes(dwRBitMask)); // Green (or U) mask for reading color data. const uint dwGBitMask = 240; header.AddRange(BitConverter.GetBytes(dwGBitMask)); // Blue (or V) mask for reading color data. const uint dwBBitMask = 15; header.AddRange(BitConverter.GetBytes(dwBBitMask)); // Alpha mask for reading alpha data. const uint dwABitMask = 61440; header.AddRange(BitConverter.GetBytes(dwABitMask)); // DDS_PIXELFORMAT End // Specifies the complexity of the surfaces stored. const uint dwCaps = 4096; header.AddRange(BitConverter.GetBytes(dwCaps)); // dwCaps2, dwCaps3, dwCaps4, dwReserved2. // Unused. var blank1 = new byte[16]; header.AddRange(blank1); break; } default: { // dwRGBBitCount, dwRBitMask, dwGBitMask, dwBBitMask, dwABitMask, dwCaps, dwCaps2, dwCaps3, dwCaps4, dwReserved2. // Unused. var blank1 = new byte[40]; header.AddRange(blank1); break; } } // Need to write DX10 header here. if (xivTex.Layers > 1) { // DXGI_FORMAT dxgiFormat uint dxgiFormat = 0; if (xivTex.TextureFormat == XivTexFormat.DXT1) { dxgiFormat = (uint)DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM; } else if (xivTex.TextureFormat == XivTexFormat.DXT5) { dxgiFormat = (uint)DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM; } else { dxgiFormat = (uint)DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM; } header.AddRange(BitConverter.GetBytes(dxgiFormat)); // D3D10_RESOURCE_DIMENSION resourceDimension header.AddRange(BitConverter.GetBytes((int)3)); // UINT miscFlag header.AddRange(BitConverter.GetBytes((int)0)); // UINT arraySize header.AddRange(BitConverter.GetBytes(xivTex.Layers)); // UINT miscFlags2 header.AddRange(BitConverter.GetBytes((int)0)); } return(header.ToArray()); }
/// <summary> /// Creates the header for the compressed texture data to be imported. /// </summary> /// <param name="xivTex">Data for the currently displayed texture.</param> /// <param name="mipPartOffsets">List of part offsets.</param> /// <param name="mipPartCount">List containing the amount of parts per mipmap.</param> /// <param name="uncompressedLength">Length of the uncompressed texture file.</param> /// <param name="newMipCount">The number of mipmaps the DDS texture to be imported contains.</param> /// <param name="newWidth">The width of the DDS texture to be imported.</param> /// <param name="newHeight">The height of the DDS texture to be imported.</param> /// <returns>The created header data.</returns> public byte[] MakeType4DatHeader(XivTex xivTex, List <short> mipPartOffsets, List <short> mipPartCount, int uncompressedLength, int newMipCount, int newWidth, int newHeight) { var headerData = new List <byte>(); var headerSize = 24 + (newMipCount * 20) + (mipPartOffsets.Count * 2); var headerPadding = 128 - (headerSize % 128); headerData.AddRange(BitConverter.GetBytes(headerSize + headerPadding)); headerData.AddRange(BitConverter.GetBytes(4)); headerData.AddRange(BitConverter.GetBytes(uncompressedLength)); headerData.AddRange(BitConverter.GetBytes(0)); headerData.AddRange(BitConverter.GetBytes(0)); headerData.AddRange(BitConverter.GetBytes(newMipCount)); var partIndex = 0; var mipOffsetIndex = 80; var uncompMipSize = newHeight * newWidth; switch (xivTex.TextureFormat) { case XivTexFormat.DXT1: uncompMipSize = (newWidth * newHeight) / 2; break; case XivTexFormat.DXT5: case XivTexFormat.A8: uncompMipSize = newWidth * newHeight; break; case XivTexFormat.A1R5G5B5: case XivTexFormat.A4R4G4B4: uncompMipSize = (newWidth * newHeight) * 2; break; case XivTexFormat.L8: case XivTexFormat.A8R8G8B8: case XivTexFormat.X8R8G8B8: case XivTexFormat.R32F: case XivTexFormat.G16R16F: case XivTexFormat.G32R32F: case XivTexFormat.A16B16G16R16F: case XivTexFormat.A32B32G32R32F: case XivTexFormat.DXT3: case XivTexFormat.D16: default: uncompMipSize = (newWidth * newHeight) * 4; break; } for (var i = 0; i < newMipCount; i++) { headerData.AddRange(BitConverter.GetBytes(mipOffsetIndex)); var paddedSize = 0; for (var j = 0; j < mipPartCount[i]; j++) { paddedSize = paddedSize + mipPartOffsets[j + partIndex]; } headerData.AddRange(BitConverter.GetBytes(paddedSize)); headerData.AddRange(uncompMipSize > 16 ? BitConverter.GetBytes(uncompMipSize) : BitConverter.GetBytes(16)); uncompMipSize = uncompMipSize / 4; headerData.AddRange(BitConverter.GetBytes(partIndex)); headerData.AddRange(BitConverter.GetBytes((int)mipPartCount[i])); partIndex = partIndex + mipPartCount[i]; mipOffsetIndex = mipOffsetIndex + paddedSize; } foreach (var part in mipPartOffsets) { headerData.AddRange(BitConverter.GetBytes(part)); } headerData.AddRange(new byte[headerPadding]); return(headerData.ToArray()); }
private async Task <TextureModel> MakeTextureModel(XivTex tex) { var layer = (int)Math.Floor(RowData[2][3] * 64); if (layer > 63 || layer < 0) { layer = 0; } var tileX = (float)RowData[3][0]; var tileY = (float)RowData[3][3]; var tileSkewX = (float)RowData[3][1]; var tileSkewY = (float)RowData[3][2]; var _tex = new Tex(XivCache.GameInfo.GameDirectory); var data = await _tex.GetImageData(tex, layer); var ogW = 32; var ogH = 32; var w = 256; var h = 256; Color4[] colors = new Color4[w * h]; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { var nPixel = (y * w) + x; //U = Dot Product ([u,v], [Red, Green]) float u = (float)x / (float)w; float v = ((float)y) / (float)h; // This is now the new U and V coordinates we want to map to. var newU = SharpDX.Vector2.Dot(new SharpDX.Vector2(u, v), new SharpDX.Vector2(tileX, tileSkewX)) % 1.0f; var newV = SharpDX.Vector2.Dot(new SharpDX.Vector2(u, v), new SharpDX.Vector2(tileSkewY, tileY)) % 1.0f; var xPx = (int)(newU * ogW); var yPx = (int)(newV * ogH); xPx = xPx < 0 ? ogW - xPx : xPx; yPx = yPx < 0 ? ogH - yPx : yPx; xPx = xPx >= ogW ? 0 : xPx; yPx = yPx >= ogH ? 0 : yPx; var ogPixel = (yPx * ogW) + xPx; var ogDataOffset = ogPixel * 4; colors[nPixel] = new Color4() { Red = data[ogDataOffset] / 255.0f, Green = data[ogDataOffset + 1] / 255.0f, Blue = data[ogDataOffset + 2] / 255.0f, Alpha = 1.0f, }; } } var tm = new TextureModel(colors, w, h); return(tm); }