public async Task <long> ImportTex(string internalPath, string externalPath, IItem item, string source, IndexFile cachedIndexFile = null, ModList cachedModList = null) { long offset = 0; var path = internalPath; var df = IOUtil.GetDataFileFromPath(path); var data = await MakeTexData(path, externalPath); var modding = new Modding(_gameDirectory); Mod entry = null; if (cachedModList != null) { entry = cachedModList.Mods.FirstOrDefault(x => x.fullPath == path); } else { entry = await modding.TryGetModEntry(path); } var type = Path.GetExtension(path) == ".atex" ? 2 : 4; offset = await _dat.WriteModFile(data, path, source, item, cachedIndexFile, cachedModList); return(offset); }
/// <summary> /// Gets the metadata file for a given root. /// </summary> /// <param name="root"></param> /// <returns></returns> public static async Task <ItemMetadata> GetMetadata(XivDependencyRoot root, bool forceDefault = false) { if (root == null) { return(null); } Mod mod = null; var filePath = root.Info.GetRootFile(); if (!forceDefault) { var _modding = new Modding(XivCache.GameInfo.GameDirectory); mod = await _modding.TryGetModEntry(filePath); } if (mod != null && mod.enabled) { var _dat = new Dat(XivCache.GameInfo.GameDirectory); // We have modded metadata stored in the .meta file in the DAT we can use. var data = await _dat.GetType2Data(filePath, false); // Run it through the binary deserializer and we're good. //return await Deserialize(data); return(await CreateFromRaw(root, forceDefault)); } else { // This is the fun part where we get to pull the Metadata from all the disparate files around the FFXIV File System. return(await CreateFromRaw(root, forceDefault)); } }
public async Task DisableMod() { _view.SaveButton.IsEnabled = false; _view.CancelButton.IsEnabled = false; _view.DisableButton.IsEnabled = false; _view.DisableButton.Content = UIStrings.Working_Ellipsis; var files = new List <string>(); // If we're disabling from the Edit Multi menu, diable all variant versions as well. if (_mode == MaterialEditorMode.EditMulti) { var sameModelItems = await _item.GetSharedModelItems(); var itemType = ItemType.GetPrimaryItemType(_item); // Find all the variant materials foreach (var item in sameModelItems) { var variantPath = await _mtrl.GetMtrlPath(item, _material.GetRace(), _material.GetMaterialIdentifier(), itemType); files.Add(variantPath.Folder + "/" + variantPath.File); } } else { // Just disabling this one. files.Add(_material.MTRLPath); } files = files.Distinct().ToList(); foreach (var file in files) { var modEntry = await _modding.TryGetModEntry(file); if (!modEntry.enabled) { continue; } // If the file is a custom addition, and not a modification. if (modEntry.source != XivStrings.TexTools) { await _modding.DeleteMod(file); } else { await _modding.ToggleModStatus(file, false); } } _view.Close(false); }
private async void ToggleButton_Click(object sender, RoutedEventArgs e) { var _modding = new Modding(XivCache.GameInfo.GameDirectory); var path = _root.Info.GetRootFile(); var mod = await _modding.TryGetModEntry(path); if (mod == null) { return; } var enabled = mod.enabled; await MainWindow.GetMainWindow().LockUi("Updating Metadata"); try { if (enabled) { await _modding.ToggleModStatus(path, false); ToggleButton.Content = "Enable"; } else { await _modding.ToggleModStatus(path, true); ToggleButton.Content = "Disable"; } } catch (Exception ex) { FlexibleMessageBox.Show("Unable to toggle mod status.\n\nError: " + ex.Message, "Mod Toggle Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); } finally { await MainWindow.GetMainWindow().UnlockUi(); var mw = MainWindow.GetMainWindow(); mw.ReloadItem(); } }
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 <bool> SetMaterial(XivMtrl material, IItemModel item, MaterialEditorMode mode) { if (material == null) { return(false); } _mode = mode; _material = material; _item = item; var gameDirectory = new DirectoryInfo(Properties.Settings.Default.FFXIV_Directory); _mtrl = new Mtrl(gameDirectory, item.DataFile, GetLanguage()); _index = new Index(gameDirectory); _modding = new Modding(gameDirectory); _gear = new Gear(gameDirectory, GetLanguage()); // Drop the multi functions down to singles if they only have one Material to edit anyways. if (_mode == MaterialEditorMode.EditMulti || _mode == MaterialEditorMode.NewMulti) { // This isn't an actual perfect check for if there's only one Variant, but doing so // would be a bit expensive here, and passing it through EditMulti isn't harmful anyways. var sameModelItems = await _item.GetSharedModelItems(); if (sameModelItems.Count == 1) { if (_mode == MaterialEditorMode.EditMulti) { _mode = MaterialEditorMode.EditSingle; } else { _mode = MaterialEditorMode.NewSingle; } } } /* * // Debug code for finding unknown Shader Parameters. * var unknowns = new List<ShaderParameterStruct>(); * foreach(var sp in material.ShaderParameterList) * { * if (!Enum.IsDefined(typeof(MtrlShaderParameterId), sp.ParameterID)) * { * unknowns.Add(sp); * } * } * if(unknowns.Count > 0) * { * // Debug line * var json = JsonConvert.SerializeObject(unknowns.ToArray()); * } */ // Update to new material name switch (_mode) { case MaterialEditorMode.EditSingle: _view.MaterialPathLabel.Text = _material.MTRLPath; break; case MaterialEditorMode.EditMulti: _view.MaterialPathLabel.Text = "Editing Multiple Materials: Material " + _material.GetMaterialIdentifier(); break; case MaterialEditorMode.NewSingle: _view.MaterialPathLabel.Text = "New Material"; break; case MaterialEditorMode.NewMulti: _view.MaterialPathLabel.Text = "New Materials"; break; } var shader = _material.GetShaderInfo(); var normal = _material.GetMapInfo(XivTexType.Normal); var diffuse = _material.GetMapInfo(XivTexType.Diffuse); var specular = _material.GetMapInfo(XivTexType.Specular); var multi = _material.GetMapInfo(XivTexType.Multi); var reflection = _material.GetMapInfo(XivTexType.Reflection); // Show Paths _view.NormalTextBox.Text = normal == null ? "" : normal.path; _view.SpecularTextBox.Text = specular == null ? "" : specular.path; _view.SpecularTextBox.Text = multi == null ? _view.SpecularTextBox.Text : multi.path; _view.DiffuseTextBox.Text = diffuse == null ? "" : diffuse.path; _view.DiffuseTextBox.Text = reflection == null ? _view.DiffuseTextBox.Text : reflection.path; // Add Other option if needed. if (shader.Shader == MtrlShader.Other) { _view.ShaderSource.Add(new KeyValuePair <MtrlShader, string>(MtrlShader.Other, "Other")); } // Show Settings _view.TransparencyComboBox.SelectedValue = shader.TransparencyEnabled; _view.BackfacesComboBox.SelectedValue = shader.RenderBackfaces; _view.ColorsetComboBox.SelectedValue = shader.HasColorset; _view.ShaderComboBox.SelectedValue = shader.Shader; _view.PresetComboBox.SelectedValue = shader.Preset; if (_mode == MaterialEditorMode.NewMulti) { // Bump up the material identifier letter. _newMaterialIdentifier = await GetNewMaterialIdentifier(); _view.MaterialPathLabel.Text = "New Materials: Material " + _newMaterialIdentifier; } else if (_mode == MaterialEditorMode.NewSingle) { _newMaterialIdentifier = await GetNewMaterialIdentifier(); _view.MaterialPathLabel.Text = "New Material: Material " + _newMaterialIdentifier; } // Get the mod entry. if (_mode == MaterialEditorMode.EditSingle || _mode == MaterialEditorMode.EditMulti) { var mod = await _modding.TryGetModEntry(_material.MTRLPath); if (mod != null && mod.enabled) { _view.DisableButton.IsEnabled = true; _view.DisableButton.Visibility = System.Windows.Visibility.Visible; } } return(true); }
public async Task DisableMod() { _view.SaveButton.IsEnabled = false; _view.CancelButton.IsEnabled = false; _view.DisableButton.IsEnabled = false; _view.DisableButton.Content = UIStrings.Working_Ellipsis; var files = new HashSet <string>(); var root = _item.GetRoot(); if (root == null || !Imc.UsesImc(root)) { // This is the only copy of the material we know how to find. files.Add(_material.MTRLPath); } else { var imc = new Imc(XivCache.GameInfo.GameDirectory); var info = await imc.GetFullImcInfo(_item); var entries = info.GetAllEntries(root.Info.Slot); var materialSets = entries.Select(x => x.MaterialSet).ToHashSet(); var extract = new Regex("(v[0-9]{4})"); var rep = extract.Match(_material.MTRLPath).Groups[1].Value; // Remove the material in all of the referenced material sets. foreach (var setId in materialSets) { var newPath = _material.MTRLPath.Replace(rep, "v" + setId.ToString().PadLeft(4, '0')); files.Add(newPath); } } try { foreach (var file in files) { var modEntry = await _modding.TryGetModEntry(file); if (modEntry == null) { continue; } // If the file is a custom addition, and not a modification. if (modEntry.IsCustomFile()) { await _modding.DeleteMod(file); } else { await _modding.ToggleModStatus(file, false); } } } catch (Exception ex) { FlexibleMessageBox.Show("Unable to delete Mod.\n\nError: " + ex.Message, "Mod Delete Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } _view.Close(false); }
public async Task <bool> SetRoot(XivDependencyRoot root, int defaultVariant = 0) { _root = root; if (_root == null) { return(false); } if (root.Info.PrimaryType == XivItemType.human) { if (_lastNumber > 0) { if (_lastNumber != (int)root.Info.SecondaryId) { // Change root to the new "number" root. var nRoot = new XivDependencyRootInfo() { PrimaryId = _root.Info.PrimaryId, PrimaryType = _root.Info.PrimaryType, SecondaryId = _lastNumber, SecondaryType = _root.Info.SecondaryType, Slot = _root.Info.Slot }; _root = nRoot.ToFullRoot(); } } if (_root.Info.SecondaryId > 0) { _lastNumber = (int)_root.Info.SecondaryId; } } SetLabel.Content = XivItemTypes.GetSystemPrefix(root.Info.PrimaryType) + root.Info.PrimaryId.ToString().PadLeft(4, '0'); SlotLabel.Content = Mdl.SlotAbbreviationDictionary.FirstOrDefault(x => x.Value == _root.Info.Slot).Key + "(" + _root.Info.Slot + ")"; var items = await _root.GetAllItems(); ItemNameBox.Text = "[" + items.Count + "] " + items[0].Name; var _modding = new Modding(XivCache.GameInfo.GameDirectory); var path = _root.Info.GetRootFile(); var mod = await _modding.TryGetModEntry(path); if (mod == null) { ToggleButton.IsEnabled = false; ToggleButton.Content = "Enable"; } else { ToggleButton.IsEnabled = true; if (mod.enabled) { ToggleButton.Content = "Disable"; } else { ToggleButton.Content = "Enable"; } } return(await _vm.SetRoot(_root, defaultVariant)); }
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); }