public async Task ShowConversionStats() { if (!DestinationOk(Destination)) { return; } var imc = new Imc(XivCache.GameInfo.GameDirectory); SourceBox.Text = Source.Info.GetBaseFileName() + " (" + SourceItem.Name + ")"; DestinationBox.Text = Destination.Info.GetBaseFileName() + " (" + DestinationItem.Name + ")"; if (Imc.UsesImc(Source)) { try { var sourceInfo = await imc.GetFullImcInfo(Source.GetRawImcFilePath()); var destInfo = await imc.GetFullImcInfo(Destination.GetRawImcFilePath()); SourceVariantsBox.Text = (sourceInfo.SubsetCount + 1).ToString(); DestinationVariantsBox.Text = (destInfo.SubsetCount + 1).ToString(); SameVariantBox.IsEnabled = true; SameVariantBox.IsChecked = true; } catch { SameVariantBox.IsEnabled = true; SameVariantBox.IsChecked = true; } } else { SameVariantBox.IsEnabled = false; SameVariantBox.IsChecked = false; SourceVariantsBox.Text = "1"; DestinationVariantsBox.Text = "1"; } var items = await Destination.GetAllItems(); AffectedItemsBox.Items.Clear(); foreach (var item in items) { AffectedItemsBox.Items.Add(item.Name); } }
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 AsyncInit() { var root = _item.GetRoot(); if (root == null) { return; } var gd = XivCache.GameInfo.GameDirectory; var lang = XivCache.GameInfo.GameLanguage; var df = IOUtil.GetDataFileFromPath(root.Info.GetRootFile()); var _index = new Index(gd); var _mtrl = new Mtrl(XivCache.GameInfo.GameDirectory); var _mdl = new Mdl(gd, df); var _imc = new Imc(gd); var raceRegex = new Regex("c([0-9]{4})[^b]"); ItemNameBox.Text = _item.Name; var setName = root.Info.GetBaseFileName(false); SetLabel.Text = "Set: " + setName; if (!String.IsNullOrWhiteSpace(root.Info.Slot)) { var niceSlot = Mdl.SlotAbbreviationDictionary.FirstOrDefault(x => x.Value == root.Info.Slot); if (niceSlot.Key != null) { SlotLabel.Text = "Slot: " + niceSlot.Key + " (" + root.Info.Slot + ")"; } else { SlotLabel.Text = "Slot: Unknown (" + root.Info.Slot + ")"; } } else { SlotLabel.Text = "Slot: --"; } var usesImc = Imc.UsesImc(_item); if (usesImc) { VariantLabel.Text = "Variant: " + _item.ModelInfo.ImcSubsetID; } else { VariantLabel.Text = "Variant: --"; } var mSet = await _imc.GetMaterialSetId(_item); if (mSet > 0) { MaterialSetLabel.Text = "Material Set: " + mSet; } else { MaterialSetLabel.Text = "Material Set: --"; } var races = XivRaces.PlayableRaces; var models = await root.GetModelFiles(); var materials = await root.GetMaterialFiles(mSet); #region Race Chart var rowIdx = 1; foreach (var race in races) { var rCode = race.GetRaceCode(); var row = new RowDefinition(); row.Height = new GridLength(30); RacialGrid.RowDefinitions.Add(row); var lBase = new Label(); lBase.Content = race.GetDisplayName(); lBase.SetValue(Grid.RowProperty, rowIdx); RacialGrid.Children.Add(lBase); XivRace?usedMdlRace = race; string usedMdl = null;; if (race != XivRace.All_Races) { // Check if the race has a model. var mdl = models.FirstOrDefault(x => x.Contains("c" + rCode)); if (mdl == null) { // Gotta see which race they're shared from. var node = XivRaceTree.GetNode(race); var parent = node.Parent; while (parent != null) { var code = parent.Race.GetRaceCode(); mdl = models.FirstOrDefault(x => x.Contains("c" + code)); if (mdl != null) { usedMdlRace = parent.Race; usedMdl = mdl; break; } parent = parent.Parent; } if (mdl == null) { // No model exists for this item. usedMdlRace = null; } } else { usedMdl = mdl; } } var mdlRaceString = "None"; if (usedMdlRace == race) { mdlRaceString = "Own"; } else { if (usedMdlRace != null) { mdlRaceString = ((XivRace)usedMdlRace).GetDisplayName(); } } XivRace?usedMtrlRace = usedMdlRace; if (race != XivRace.All_Races) { if (usedMdlRace == null) { usedMtrlRace = null; } else { // Get the materials used by this racial's model. var mdl = usedMdl; var mdlMaterials = await XivCache.GetChildFiles(mdl); var mtrl = mdlMaterials.FirstOrDefault(x => raceRegex.IsMatch(x)); if (mtrl == null) { usedMtrlRace = null; } else { var code = raceRegex.Match(mtrl).Groups[1].Value; usedMtrlRace = XivRaces.GetXivRace(code); if (usedMtrlRace == XivRace.All_Races) { usedMtrlRace = null; } } } } var mtrlRaceString = "None"; if (usedMtrlRace == race) { mtrlRaceString = "Own"; } else { if (usedMtrlRace != null) { mtrlRaceString = ((XivRace)usedMtrlRace).GetDisplayName(); } } var lMdl = new Label(); lMdl.Content = mdlRaceString; lMdl.SetValue(Grid.RowProperty, rowIdx); lMdl.SetValue(Grid.ColumnProperty, 1); RacialGrid.Children.Add(lMdl); var lMtrl = new Label(); lMtrl.Content = mtrlRaceString; lMtrl.SetValue(Grid.RowProperty, rowIdx); lMtrl.SetValue(Grid.ColumnProperty, 2); RacialGrid.Children.Add(lMtrl); rowIdx++; } #endregion if (Imc.UsesImc(_item) && _item.ModelInfo != null) { var myImcSubsetId = _item.ModelInfo.ImcSubsetID; var allItems = await root.GetAllItems(); var fInfo = await _imc.GetFullImcInfo(_item); var entries = fInfo.GetAllEntries(_item.GetItemSlotAbbreviation(), true); foreach (var item in allItems) { SameModelItems.Add(new KeyValuePair <string, IItem>(item.Name, item)); if (entries.Count > item.ModelInfo.ImcSubsetID) { var imSet = entries[item.ModelInfo.ImcSubsetID].MaterialSet; if (mSet == imSet) { SameMSetItems.Add(new KeyValuePair <string, IItem>(item.Name, item)); } } if (item.ModelInfo.ImcSubsetID == myImcSubsetId) { SameVariantItems.Add(new KeyValuePair <string, IItem>(item.Name, item)); } } } }
/// <summary> /// Copies the entirety of a given root to a new root. /// </summary> /// <param name="Source">Original Root to copy from.</param> /// <param name="Destination">Destination root to copy to.</param> /// <param name="ApplicationSource">Application to list as the source for the resulting mod entries.</param> /// <returns>Returns a Dictionary of all the file conversion</returns> public static async Task <Dictionary <string, string> > CloneRoot(XivDependencyRoot Source, XivDependencyRoot Destination, string ApplicationSource, int singleVariant = -1, string saveDirectory = null, IProgress <string> ProgressReporter = null, IndexFile index = null, ModList modlist = null, ModPack modPack = null) { if (!IsSupported(Source) || !IsSupported(Destination)) { throw new InvalidDataException("Cannot clone unsupported root."); } if (ProgressReporter != null) { ProgressReporter.Report("Stopping Cache Worker..."); } var workerStatus = XivCache.CacheWorkerEnabled; XivCache.CacheWorkerEnabled = false; try { var df = IOUtil.GetDataFileFromPath(Source.ToString()); var _imc = new Imc(XivCache.GameInfo.GameDirectory); var _mdl = new Mdl(XivCache.GameInfo.GameDirectory, df); var _dat = new Dat(XivCache.GameInfo.GameDirectory); var _index = new Index(XivCache.GameInfo.GameDirectory); var _mtrl = new Mtrl(XivCache.GameInfo.GameDirectory); var _modding = new Modding(XivCache.GameInfo.GameDirectory); var doSave = false; if (index == null) { doSave = true; index = await _index.GetIndexFile(df); modlist = await _modding.GetModListAsync(); } bool locked = _index.IsIndexLocked(df); if (locked) { throw new Exception("Game files currently in use."); } if (ProgressReporter != null) { ProgressReporter.Report("Analyzing items and variants..."); } // First, try to get everything, to ensure it's all valid. ItemMetadata originalMetadata = await GetCachedMetadata(index, modlist, Source, df, _dat); var originalModelPaths = await Source.GetModelFiles(index, modlist); var originalMaterialPaths = await Source.GetMaterialFiles(-1, index, modlist); var originalTexturePaths = await Source.GetTextureFiles(-1, index, modlist); var originalVfxPaths = new HashSet <string>(); if (Imc.UsesImc(Source)) { var avfxSets = originalMetadata.ImcEntries.Select(x => x.Vfx).Distinct(); foreach (var avfx in avfxSets) { var avfxStuff = await ATex.GetVfxPath(Source.Info, avfx); if (String.IsNullOrEmpty(avfxStuff.Folder) || String.IsNullOrEmpty(avfxStuff.File)) { continue; } var path = avfxStuff.Folder + "/" + avfxStuff.File; if (index.FileExists(path)) { originalVfxPaths.Add(path); } } } // Time to start editing things. // First, get a new, clean copy of the metadata, pointed at the new root. var newMetadata = await GetCachedMetadata(index, modlist, Source, df, _dat); newMetadata.Root = Destination.Info.ToFullRoot(); ItemMetadata originalDestinationMetadata = null; try { originalDestinationMetadata = await GetCachedMetadata(index, modlist, Destination, df, _dat); } catch { originalDestinationMetadata = new ItemMetadata(Destination); } // Set 0 needs special handling. if (Source.Info.PrimaryType == XivItemType.equipment && Source.Info.PrimaryId == 0) { var set1Root = new XivDependencyRoot(Source.Info.PrimaryType, 1, null, null, Source.Info.Slot); var set1Metadata = await GetCachedMetadata(index, modlist, set1Root, df, _dat); newMetadata.EqpEntry = set1Metadata.EqpEntry; if (Source.Info.Slot == "met") { newMetadata.GmpEntry = set1Metadata.GmpEntry; } } else if (Destination.Info.PrimaryType == XivItemType.equipment && Destination.Info.PrimaryId == 0) { newMetadata.EqpEntry = null; newMetadata.GmpEntry = null; } // Now figure out the path names for all of our new paths. // These dictionarys map Old Path => New Path Dictionary <string, string> newModelPaths = new Dictionary <string, string>(); Dictionary <string, string> newMaterialPaths = new Dictionary <string, string>(); Dictionary <string, string> newMaterialFileNames = new Dictionary <string, string>(); Dictionary <string, string> newTexturePaths = new Dictionary <string, string>(); Dictionary <string, string> newAvfxPaths = new Dictionary <string, string>(); if (ProgressReporter != null) { ProgressReporter.Report("Calculating files to copy..."); } // For each path, replace any instances of our primary and secondary types. foreach (var path in originalModelPaths) { newModelPaths.Add(path, UpdatePath(Source, Destination, path)); } foreach (var path in originalMaterialPaths) { var nPath = UpdatePath(Source, Destination, path); newMaterialPaths.Add(path, nPath); var fName = Path.GetFileName(path); if (!newMaterialFileNames.ContainsKey(fName)) { newMaterialFileNames.Add(fName, Path.GetFileName(nPath)); } } foreach (var path in originalTexturePaths) { newTexturePaths.Add(path, UpdatePath(Source, Destination, path)); } foreach (var path in originalVfxPaths) { newAvfxPaths.Add(path, UpdatePath(Source, Destination, path)); } var destItem = Destination.GetFirstItem(); var srcItem = (await Source.GetAllItems(singleVariant))[0]; var iCat = destItem.SecondaryCategory; var iName = destItem.Name; var files = newModelPaths.Select(x => x.Value).Union( newMaterialPaths.Select(x => x.Value)).Union( newAvfxPaths.Select(x => x.Value)).Union( newTexturePaths.Select(x => x.Value)); var allFiles = new HashSet <string>(); foreach (var f in files) { allFiles.Add(f); } allFiles.Add(Destination.Info.GetRootFile()); if (ProgressReporter != null) { ProgressReporter.Report("Getting modlist..."); } if (ProgressReporter != null) { ProgressReporter.Report("Removing existing modifications to destination root..."); } if (Destination != Source) { var dPath = Destination.Info.GetRootFolder(); var allMods = modlist.Mods.ToList(); foreach (var mod in allMods) { if (mod.fullPath.StartsWith(dPath) && !mod.IsInternal()) { if (Destination.Info.SecondaryType != null || Destination.Info.Slot == null) { // If this is a slotless root, purge everything. await _modding.DeleteMod(mod.fullPath, false, index, modlist); } else if (allFiles.Contains(mod.fullPath) || mod.fullPath.Contains(Destination.Info.GetBaseFileName(true))) { // Otherwise, only purge the files we're replacing, and anything else that // contains our slot name. await _modding.DeleteMod(mod.fullPath, false, index, modlist); } } } } if (ProgressReporter != null) { ProgressReporter.Report("Copying models..."); } // Load, Edit, and resave the model files. foreach (var kv in newModelPaths) { var src = kv.Key; var dst = kv.Value; var offset = index.Get8xDataOffset(src); var xmdl = await _mdl.GetRawMdlData(src, false, offset); var tmdl = TTModel.FromRaw(xmdl); if (xmdl == null || tmdl == null) { continue; } tmdl.Source = dst; xmdl.MdlPath = dst; // Replace any material references as needed. foreach (var m in tmdl.MeshGroups) { foreach (var matKv in newMaterialFileNames) { m.Material = m.Material.Replace(matKv.Key, matKv.Value); } } // Save new Model. var bytes = await _mdl.MakeNewMdlFile(tmdl, xmdl, null); await _dat.WriteModFile(bytes, dst, ApplicationSource, destItem, index, modlist); } if (ProgressReporter != null) { ProgressReporter.Report("Copying textures..."); } // Raw Copy all Texture files to the new destinations to avoid having the MTRL save functions auto-generate blank textures. foreach (var kv in newTexturePaths) { var src = kv.Key; var dst = kv.Value; await _dat.CopyFile(src, dst, ApplicationSource, true, destItem, index, modlist); } if (ProgressReporter != null) { ProgressReporter.Report("Copying materials..."); } HashSet <string> CopiedMaterials = new HashSet <string>(); // Load every Material file and edit the texture references to the new texture paths. foreach (var kv in newMaterialPaths) { var src = kv.Key; var dst = kv.Value; try { var offset = index.Get8xDataOffset(src); if (offset == 0) { continue; } var xivMtrl = await _mtrl.GetMtrlData(offset, src, 11); xivMtrl.MTRLPath = dst; for (int i = 0; i < xivMtrl.TexturePathList.Count; i++) { foreach (var tkv in newTexturePaths) { xivMtrl.TexturePathList[i] = xivMtrl.TexturePathList[i].Replace(tkv.Key, tkv.Value); } } await _mtrl.ImportMtrl(xivMtrl, destItem, ApplicationSource, index, modlist); CopiedMaterials.Add(dst); } catch (Exception ex) { // Let functions later handle this mtrl then. } } if (ProgressReporter != null) { ProgressReporter.Report("Copying VFX..."); } // Copy VFX files. foreach (var kv in newAvfxPaths) { var src = kv.Key; var dst = kv.Value; await _dat.CopyFile(src, dst, ApplicationSource, true, destItem, index, modlist); } if (ProgressReporter != null) { ProgressReporter.Report("Creating missing variants..."); } // Check to see if we need to add any variants var cloneNum = newMetadata.ImcEntries.Count >= 2 ? 1 : 0; while (originalDestinationMetadata.ImcEntries.Count > newMetadata.ImcEntries.Count) { // Clone Variant 1 into the variants we are missing. newMetadata.ImcEntries.Add((XivImc)newMetadata.ImcEntries[cloneNum].Clone()); } if (singleVariant >= 0) { if (ProgressReporter != null) { ProgressReporter.Report("Setting single-variant data..."); } if (singleVariant < newMetadata.ImcEntries.Count) { var v = newMetadata.ImcEntries[singleVariant]; for (int i = 0; i < newMetadata.ImcEntries.Count; i++) { newMetadata.ImcEntries[i] = (XivImc)v.Clone(); } } } // Update Skeleton references to be for the correct set Id. var setId = Destination.Info.SecondaryId == null ? (ushort)Destination.Info.PrimaryId : (ushort)Destination.Info.SecondaryId; foreach (var entry in newMetadata.EstEntries) { entry.Value.SetId = setId; } if (ProgressReporter != null) { ProgressReporter.Report("Copying metdata..."); } // Poke through the variants and adjust any that point to null Material Sets to instead use a valid one. if (newMetadata.ImcEntries.Count > 0 && originalMetadata.ImcEntries.Count > 0) { var valid = newMetadata.ImcEntries.FirstOrDefault(x => x.MaterialSet != 0).MaterialSet; if (valid <= 0) { valid = originalMetadata.ImcEntries.FirstOrDefault(x => x.MaterialSet != 0).MaterialSet; } for (int i = 0; i < newMetadata.ImcEntries.Count; i++) { var entry = newMetadata.ImcEntries[i]; if (entry.MaterialSet == 0) { entry.MaterialSet = valid; } } } await ItemMetadata.SaveMetadata(newMetadata, ApplicationSource, index, modlist); // Save the new Metadata file via the batch function so that it's only written to the memory cache for now. await ItemMetadata.ApplyMetadataBatched(new List <ItemMetadata>() { newMetadata }, index, modlist, false); if (ProgressReporter != null) { ProgressReporter.Report("Filling in missing material sets..."); } // Validate all variants/material sets for valid materials, and copy materials as needed to fix. if (Imc.UsesImc(Destination)) { var mSets = newMetadata.ImcEntries.Select(x => x.MaterialSet).Distinct(); foreach (var mSetId in mSets) { var path = Destination.Info.GetRootFolder() + "material/v" + mSetId.ToString().PadLeft(4, '0') + "/"; foreach (var mkv in newMaterialFileNames) { // See if the material was copied over. var destPath = path + mkv.Value; if (CopiedMaterials.Contains(destPath)) { continue; } string existentCopy = null; // If not, find a material where one *was* copied over. foreach (var mSetId2 in mSets) { var p2 = Destination.Info.GetRootFolder() + "material/v" + mSetId2.ToString().PadLeft(4, '0') + "/"; foreach (var cmat2 in CopiedMaterials) { if (cmat2 == p2 + mkv.Value) { existentCopy = cmat2; break; } } } // Shouldn't ever actually hit this, but if we do, nothing to be done about it. if (existentCopy == null) { continue; } // Copy the material over. await _dat.CopyFile(existentCopy, destPath, ApplicationSource, true, destItem, index, modlist); } } } if (ProgressReporter != null) { ProgressReporter.Report("Updating modlist..."); } if (modPack == null) { modPack = new ModPack() { author = "System", name = "Item Copy - " + srcItem.Name + " to " + iName, url = "", version = "1.0" }; } List <Mod> mods = new List <Mod>(); foreach (var mod in modlist.Mods) { if (allFiles.Contains(mod.fullPath)) { // Ensure all of our modified files are attributed correctly. mod.name = iName; mod.category = iCat; mod.source = ApplicationSource; mod.modPack = modPack; mods.Add(mod); } } if (!modlist.ModPacks.Any(x => x.name == modPack.name)) { modlist.ModPacks.Add(modPack); } if (doSave) { // Save everything. await _index.SaveIndexFile(index); await _modding.SaveModListAsync(modlist); } XivCache.QueueDependencyUpdate(allFiles.ToList()); if (saveDirectory != null) { ProgressReporter.Report("Creating TTMP File..."); var desc = "Item Converter Modpack - " + srcItem.Name + " -> " + iName + "\nCreated at: " + DateTime.Now.ToString(); // Time to save the modlist to file. var dir = new DirectoryInfo(saveDirectory); var _ttmp = new TTMP(dir, ApplicationSource); var smpd = new SimpleModPackData() { Author = modPack.author, Description = desc, Url = modPack.url, Version = new Version(1, 0, 0), Name = modPack.name, SimpleModDataList = new List <SimpleModData>() }; foreach (var mod in mods) { var size = await _dat.GetCompressedFileSize(mod.data.modOffset, df); var smd = new SimpleModData() { Name = iName, FullPath = mod.fullPath, DatFile = df.GetDataFileName(), Category = iCat, IsDefault = false, ModSize = size, ModOffset = mod.data.modOffset }; smpd.SimpleModDataList.Add(smd); } await _ttmp.CreateSimpleModPack(smpd, XivCache.GameInfo.GameDirectory, null, true); } if (ProgressReporter != null) { ProgressReporter.Report("Root copy complete."); } // Return the final file conversion listing. var ret = newModelPaths.Union(newMaterialPaths).Union(newAvfxPaths).Union(newTexturePaths); var dict = ret.ToDictionary(x => x.Key, x => x.Value); dict.Add(Source.Info.GetRootFile(), Destination.Info.GetRootFile()); return(dict); } finally { XivCache.CacheWorkerEnabled = workerStatus; } }