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 SaveMulti() { var _imc = new Imc(XivCache.GameInfo.GameDirectory); var _index = new Index(XivCache.GameInfo.GameDirectory); // Get tokenized map info structs. // This will let us set them in the new Materials and // Detokenize them using the new paths. var mapInfos = _material.GetAllMapInfos(true); // Shader info likewise will be pumped into each new material. var shaderInfo = _material.GetShaderInfo(); // Add new Materials for shared model items. var oldMaterialIdentifier = _material.GetMaterialIdentifier(); var oldMtrlName = Path.GetFileName(_material.MTRLPath); // Ordering these by name ensures that we create textures for the new variants in the first // item alphabetically, just for consistency's sake. var sameModelItems = (await _item.GetSharedModelItems()).OrderBy(x => x.Name, new ItemNameComparer()); var oldVariantString = "/v" + _material.GetVariant().ToString().PadLeft(4, '0') + '/'; var modifiedVariants = new List <int>(); var mtrlReplacementRegex = "_" + oldMaterialIdentifier + ".mtrl"; var mtrlReplacementRegexResult = "_" + _newMaterialIdentifier + ".mtrl"; if (_mode == MaterialEditorMode.NewRace) { mtrlReplacementRegexResult = mtrlReplacementRegex; } var newMtrlName = oldMtrlName.Replace(mtrlReplacementRegex, mtrlReplacementRegexResult); var root = _item.GetRootInfo(); var imcEntries = new List <XivImc>(); var materialSets = new HashSet <byte>(); try { var imcInfo = await _imc.GetFullImcInfo(_item); imcEntries = imcInfo.GetAllEntries(root.Slot, true); materialSets = imcEntries.Select(x => x.MaterialSet).ToHashSet(); } catch { // Item doesn't use IMC entries, and thus only has a single variant. materialSets.Clear(); materialSets.Add(0); } // We need to save our non-existent base material once before we can continue. if (_mode == MaterialEditorMode.NewRace) { await _mtrl.ImportMtrl(_material, _item, XivStrings.TexTools); } var count = 0; var allItems = (await root.ToFullRoot().GetAllItems()); var matNumToItems = new Dictionary <int, List <IItemModel> >(); foreach (var i in allItems) { if (imcEntries.Count <= i.ModelInfo.ImcSubsetID) { continue; } var matSet = imcEntries[i.ModelInfo.ImcSubsetID].MaterialSet; if (!matNumToItems.ContainsKey(matSet)) { matNumToItems.Add(matSet, new List <IItemModel>()); } var saveItem = i; if (typeof(XivCharacter) == i.GetType()) { var temp = (XivCharacter)((XivCharacter)_item).Clone(); temp.Name = saveItem.SecondaryCategory; saveItem = temp; } matNumToItems[matSet].Add(saveItem); } var keys = matNumToItems.Keys.ToList(); foreach (var key in keys) { var list = matNumToItems[key]; matNumToItems[key] = list.OrderBy(x => x.Name, new ItemNameComparer()).ToList(); } // Load and modify all the MTRLs. foreach (var materialSetId in materialSets) { var variantPath = _mtrl.GetMtrlFolder(root, materialSetId); var oldMaterialPath = variantPath + "/" + oldMtrlName; var newMaterialPath = variantPath + "/" + newMtrlName; // Don't create materials for set 0. (SE sets the material ID to 0 when that particular set-slot doesn't actually exist as an item) if (materialSetId == 0 && imcEntries.Count > 0) { continue; } var dxVersion = 11; XivMtrl itemXivMtrl; // Get mtrl path if (await _index.FileExists(oldMaterialPath)) { // Use the Material from this variant as a base? itemXivMtrl = await _mtrl.GetMtrlData(_item, oldMaterialPath, dxVersion); } else { itemXivMtrl = await _mtrl.GetMtrlData(_item, _material.MTRLPath, dxVersion); } // If we're an item that doesn't use IMC variants, make sure we don't accidentally move the material around. if (materialSetId != 0) { // Shift the MTRL to the new variant folder. itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, oldVariantString, "/v" + materialSetId.ToString().PadLeft(4, '0') + "/"); } if (_mode == MaterialEditorMode.NewMulti) { // Change the MTRL part identifier. itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, mtrlReplacementRegex, mtrlReplacementRegexResult); } // Load the Shader Settings itemXivMtrl.SetShaderInfo(shaderInfo, true); // Loop our tokenized map infos and pump them back in // using the new modified material to detokenize them. foreach (var info in mapInfos) { itemXivMtrl.SetMapInfo(info.Usage, (MapInfo)info.Clone()); } IItem item; try { item = matNumToItems[materialSetId].First(); } catch { item = (await XivCache.GetFirstRoot(itemXivMtrl.MTRLPath)).GetFirstItem(); } count++; // Write the new Material await _mtrl.ImportMtrl(itemXivMtrl, item, XivStrings.TexTools); _view.SaveStatusLabel.Content = "Updated " + count + "/" + materialSets.Count + " Material Sets..."; } }
public async Task SaveMulti() { // Get tokenized map info structs. // This will let us set them in the new Materials and // Detokenize them using the new paths. var mapInfos = _material.GetAllMapInfos(true); // Shader info likewise will be pumped into each new material. var shaderInfo = _material.GetShaderInfo(); // Add new Materials for shared model items. var oldMaterialIdentifier = _material.GetMaterialIdentifier(); // Ordering these by name ensures that we create textures for the new variants in the first // item alphabetically, just for consistency's sake. var sameModelItems = (await _item.GetSharedModelItems()).OrderBy(x => x.Name, new ItemNameComparer()); var oldVariantString = "/v" + _material.GetVariant().ToString().PadLeft(4, '0') + '/'; var modifiedVariants = new List <int>(); var mtrlReplacementRegex = "_" + oldMaterialIdentifier + ".mtrl"; var mtrlReplacementRegexResult = "_" + _newMaterialIdentifier + ".mtrl"; // Load and modify all the MTRLs. foreach (var item in sameModelItems) { // Resolve this item's material variant. // - This isn't always the same as the item model variant, for some reason. // - So it has to be resolved manually. var variantMtrlPath = ""; var itemType = ItemType.GetPrimaryItemType(_item); variantMtrlPath = (await _mtrl.GetMtrlPath(item, _material.GetRace(), oldMaterialIdentifier, itemType)).Folder; var match = Regex.Match(variantMtrlPath, "/v([0-9]+)"); var variant = 0; if (match.Success) { variant = Int32.Parse(match.Groups[1].Value); } // Only modify each Variant once. if (modifiedVariants.Contains(variant)) { continue; } var dxVersion = 11; XivMtrl itemXivMtrl; // Get mtrl path -- TODO: Need support here for offhand item materials. // But Offhand support is basically completely broken anyways, so this can wait. itemXivMtrl = await _mtrl.GetMtrlData(_item, _material.GetRace(), oldMaterialIdentifier, dxVersion); // Shift the MTRL to the new variant folder. itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, oldVariantString, "/v" + variant.ToString().PadLeft(4, '0') + "/"); if (_mode == MaterialEditorMode.NewMulti) { // Change the MTRL part identifier. itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, mtrlReplacementRegex, mtrlReplacementRegexResult); } // Load the Shader Settings itemXivMtrl.SetShaderInfo(shaderInfo, true); // Loop our tokenized map infos and pump them back in // using the new modified material to detokenize them. foreach (var info in mapInfos) { itemXivMtrl.SetMapInfo(info.Usage, (MapInfo)info.Clone()); } // Write the new Material await _mtrl.ImportMtrl(itemXivMtrl, item, XivStrings.TexTools); modifiedVariants.Add(variant); _view.SaveStatusLabel.Content = "Updated " + modifiedVariants.Count + " Variants..."; } }
/// <summary> /// Updates the View/ViewModel with a new selected base item. /// </summary> /// <param name="item"></param> /// <returns></returns> public async Task <bool> SetItem(IItem item, MainWindow mainWindow = null) { var gameDirectory = new DirectoryInfo(Properties.Settings.Default.FFXIV_Directory); _imc = new Imc(gameDirectory); _gear = new Gear(gameDirectory, XivLanguages.GetXivLanguage(Properties.Settings.Default.Application_Language)); if (mainWindow != null) { _mainWindow = mainWindow; } _item = item; _tree.Items.Clear(); IItemModel im = null; try { im = (IItemModel)item; } catch (Exception ex) { return(false); } if (im == null || im.ModelInfo == null) { return(false); } var topLevelItem = new TreeViewItem(); topLevelItem.Header = ""; if (im.ModelInfo.PrimaryID > 0) { topLevelItem.Header += CapFirst(item.GetPrimaryItemType().ToString()) + " #" + im.ModelInfo.PrimaryID.ToString().PadLeft(4, '0'); } else { topLevelItem.Header += CapFirst(item.GetPrimaryItemType().ToString()); } _tree.Items.Add(topLevelItem); var nextParent = topLevelItem; if (im.ModelInfo.SecondaryID > 0) { var nextNode = new TreeViewItem(); nextNode.Header += CapFirst(item.GetSecondaryItemType().ToString()) + " #" + im.ModelInfo.SecondaryID.ToString().PadLeft(4, '0'); nextParent.Items.Add(nextNode); nextParent.IsExpanded = true; nextParent = nextNode; } var abbreviation = _item.GetItemSlotAbbreviation(); if (abbreviation != "") { var nextNode = new TreeViewItem(); nextNode.Header = Mdl.SlotAbbreviationDictionary.First(x => x.Value == abbreviation).Key; nextParent.Items.Add(nextNode); nextParent.IsExpanded = true; nextParent = nextNode; } FullImcInfo fullInfo = null; try { fullInfo = await _imc.GetFullImcInfo(im); } catch (Exception ex) { // This item has no IMC file. var nextNode = new TreeViewItem(); nextNode.Header = im.Name; nextNode.DataContext = im; //nextNode.MouseDoubleClick += ItemNode_Activated; nextParent.Items.Add(nextNode); nextParent.IsExpanded = true; nextNode.IsSelected = true; nextParent = nextNode; // No shared items for things without IMC files, so just hide the view entirely? return(false); } var sharedList = await im.GetSharedModelItems(); var myMaterialSetNumber = fullInfo.GetEntry(im.ModelInfo.ImcSubsetID, im.GetItemSlotAbbreviation()).MaterialSet; var myImcNumber = im.ModelInfo.ImcSubsetID; var imcVariantHeaders = new Dictionary <int, TreeViewItem>(); TreeViewItem myImcHeader = null; TreeViewItem myNode = null; foreach (var i in sharedList) { // Get the Variant # information var info = fullInfo.GetEntry(i.ModelInfo.ImcSubsetID, i.GetItemSlotAbbreviation()); if (info == null) { // Invalid IMC Set ID for the item. continue; } var imcVariant = i.ModelInfo.ImcSubsetID; var mtrlVariant = info.MaterialSet; if (!imcVariantHeaders.ContainsKey(imcVariant)) { imcVariantHeaders.Add(imcVariant, new TreeViewItem()); imcVariantHeaders[imcVariant].Header = "Variant " + i.ModelInfo.ImcSubsetID; imcVariantHeaders[imcVariant].DataContext = i.ModelInfo.ImcSubsetID; imcVariantHeaders[imcVariant].Header += " - Material Set: " + mtrlVariant; var hiddenParts = MaskToHidenParts(info.Mask); imcVariantHeaders[i.ModelInfo.ImcSubsetID].Header += " | Hidden Parts: "; if (hiddenParts.Count > 0) { imcVariantHeaders[imcVariant].Header += String.Join(",", hiddenParts); } else { imcVariantHeaders[imcVariant].Header += "None"; } imcVariantHeaders[imcVariant].Header += " - [%] " + i.Name; if (i.ModelInfo.ImcSubsetID == myImcNumber) { myImcHeader = imcVariantHeaders[imcVariant]; } } var nextNode = new TreeViewItem(); nextNode.Header = i.Name; nextNode.DataContext = i; imcVariantHeaders[i.ModelInfo.ImcSubsetID].Items.Add(nextNode); if (i.Name == im.Name) { myNode = nextNode; } else { nextNode.MouseDoubleClick += ItemNode_Activated; } } foreach (var h in imcVariantHeaders) { var st = ((string)h.Value.Header); h.Value.Header = st.Replace("%", h.Value.Items.Count.ToString()); } var ordered = imcVariantHeaders.OrderBy(x => x.Key); foreach (var kv in ordered) { nextParent.Items.Add(kv.Value); } nextParent.IsExpanded = true; if (myImcHeader != null) { myImcHeader.IsExpanded = true; } if (myNode != null) { myNode.IsSelected = true; } return(true); }