private void UpdateMaterialsList() { // Remove the Custom Tag at the end if it's there. _view.MaterialsSource.Remove(CustomTag); // If we don't have this already, we're in our first itteration of this function. if (!_view.MaterialsSource.Contains(SkinTag)) { _view.MaterialsSource.Add(SkinTag); } // Add in any new item materials. foreach (var m in _newModel.MeshGroups) { var result = ItemMaterialRegex.Match(m.Material); if (result.Success) { if (_view.MaterialsSource.Any(x => x.Key == m.Material)) { continue; } // We have a new item material here that we haven't added to the list yet. var raceId = result.Groups[1].Value; var partId = result.Groups[2].Value; var race = XivRaces.GetXivRace(raceId); _view.MaterialsSource.Add(new KeyValuePair <string, string>(m.Material, "Material " + partId.ToUpper() + " - " + XivRaces.GetDisplayName(race))); } } // Re-Add the custom tag. _view.MaterialsSource.Add(CustomTag); }
private void AddMaterial(string m) { if (_view.MaterialsSource.Any(x => x.Key == m)) { return; } var result = ItemMaterialRegex.Match(m); if (result.Success // This check is a little janky, but it's to avoid reading cross-slot references as the same item. // ... Not that this should ever really happen, but it's *technically* possible. && (_root.Info.Slot == null || m.Contains(_root.Info.Slot))) { // We have a new item material here that we haven't added to the list yet. var raceId = result.Groups[1].Value; var partId = result.Groups[2].Value; var race = XivRaces.GetXivRace(raceId); _view.MaterialsSource.Add(new KeyValuePair <string, string>(m, "Material " + partId.ToUpper() + " - " + XivRaces.GetDisplayName(race))); } else { _view.MaterialsSource.Add(new KeyValuePair <string, string>(m, Path.GetFileNameWithoutExtension(m))); } }
/// <summary> /// Automatically attempts to set the racial override status based upon a selected file name. /// </summary> private void SetRaceOverrideByFileName() { if (String.IsNullOrWhiteSpace(_view.FileNameTextBox.Text)) { return; } var fileName = Path.GetFileNameWithoutExtension(_view.FileNameTextBox.Text); var raceRegex = new Regex("c([0-9]{4})"); var match = raceRegex.Match(fileName); if (match.Success) { // Swap the race to a non-NPC version if it's an NPC race. var raceCode = match.Groups[1].Value.Substring(0, 2) + "01"; var race = XivRaces.GetXivRace(raceCode); if (race != _race && race != XivRace.All_Races && _race != XivRace.All_Races) { // We have a valid race. _view.OverrideRaceButton.IsChecked = true; _view.RaceComboBox.SelectedValue = race; } } }
private static async Task <Dictionary <XivRace, ExtraSkeletonEntry> > DeserializeEstData(byte[] data, XivDependencyRoot root, uint dataVersion) { if (dataVersion == 1) { // Version 1 didn't include EST data, so just get the defaults. return(await Est.GetExtraSkeletonEntries(root)); } // 6 Bytes per entry. var count = data.Length / 6; var ret = new Dictionary <XivRace, ExtraSkeletonEntry>(count); for (int i = 0; i < count; i++) { var offset = i * 6; var raceCode = BitConverter.ToUInt16(data, offset); var setId = BitConverter.ToUInt16(data, offset + 2); var skelId = BitConverter.ToUInt16(data, offset + 4); var race = XivRaces.GetXivRace(raceCode); ret.Add(race, new ExtraSkeletonEntry(race, setId, skelId)); } return(ret); }
private void SetupRaces() { var races = Eqp.PlayableRaces; _view.RaceComboBox.SelectedValuePath = "Key"; _view.RaceComboBox.DisplayMemberPath = "Value"; _view.RaceComboBox.IsEnabled = false; if (_race == XivRace.All_Races) { _view.OverrideRaceButton.IsEnabled = false; var kv = new KeyValuePair <XivRace, string>(XivRace.All_Races, "--"); _view.RaceComboBox.Items.Add(kv); } else { foreach (var race in races) { var kv = new KeyValuePair <XivRace, string>(race, XivRaces.GetDisplayName(race)); _view.RaceComboBox.Items.Add(kv); } } _view.RaceComboBox.SelectedValue = _race; }
private static async Task <string> GetExtraSkelbPath(XivDependencyRootInfo root, XivRace race = XivRace.All_Races) { var type = Est.GetEstType(root); // Type doesn't use extra skels. if (type == Est.EstType.Invalid) { return(null); } var id = (ushort)root.PrimaryId; if (type == Est.EstType.Face || type == Est.EstType.Hair) { id = (ushort)root.SecondaryId; } // Hair and face types have a race defined at root level. if ((type == Est.EstType.Face || type == Est.EstType.Hair) && race == XivRace.All_Races) { var ret = new Dictionary <XivRace, ExtraSkeletonEntry>(); race = XivRaces.GetXivRace(root.PrimaryId); } var entry = await Est.GetExtraSkeletonEntry(type, race, id); var skelId = entry.SkelId; if (skelId == 0) { if (race == XivRace.Hyur_Midlander_Male) { return(null); } // Okay, this model, *as defined* has no EX Skeleton. _HOWEVER_ its parent skeleton could. // So we need to re-call and check up the chain until we hit Hyur M. var parent = XivRaceTree.GetNode(race).Parent; if (parent == null) { return(null); } // It's worth noting in these cases, the Skeletal bones themselves will still be using the matrices appropriate // for their parent race in this method, but that should be sufficient for now. return(await GetExtraSkelbPath(root, parent.Race)); } var prefix = Est.GetSystemPrefix(type); var slot = Est.GetSystemSlot(type); var raceCode = XivRaces.GetRaceCode(race); var skelCode = skelId.ToString().PadLeft(4, '0'); var path = $"chara/human/c{raceCode}/skeleton/{slot}/{prefix}{skelCode}/skl_c{raceCode}{prefix}{skelCode}.sklb"; return(path); }
/// <summary> /// Gets the race from the path /// </summary> /// <param name="modPath">The mod path</param> /// <returns>The race as XivRace</returns> public static XivRace GetRace(string modPath) { // I'm 99% sure we have another function somewhere that already does this, but whatever. var match = _extractRaceRegex.Match(modPath); if (match.Success) { return(XivRaces.GetXivRace(match.Groups[1].Value)); } return(XivRace.All_Races); }
/// <summary> /// Retrieves the base racial or body skeleton for a given model file, parsing it from the base /// game files to generate it, if necessary. /// </summary> /// <param name="fullMdlPath"></param> /// <returns></returns> public static async Task <string> GetBaseSkeletonFile(string fullMdlPath) { var root = await XivCache.GetFirstRoot(fullMdlPath); var race = XivRace.All_Races; if (root.Info.PrimaryType == XivItemType.human || root.Info.PrimaryType == XivItemType.equipment || root.Info.PrimaryType == XivItemType.accessory) { race = XivRaces.GetXivRace(fullMdlPath.Substring(1, 4)); } return(await GetBaseSkeletonFile(root.Info, race)); }
public static void Write(byte[] data, ExtraSkeletonEntry entry, int count, int index) { int offset = (int)(4 + (index * 4)); short raceId = Int16.Parse(XivRaces.GetRaceCode(entry.Race)); IOUtil.ReplaceBytesAt(data, BitConverter.GetBytes(entry.SetId), offset); IOUtil.ReplaceBytesAt(data, BitConverter.GetBytes(raceId), offset + 2); var baseOffset = 4 + (count * 4); offset = (int)(baseOffset + (index * 2)); IOUtil.ReplaceBytesAt(data, BitConverter.GetBytes(entry.SkelId), offset); }
/// <summary> /// Retrieves the Ex skeleton file for a given model file, parsing it from the base /// game files to generate it, if necessary. /// </summary> /// <param name="fullMdlPath"></param> /// <returns></returns> public static async Task <string> GetExtraSkeletonFile(string fullMdlPath) { var root = await XivCache.GetFirstRoot(fullMdlPath); var estType = Est.GetEstType(root); if (estType == Est.EstType.Invalid) { return(null); } // This is a hair/hat/face/body model at this point so this is a safe pull. var race = XivRaces.GetXivRace(fullMdlPath.Substring(1, 4)); return(await GetExtraSkeletonFile(root.Info, race)); }
/// <summary> /// Gets the race from the path /// </summary> /// <param name="modPath">The mod path</param> /// <returns>The race as XivRace</returns> private Task <XivRace> GetRace(string modPath) { return(Task.Run(() => { var xivRace = XivRace.All_Races; if (modPath.Contains("ui/") || modPath.Contains(".avfx")) { xivRace = XivRace.All_Races; } else if (modPath.Contains("monster")) { xivRace = XivRace.Monster; } else if (modPath.Contains("bgcommon")) { xivRace = XivRace.All_Races; } else if (modPath.Contains(".tex") || modPath.Contains(".mdl") || modPath.Contains(".atex")) { if (modPath.Contains("accessory") || modPath.Contains("weapon") || modPath.Contains("/common/")) { xivRace = XivRace.All_Races; } else { if (modPath.Contains("demihuman")) { xivRace = XivRace.DemiHuman; } else if (modPath.Contains("/v")) { var raceCode = modPath.Substring(modPath.IndexOf("_c") + 2, 4); xivRace = XivRaces.GetXivRace(raceCode); } else { var raceCode = modPath.Substring(modPath.IndexOf("/c") + 2, 4); xivRace = XivRaces.GetXivRace(raceCode); } } } return xivRace; })); }
private static (XivRace Race, string BodyID) GetSettingsRace(int gender) { var settingsRace = Settings.Default.Default_Race; var defaultBody = "0001"; if (settingsRace.Equals(XivStringRaces.Hyur_M)) { if (gender == 0) { return(XivRaces.GetXivRace("0101"), defaultBody); } } if (settingsRace.Equals(XivStringRaces.Hyur_H)) { if (gender == 0) { return(XivRaces.GetXivRace("0301"), defaultBody); } return(XivRaces.GetXivRace("0401"), defaultBody); } if (settingsRace.Equals(XivStringRaces.Aura_R)) { if (gender == 0) { return(XivRaces.GetXivRace("1301"), defaultBody); } return(XivRaces.GetXivRace("1401"), defaultBody); } if (settingsRace.Equals(XivStringRaces.Aura_X)) { if (gender == 0) { return(XivRaces.GetXivRace("1301"), "0101"); } return(XivRaces.GetXivRace("1401"), "0101"); } return(XivRaces.GetXivRace("0201"), defaultBody); }
public static XivRace GetRaceFromPath(string path) { if (path == null) { return(XivRace.All_Races); } var xivRace = XivRace.All_Races; if (path.Contains("ui/") || path.Contains(".avfx")) { xivRace = XivRace.All_Races; } else if (path.Contains("monster")) { xivRace = XivRace.Monster; } else if (path.Contains(".tex") || path.Contains(".mdl") || path.Contains(".atex")) { if (path.Contains("weapon") || path.Contains("/common/")) { xivRace = XivRace.All_Races; } else { if (path.Contains("demihuman")) { xivRace = XivRace.DemiHuman; } else if (path.Contains("/v")) { var raceCode = path.Substring(path.IndexOf("_c") + 2, 4); xivRace = XivRaces.GetXivRace(raceCode); } else { var raceCode = path.Substring(path.IndexOf("/c") + 2, 4); xivRace = XivRaces.GetXivRace(raceCode); } } } return(xivRace); }
public static ExtraSkeletonEntry Read(byte[] data, uint count, uint index) { int offset = (int)(4 + (index * 4)); var setId = BitConverter.ToUInt16(data, offset); var raceId = BitConverter.ToUInt16(data, offset + 2); var race = XivRaces.GetXivRace(raceId.ToString().PadLeft(4, '0')); var baseOffset = 4 + (count * 4); offset = (int)(baseOffset + (index * 2)); var skelId = BitConverter.ToUInt16(data, offset); var ret = new ExtraSkeletonEntry(race, setId, skelId); return(ret); }
/// <summary> /// Gets the race from the path /// </summary> /// <param name="modPath">The mod path</param> /// <returns>The race as XivRace</returns> private XivRace GetRace(string modPath) { XivRace xivRace = XivRace.All_Races; if (modPath.Contains("ui/") || modPath.Contains(".avfx")) { xivRace = XivRace.All_Races; } else if (modPath.Contains("monster")) { xivRace = XivRace.Monster; } else if (modPath.Contains("bgcommon")) { xivRace = XivRace.All_Races; } else if (modPath.Contains(".tex") || modPath.Contains(".mdl") || modPath.Contains(".atex")) { if (modPath.Contains("accessory") || modPath.Contains("weapon") || modPath.Contains("/common/")) { xivRace = XivRace.All_Races; } else { if (modPath.Contains("demihuman")) { xivRace = XivRace.DemiHuman; } else if (modPath.Contains("/v")) { string raceCode = modPath.Substring(modPath.IndexOf("_c") + 2, 4); xivRace = XivRaces.GetXivRace(raceCode); } else { string raceCode = modPath.Substring(modPath.IndexOf("/c") + 2, 4); xivRace = XivRaces.GetXivRace(raceCode); } } } return(xivRace); }
public FileEntry(string path) { File = path; var fName = System.IO.Path.GetFileName(File); DisplayName = fName; // File name first. var match = _variantMatch.Match(path); if (match.Success) { var variant = match.Groups[1].Value; DisplayName += " - v" + variant; } match = _raceRegx.Match(fName); if (match.Success) { var race = XivRaces.GetXivRace(match.Groups[1].Value); DisplayName += " - " + race.GetDisplayName(); } }
/// <summary> /// Deserializes the binary EQDP data into a dictionary of EQDP entries. /// </summary> /// <param name="data"></param> /// <returns></returns> private static Dictionary <XivRace, EquipmentDeformationParameter> DeserializeEqdpData(byte[] data, XivDependencyRoot root, uint dataVersion) { const int eqdpEntrySize = 5; var entries = data.Length / eqdpEntrySize; var ret = new Dictionary <XivRace, EquipmentDeformationParameter>(); var read = 0; using (var reader = new BinaryReader(new MemoryStream(data))) { while (read < entries) { var raceCode = reader.ReadInt32(); var race = XivRaces.GetXivRace(raceCode.ToString().PadLeft(4, '0')); var eqpByte = reader.ReadByte(); var entry = EquipmentDeformationParameter.FromByte(eqpByte); ret.Add(race, entry); read++; } } // Catch for cases where for some reason the EQP doesn't have all races, // for example, SE adding more races in the future, and we're // reading old metadata entries. foreach (var race in Eqp.PlayableRaces) { if (!ret.ContainsKey(race)) { ret.Add(race, new EquipmentDeformationParameter()); } } return(ret); }
public static async Task <Dictionary <XivRace, ExtraSkeletonEntry> > GetExtraSkeletonEntries(XivDependencyRootInfo root, bool forceDefault = false) { var type = GetEstType(root); if (type == EstType.Invalid) { return(new Dictionary <XivRace, ExtraSkeletonEntry>()); } var id = (ushort)root.PrimaryId; if (type == EstType.Face || type == EstType.Hair) { id = (ushort)root.SecondaryId; } var entries = await GetExtraSkeletonEntries(type, id, forceDefault); // Hair and Faces have to be further trimmed down to only the entries associated with their root. if (type == EstType.Face || type == EstType.Hair) { var ret = new Dictionary <XivRace, ExtraSkeletonEntry>(); var race = XivRaces.GetXivRace(root.PrimaryId); if (entries.ContainsKey(race)) { ret.Add(race, entries[race]); } else { ret.Add(race, new ExtraSkeletonEntry(race, id)); } entries = ret; } return(entries); }
/// <summary> /// Gets the materials for the model /// </summary> /// <returns>A dictionary containing the mesh number(key) and the associated texture data (value)</returns> public static Dictionary <int, ModelTextureData> GetMaterials( DirectoryInfo gameDirectory, IItemModel item, XivMdl mdlData, XivRace race) { var textureDataDictionary = new Dictionary <int, ModelTextureData>(); var mtrlDictionary = new Dictionary <int, XivMtrl>(); var mtrl = new Mtrl(gameDirectory, item.DataFile); var mtrlFilePaths = mdlData.PathData.MaterialList; var hasColorChangeShader = false; Color? customColor = null; WinColor winColor; var materialNum = 0; foreach (var mtrlFilePath in mtrlFilePaths) { var mtrlItem = new XivGenericItemModel { Category = item.Category, ItemCategory = item.ItemCategory, ItemSubCategory = item.ItemSubCategory, ModelInfo = new XivModelInfo { Body = item.ModelInfo.Body, ModelID = item.ModelInfo.ModelID, ModelType = item.ModelInfo.ModelType, Variant = item.ModelInfo.Variant }, Name = item.Name }; var modelID = mtrlItem.ModelInfo.ModelID; var bodyID = mtrlItem.ModelInfo.Body; var filePath = mtrlFilePath; if (!filePath.Contains("hou") && mtrlFilePath.Count(x => x == '/') > 1) { filePath = mtrlFilePath.Substring(mtrlFilePath.LastIndexOf("/")); } var typeChar = $"{mtrlFilePath[4]}{mtrlFilePath[9]}"; var raceString = ""; switch (typeChar) { // Character Body case "cb": var body = mtrlFilePath.Substring(mtrlFilePath.IndexOf("b") + 1, 4); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); if (!raceString.Equals("0901") && !raceString.Equals("1001") && !raceString.Equals("1101")) { var gender = 0; if (int.Parse(raceString.Substring(0, 2)) % 2 == 0) { gender = 1; } var settingsRace = GetSettingsRace(gender); race = settingsRace.Race; filePath = mtrlFilePath.Replace(raceString, race.GetRaceCode()).Replace(body, settingsRace.BodyID); body = settingsRace.BodyID; } mtrlItem = new XivGenericItemModel { Category = XivStrings.Character, ItemCategory = XivStrings.Body, Name = XivStrings.Body, ModelInfo = new XivModelInfo { Body = int.Parse(body) } }; winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Skin_Color); customColor = new Color(winColor.R, winColor.G, winColor.B, winColor.A); break; // Face case "cf": bodyID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("f") + 1, 4)); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); mtrlItem = new XivGenericItemModel { Category = XivStrings.Character, ItemCategory = XivStrings.Face, Name = XivStrings.Face, ModelInfo = new XivModelInfo { Body = bodyID } }; break; // Hair case "ch": bodyID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("h") + 1, 4)); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); mtrlItem = new XivGenericItemModel { Category = XivStrings.Character, ItemCategory = XivStrings.Hair, Name = XivStrings.Hair, ModelInfo = new XivModelInfo { Body = bodyID } }; winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Hair_Color); customColor = new Color(winColor.R, winColor.G, winColor.B, winColor.A); break; // Tail case "ct": var tempPath = mtrlFilePath.Substring(4); bodyID = int.Parse(tempPath.Substring(tempPath.IndexOf("t") + 1, 4)); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); mtrlItem = new XivGenericItemModel { Category = XivStrings.Character, ItemCategory = XivStrings.Tail, Name = XivStrings.Tail, ModelInfo = new XivModelInfo { Body = bodyID } }; winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Hair_Color); customColor = new Color(winColor.R, winColor.G, winColor.B, winColor.A); break; // Equipment case "ce": modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("e") + 1, 4)); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); mtrlItem.ModelInfo.ModelID = modelID; break; // Accessory case "ca": modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("a") + 1, 4)); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); mtrlItem.ModelInfo.ModelID = modelID; break; // Weapon case "wb": modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("w") + 1, 4)); bodyID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("b") + 1, 4)); mtrlItem.ModelInfo.ModelID = modelID; mtrlItem.ModelInfo.Body = bodyID; break; // Monster case "mb": modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("_m") + 2, 4)); bodyID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("b") + 1, 4)); mtrlItem.ModelInfo.ModelID = modelID; mtrlItem.ModelInfo.Body = bodyID; break; // DemiHuman case "de": modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("d") + 1, 4)); bodyID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("e") + 1, 4)); mtrlItem.ModelInfo.ModelID = modelID; mtrlItem.ModelInfo.Body = bodyID; break; default: break; } var dxVersion = int.Parse(Settings.Default.DX_Version); var mtrlFile = filePath.Remove(0, 1); XivMtrl mtrlData; try { mtrlData = mtrl.GetMtrlData(mtrlItem, race, mtrlFile, dxVersion); } catch (Exception) { if (mtrlItem.ModelInfo.ModelID == item.ModelInfo.ModelID) { throw; } // Fall back to material data from the primary model. mtrlData = mtrl.GetMtrlData(item, race, mtrlFile, dxVersion); } if (mtrlData.Shader.Contains("colorchange")) { hasColorChangeShader = true; } mtrlDictionary.Add(materialNum, mtrlData); materialNum++; } foreach (var xivMtrl in mtrlDictionary) { var modelTexture = new ModelTexture(gameDirectory, xivMtrl.Value); if (hasColorChangeShader) { var modelMaps = modelTexture.GetModelMaps(null, true); textureDataDictionary.Add(xivMtrl.Key, modelMaps); } else { if (item.ItemCategory.Equals(XivStrings.Face)) { var path = xivMtrl.Value.MTRLPath; if (path.Contains("_iri_")) { winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Iris_Color); } else if (path.Contains("_etc_")) { winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Etc_Color); } else { winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Skin_Color); } customColor = new Color(winColor.R, winColor.G, winColor.B, winColor.A); } var modelMaps = modelTexture.GetModelMaps(customColor); textureDataDictionary.Add(xivMtrl.Key, modelMaps); } } return(textureDataDictionary); }
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)); } } } }
private async Task <List <IItem> > BuildCategoryTree() { foreach (var kv in _categoryStructure) { // Make the top level node. var e = new ItemTreeElement(null, null, kv.Key); foreach (var secondary in kv.Value) { var e2 = new ItemTreeElement(e, null, secondary); e.Children.Add(e2); } CategoryElements.Add(e); } var gameDir = XivCache.GameInfo.GameDirectory; var language = XivCache.GameInfo.GameLanguage; var items = await XivCache.GetFullItemList(); foreach (var item in items) { // Find what node we should be attached to. ItemTreeElement catParent = null; var topLevel = CategoryElements.FirstOrDefault(x => x.DisplayName == item.PrimaryCategory); if (topLevel == null) { topLevel = new ItemTreeElement(null, null, item.PrimaryCategory); CategoryElements.Add(topLevel); } var secondLevel = topLevel.Children.FirstOrDefault(x => x.DisplayName == item.SecondaryCategory); if (secondLevel == null) { if (item.SecondaryCategory == item.Name) { // These are a special snowflake case. secondLevel = topLevel; } else { secondLevel = new ItemTreeElement(topLevel, null, item.SecondaryCategory); topLevel.Children.Add(secondLevel); } } catParent = secondLevel; ItemTreeElement setParent = null; // Try and see if we have a valid root parent to attach to in the sets tree. try { var type = item.GetType(); // Perf. Much faster to just not test those types at all, as we know they won't resolve. if (type != typeof(XivUi)) { var itemRoot = item.GetRootInfo(); if (itemRoot.PrimaryType != XivItemType.unknown) { var st = itemRoot.ToString(); if (DependencyRootNodes.ContainsKey(st)) { setParent = DependencyRootNodes[st]; } } } } catch (Exception ex) { throw; } ItemTreeElement e2; if (ExpandCharacterMenu && typeof(XivCharacter) == item.GetType()) { var charItem = (XivCharacter)item; if (charItem.ModelInfo != null && charItem.ModelInfo.PrimaryID > 0) { e2 = new ItemTreeElement(catParent, setParent, item.Name); } else { e2 = new ItemTreeElement(catParent, setParent, item); } } else { e2 = new ItemTreeElement(catParent, setParent, item); } if (catParent != null) { catParent.Children.Add(e2); } if (setParent != null) { setParent.Children.Add(e2); } if (ExpandCharacterMenu) { if (typeof(XivCharacter) == item.GetType()) { // Cache the references to our human root nodes. var charItem = (XivCharacter)item; var type = charItem.GetSecondaryItemType(); if (type != XivItemType.none) { var raceCode = charItem.ModelInfo.PrimaryID; var race = XivRaces.GetXivRace(raceCode); if (!HumanParentNodes.ContainsKey(type)) { HumanParentNodes.Add(type, new Dictionary <XivRace, ItemTreeElement>()); } HumanParentNodes[type].Add(race, e2); } } } } return(items); }
private async void AnyTextChanged(object sender, TextChangedEventArgs e) { var to = ToBox.Text; var from = FromBox.Text; if (string.IsNullOrWhiteSpace(to) || string.IsNullOrWhiteSpace(from)) { CopyButton.IsEnabled = false; return; } to = to.Trim().ToLower(); from = from.Trim().ToLower(); if (!to.EndsWith(".mdl") || !from.EndsWith(".mdl")) { MaterialCopyNotice.Text = "--"; MaterialCopyNotice.Foreground = Brushes.Black; RaceChangeNotice.Text = "--"; RaceChangeNotice.Foreground = Brushes.Black; CopyButton.IsEnabled = false; return; } try { var df = IOUtil.GetDataFileFromPath(to); var df2 = IOUtil.GetDataFileFromPath(from); if (df != df2) { MaterialCopyNotice.Text = "Source and target files must exist within the same data file."; MaterialCopyNotice.Foreground = Brushes.Red; RaceChangeNotice.Text = "--"; RaceChangeNotice.Foreground = Brushes.Black; CopyButton.IsEnabled = false; return; } } catch { MaterialCopyNotice.Text = "At least one file path is not a valid internal FFXIV file path."; MaterialCopyNotice.Foreground = Brushes.Red; RaceChangeNotice.Text = "--"; RaceChangeNotice.Foreground = Brushes.Black; CopyButton.IsEnabled = false; return; } CopyButton.IsEnabled = true; var toRoot = await XivCache.GetFirstRoot(to); var fromRoot = await XivCache.GetFirstRoot(from); if (toRoot == null || fromRoot == null) { MaterialCopyNotice.Text = "Unknown File Root - Materials and textures will not be copied."; MaterialCopyNotice.Foreground = Brushes.DarkGoldenrod; RaceChangeNotice.Text = "Unknown File Root - Model will not be racially adjusted."; RaceChangeNotice.Foreground = Brushes.DarkGoldenrod; return; } else { MaterialCopyNotice.Text = "Materials and textures will be copied to destination root folder."; MaterialCopyNotice.Foreground = Brushes.Green; } var raceRegex = new Regex("c([0-9]{4})"); var toMatch = raceRegex.Match(to); var fromMatch = raceRegex.Match(from); if (!toMatch.Success || !fromMatch.Success) { RaceChangeNotice.Text = "Model is not racial - Model will not be racially adjusted."; RaceChangeNotice.Foreground = Brushes.Black; return; } var toRace = XivRaces.GetXivRace(toMatch.Groups[1].Value); var fromRace = XivRaces.GetXivRace(fromMatch.Groups[1].Value); if (toRace == fromRace) { RaceChangeNotice.Text = "Model races are identical - Model will not be racially adjusted."; RaceChangeNotice.Foreground = Brushes.Black; return; } RaceChangeNotice.Text = "Model will be adjusted from " + fromRace.GetDisplayName() + " to " + toRace.GetDisplayName() + "."; RaceChangeNotice.Foreground = Brushes.Green; }
/// <summary> /// This should only really be called directly if the control was created with DeferLoading set to true. /// </summary> /// <returns></returns> public async Task LoadItems() { if (_READY) { SearchTimer.Stop(); SearchTimer.Dispose(); ClearSelection(); } if (LockUiFunction != null) { await LockUiFunction(UIStrings.Loading_Items, null, this); } // Pump us into another thread so the UI stays nice and fresh. await Task.Run(async() => { CategoryElements = new ObservableCollection <ItemTreeElement>(); SetElements = new ObservableCollection <ItemTreeElement>(); DependencyRootNodes = new Dictionary <string, ItemTreeElement>(); try { // Gotta build set tree first, so the items from the item list can latch onto the nodes there. BuildSetTree(); await BuildCategoryTree(); } catch (Exception ex) { FlexibleMessageBox.Show("An error occurred while loading the item list.\n" + ex.Message, "Item List Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning); return; } var toAdd = new List <(ItemTreeElement parent, ItemTreeElement child)>(); foreach (var kv in DependencyRootNodes) { // This dependency root had no EXD-Items associated with it. // Gotta make a generic item for it. if (kv.Value.Children.Count == 0) { // See if we can actually turn this root into a fully fledged item. try { var root = await XivCache.GetFirstRoot(kv.Key); ItemTreeElement e; if (root != null) { // If we can, add it into the list. var item = root.ToRawItem(); e = new ItemTreeElement(null, kv.Value, item); toAdd.Add((kv.Value, e)); if (ExpandCharacterMenu && root.Info.PrimaryType == XivItemType.human) { // Cache our human type elements if we need them later. var race = XivRaces.GetXivRace(root.Info.PrimaryId); var sType = (XivItemType)root.Info.SecondaryType; if (!HumanParentNodes.ContainsKey(sType)) { continue; } if (!HumanParentNodes[sType].ContainsKey(race)) { continue; } var parent = HumanParentNodes[sType][race]; e.CategoryParent = parent; parent.Children.Add(e); } } else { e = new ItemTreeElement(null, kv.Value, "[Unsupported]"); toAdd.Add((kv.Value, e)); } } catch (Exception ex) { throw; } } } // Loop back through to add the new items, so we're not affecting the previous iteration. foreach (var tup in toAdd) { tup.parent.Children.Add(tup.child); } }); var view = (CollectionView)CollectionViewSource.GetDefaultView(CategoryElements); view.Filter = SearchFilter; view = (CollectionView)CollectionViewSource.GetDefaultView(SetElements); view.Filter = SearchFilter; SearchTimer = new Timer(300); SearchTimer.Elapsed += Search; CategoryTree.ItemsSource = CategoryElements; SetTree.ItemsSource = SetElements; _READY = true; Search(this, null); if (UnlockUiFunction != null) { await UnlockUiFunction(this); } if (ItemsLoaded != null) { ItemsLoaded.Invoke(this, null); } }