/// <summary> /// Gets the Part for a given Character Item /// </summary> /// <remarks> /// For Body and Tail Character Items since they don't have Types /// </remarks> /// <param name="charaItem">The character item</param> /// <param name="race">The race</param> /// <param name="num">The character item number</param> /// <param name="variant">the variant for the mtrl folder</param> /// <returns>An array of characters containing the parts for the texture</returns> public async Task <char[]> GetPartForTextures(XivCharacter charaItem, XivRace race, int num, int variant = 1) { var folder = ""; var file = ""; var parts = Constants.Alphabet; if (charaItem.SecondaryCategory == XivStrings.Body) { folder = string.Format(XivStrings.BodyMtrlFolderVar, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), variant.ToString().PadLeft(4, '0')); file = XivStrings.BodyMtrlFile; } else if (charaItem.SecondaryCategory == XivStrings.Tail) { folder = string.Format(XivStrings.TailMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); file = XivStrings.TailMtrlFile; } else if (charaItem.SecondaryCategory == XivStrings.Ear) { folder = string.Format(XivStrings.EarsMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); file = XivStrings.EarsMtrlFile; } var fileList = await _index.GetAllHashedFilesInFolder(HashGenerator.GetHash(folder), XivDataFile._04_Chara); return((from part in parts let mtrlFile = string.Format(file, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), part) where fileList.Contains(HashGenerator.GetHash(mtrlFile)) select part).ToArray()); }
/// <summary> /// Gets the Part for a given Character Item /// </summary> /// <remarks> /// For Body and Tail Character Items since they don't have Types /// </remarks> /// <param name="charaItem">The character item</param> /// <param name="race">The race</param> /// <param name="num">The character item number</param> /// <returns>A dictionary containging [</returns> public char[] GetPartForTextures(XivCharacter charaItem, XivRace race, int num) { var index = new Index(_gameDirectory); var folder = ""; var file = ""; var parts = new[] { 'a', 'b', 'c', 'd', 'e', 'f' }; if (charaItem.ItemCategory == XivStrings.Body) { folder = string.Format(XivStrings.BodyMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); file = XivStrings.BodyMtrlFile; } else if (charaItem.ItemCategory == XivStrings.Tail) { folder = string.Format(XivStrings.TailMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); file = XivStrings.TailMtrlFile; } var fileList = index.GetAllHashedFilesInFolder(HashGenerator.GetHash(folder), XivDataFile._04_Chara); return((from part in parts let mtrlFile = string.Format(file, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), part) where fileList.Contains(HashGenerator.GetHash(mtrlFile)) select part).ToArray()); }
/// <summary> /// Gets the Type and Part for a given Character Item /// </summary> /// <remarks> /// Only Hair and Face Character Items have Types /// </remarks> /// <param name="charaItem">The character item</param> /// <param name="race">The race</param> /// <param name="num">The character item number</param> /// <returns>A dictionary containing</returns> public async Task <Dictionary <string, char[]> > GetTypePartForTextures(XivCharacter charaItem, XivRace race, int num) { var typePartDictionary = new Dictionary <string, char[]>(); var folder = ""; var file = ""; var typeDict = HairSlotAbbreviationDictionary; var parts = new[] { 'a', 'b', 'c', 'd', 'e', 'f' }; if (charaItem.ItemCategory == XivStrings.Hair) { folder = string.Format(XivStrings.HairMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); file = XivStrings.HairMtrlFile; } else if (charaItem.ItemCategory == XivStrings.Face) { folder = string.Format(XivStrings.FaceMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = FaceSlotAbbreviationDictionary; file = XivStrings.FaceMtrlFile; } else if (charaItem.ItemCategory == XivStrings.Ears) { folder = string.Format(XivStrings.EarsMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = EarsSlotAbbreviationDictionary; file = XivStrings.EarsMtrlFile; } var fileList = await _index.GetAllHashedFilesInFolder(HashGenerator.GetHash(folder), XivDataFile._04_Chara); foreach (var type in typeDict) { var partList = new List <char>(); foreach (var part in parts) { var mtrlFile = string.Format(file, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), type.Value, part); if (fileList.Contains(HashGenerator.GetHash(mtrlFile))) { partList.Add(part); } } if (partList.Count > 0) { typePartDictionary.Add(type.Key, partList.ToArray()); } } return(typePartDictionary); }
/// <summary> /// Gets the Type of models for a given Character Item /// </summary> /// <param name="charaItem">The character item</param> /// <param name="race">The race</param> /// <param name="num">The character item number</param> /// <returns>A dictionary containging [</returns> public async Task <List <string> > GetTypeForModels(XivCharacter charaItem, XivRace race, int num) { var folder = ""; var file = ""; var typeDict = HairSlotAbbreviationDictionary; if (charaItem.ItemCategory == XivStrings.Body) { folder = string.Format(XivStrings.BodyMDLFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = BodySlotAbbreviationDictionary; file = XivStrings.BodyMDLFile; } else if (charaItem.ItemCategory == XivStrings.Hair) { folder = string.Format(XivStrings.HairMDLFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = HairSlotAbbreviationDictionary; file = XivStrings.HairMDLFile; } else if (charaItem.ItemCategory == XivStrings.Face) { folder = string.Format(XivStrings.FaceMDLFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = FaceSlotAbbreviationDictionary; file = XivStrings.FaceMDLFile; } else if (charaItem.ItemCategory == XivStrings.Tail) { folder = string.Format(XivStrings.TailMDLFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = TailSlotAbbreviationDictionary; file = XivStrings.TailMDLFile; } var fileList = await _index.GetAllHashedFilesInFolder(HashGenerator.GetHash(folder), XivDataFile._04_Chara); var typeList = new List <string>(); foreach (var type in typeDict) { var mdlFile = string.Format(file, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), type.Value); if (fileList.Contains(HashGenerator.GetHash(mdlFile))) { typeList.Add(type.Key); } } return(typeList); }
public async Task <List <int> > GetVariantsForTextures(XivCharacter charaItem, XivRace race, int num) { var variantList = new List <int>(); if (charaItem.ItemCategory == XivStrings.Body) { if (_language != XivLanguage.Korean) { var testDictionary = new Dictionary <int, int>(); for (var i = 0; i < 50; i++) { var folder = string.Format(XivStrings.BodyMtrlFolderVar, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), i.ToString().PadLeft(4, '0')); testDictionary.Add(HashGenerator.GetHash(folder), i); variantList = await _index.GetFolderExistsList(testDictionary, XivDataFile._04_Chara); variantList.Sort(); } } else { variantList.Add(1); } } if (charaItem.ItemCategory == XivStrings.Tail) { if (_language != XivLanguage.Korean) { var testDictionary = new Dictionary <int, int>(); for (var i = 0; i < 50; i++) { var folder = string.Format(XivStrings.TailMtrlFolderVar, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), i.ToString().PadLeft(4, '0')); testDictionary.Add(HashGenerator.GetHash(folder), i); variantList = await _index.GetFolderExistsList(testDictionary, XivDataFile._04_Chara); variantList.Sort(); } } else { variantList.Add(1); } } return(variantList); }
private static IItemModel GetFaceModel(XivRace race, byte head, byte eyebrows, byte eyes, byte nose, byte jaw, byte mouth) { int raceCode = int.Parse(race.GetRaceCode()); foreach (IItem item in allItems) { if (item is IItemModel itemModel) { if (item.PrimaryCategory != "Character") { continue; } if (item.SecondaryCategory != "Face") { continue; } if (itemModel.ModelInfo.PrimaryID != raceCode) { continue; } XivCharacter faceItem = (XivCharacter)itemModel.Clone(); faceItem.ModelInfo = new XivModelInfo(); faceItem.ModelInfo.SecondaryID = head; return(faceItem); } } throw new Exception($"Failed to find face model: {race}, {head}"); }
/// <summary> /// Gets the raw equipment or accessory deformation parameters file for a given race. /// </summary> /// <returns></returns> private async Task <List <byte> > LoadEquipmentDeformationFile(XivRace race, bool accessory = false) { var rootPath = accessory ? AccessoryDeformerParameterRootPath : EquipmentDeformerParameterRootPath; var fileName = rootPath + "c" + race.GetRaceCode() + "." + EquipmentDeformerParameterExtension; return(new List <byte>(await _dat.GetType2Data(fileName, false))); }
private static IItemModel GetHairModel(XivRace race, byte hair) { int raceCode = int.Parse(race.GetRaceCode()); foreach (IItem item in allItems) { if (item is IItemModel itemModel) { if (item.PrimaryCategory != "Character") { continue; } if (item.SecondaryCategory != "Hair") { continue; } if (itemModel.ModelInfo.PrimaryID != raceCode) { continue; } XivCharacter faceItem = (XivCharacter)itemModel.Clone(); faceItem.ModelInfo = new XivModelInfo(); faceItem.ModelInfo.SecondaryID = hair; return(faceItem); } } throw new Exception($"Failed to find hair model: {race}, {hair}"); }
/// <summary> /// Retrieves the file path to the model's base skeleton file. /// </summary> /// <param name="fullMdlPath"></param> private static async Task <string> GetBaseSkelbPath(XivDependencyRootInfo root, XivRace race = XivRace.All_Races) { var skelFolder = ""; var skelFile = ""; if (root.PrimaryType != XivItemType.human && root.PrimaryType != XivItemType.equipment && root.PrimaryType != XivItemType.accessory) { var typeName = XivItemTypes.GetSystemName(root.PrimaryType); var prefix = XivItemTypes.GetSystemPrefix(root.PrimaryType); var id = root.PrimaryId.ToString().PadLeft(4, '0'); var bodyCode = "0001"; var path = $"chara/{typeName}/{prefix}{id}/skeleton/base/b{bodyCode}/skl_{prefix}{id}b{bodyCode}.sklb"; return(path); } else { // Equipment and accessories need an external race passed in. var raceCode = race.GetRaceCode(); if (root.PrimaryType == XivItemType.human && race != XivRace.All_Races) { raceCode = root.PrimaryId.ToString().PadLeft(4, '0'); } var bodyCode = "0001"; skelFolder = $"chara/human/c{raceCode}/skeleton/base/b{bodyCode}"; skelFile = $"skl_c{raceCode}b{bodyCode}.sklb"; return(skelFolder + "/" + skelFile); } }
/// <summary> /// Gets the matrices for the bones used in the model /// </summary> /// <param name="boneList">List of bones used in the model</param> /// <param name="targetRace">Target Race to get the bone data for</param> /// <returns>A matrix array containing the pose data for each bone</returns> private Matrix[] GetMatrices(List <string> boneList, XivRace targetRace) { // Get the skeleton file for the target race var skeletonFile = Directory.GetCurrentDirectory() + $"/Skeletons/c{targetRace.GetRaceCode()}.skel"; var skeletonData = File.ReadAllLines(skeletonFile); // Deserialize the skeleton json and create a dictionary of all bones var skelData = new Dictionary <string, SkeletonData>(); foreach (var b in skeletonData) { if (b == "") { continue; } var j = JsonConvert.DeserializeObject <SkeletonData>(b); skelData.Add(j.BoneName, j); } // Add matrices for bones in the model to a list // Add missing bones if they exist in the model but not in the target race var matrixList = new List <Matrix>(); var missingBones = new List <string>(); foreach (var bone in boneList) { if (skelData.ContainsKey(bone)) { var matrix = new Matrix(skelData[bone].InversePoseMatrix); matrix.Invert(); matrixList.Add(matrix); } else { missingBones.Add(bone); } } // Show a warning when there is bones in the model that do not exist in the target race skeleton // The model will still be added, but with no bones animation will not work for that part if (missingBones.Count > 0) { var warning = new StringBuilder(); warning.AppendLine(); foreach (var bone in missingBones) { warning.AppendLine(bone); } FlexibleMessageBox.Show(string.Format(UIMessages.MissingBones, targetRace, warning), UIMessages.MissingBonesTitle, MessageBoxButtons.OK, MessageBoxIcon.Warning); } return(matrixList.ToArray()); }
/// <summary> /// Creates the Bones to be used in the format Helix Toolkit uses /// </summary> /// <param name="boneList">The list of bones in the model</param> /// <param name="targetRace">The target race to get the bones from</param> /// <returns>A list of Bone structures used by Helix Toolkit</returns> private List <Bone> MakeHelixBones(List <string> boneList, XivRace targetRace) { // Get the skeleton file for the target race var skeletonFile = Directory.GetCurrentDirectory() + $"/Skeletons/c{targetRace.GetRaceCode()}.skel"; var skeletonData = File.ReadAllLines(skeletonFile); // Deserialize the skeleton json and create a dictionary of all bones var boneDict = new Dictionary <int, SkeletonData>(); foreach (var b in skeletonData) { if (b == "") { continue; } var j = JsonConvert.DeserializeObject <SkeletonData>(b); boneDict.Add(j.BoneNumber, j); } // Add only the bones that are contained in the model including all parent bones var bonesInModel = new List <SkeletonData>(); foreach (var bone in boneDict) { if (!boneList.Contains(bone.Value.BoneName)) { continue; } bonesInModel.Add(bone.Value); AddBones(boneDict, bonesInModel, bone.Value); } // Create a bone list with the bones for the model in helix toolkit format var helixBoneList = new List <Bone>(); foreach (var bone in bonesInModel) { var bp = new Matrix(bone.InversePoseMatrix); bp.Invert(); helixBoneList.Add(new Bone { BindPose = bp, Name = bone.BoneName, ParentIndex = bone.BoneParent }); } return(helixBoneList); }
/// <summary> /// Gets the Part for a given Character Item /// </summary> /// <remarks> /// For Body and Tail Character Items since they don't have Types /// </remarks> /// <param name="charaItem">The character item</param> /// <param name="race">The race</param> /// <param name="num">The character item number</param> /// <returns>A dictionary containging [</returns> public async Task <char[]> GetPartForTextures(XivCharacter charaItem, XivRace race, int num) { var folder = ""; var file = ""; var parts = new[] { 'a', 'b', 'c', 'd', 'e', 'f' }; if (charaItem.ItemCategory == XivStrings.Body) { if (_language != XivLanguage.Korean && _language != XivLanguage.Chinese) { folder = string.Format(XivStrings.BodyMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); } else { folder = string.Format(XivStrings.BodyMtrlFolderOld, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); } file = XivStrings.BodyMtrlFile; } else if (charaItem.ItemCategory == XivStrings.Tail) { if (_language != XivLanguage.Korean && _language != XivLanguage.Chinese) { folder = string.Format(XivStrings.TailMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); } else { folder = string.Format(XivStrings.TailMtrlFolderOld, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); } file = XivStrings.TailMtrlFile; } var fileList = await _index.GetAllHashedFilesInFolder(HashGenerator.GetHash(folder), XivDataFile._04_Chara); return((from part in parts let mtrlFile = string.Format(file, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), part) where fileList.Contains(HashGenerator.GetHash(mtrlFile)) select part).ToArray()); }
private static IItemModel GetEarsTailModel(XivRace race, byte id) { int raceCode = int.Parse(race.GetRaceCode()); if (id == 0) { id = 1; } foreach (IItem item in allItems) { if (item is IItemModel itemModel) { if (item.PrimaryCategory != "Character") { continue; } if (item.SecondaryCategory != "Ear" && item.SecondaryCategory != "Tail") { continue; } if (itemModel.ModelInfo.PrimaryID != raceCode) { continue; } XivCharacter earTailItem = (XivCharacter)itemModel.Clone(); earTailItem.ModelInfo = new XivModelInfo(); earTailItem.ModelInfo.SecondaryID = id; return(earTailItem); } } throw new Exception($"Failed to find Ears/Tail model: {race}, {id}"); }
/// <summary> /// Gets the mtrl path for a given item /// </summary> /// <param name="itemModel">Item that contains model data</param> /// <param name="xivRace">The race for the requested data</param> /// <param name="part">The mtrl part <see cref="GearInfo.GetPartList(IItemModel, XivRace)"/></param> /// <param name="itemType">The type of the item</param> /// <returns>A tuple containing the mtrl folder and file, and whether it has a vfx</returns> private (string Folder, string File, bool HasVfx) GetMtrlPath(IItemModel itemModel, XivRace xivRace, char part, XivItemType itemType) { // The default version number var version = "0001"; var hasVfx = false; if (itemType != XivItemType.human && itemType != XivItemType.furniture) { // get the items version from the imc file var imc = new Imc(_gameDirectory, _dataFile); var imcInfo = imc.GetImcInfo(itemModel, itemModel.ModelInfo); version = imcInfo.Version.ToString().PadLeft(4, '0'); if (imcInfo.Vfx > 0) { hasVfx = true; } } var id = itemModel.ModelInfo.ModelID.ToString().PadLeft(4, '0'); var bodyVer = itemModel.ModelInfo.Body.ToString().PadLeft(4, '0'); var race = xivRace.GetRaceCode(); string mtrlFolder = "", mtrlFile = ""; switch (itemType) { case XivItemType.equipment: mtrlFolder = $"chara/{itemType}/e{id}/material/v{version}"; mtrlFile = $"mt_c{race}e{id}_{SlotAbbreviationDictionary[itemModel.ItemCategory]}_{part}{MtrlExtension}"; break; case XivItemType.accessory: mtrlFolder = $"chara/{itemType}/a{id}/material/v{version}"; mtrlFile = $"mt_c{race}a{id}_{SlotAbbreviationDictionary[itemModel.ItemCategory]}_{part}{MtrlExtension}"; break; case XivItemType.weapon: mtrlFolder = $"chara/{itemType}/w{id}/obj/body/b{bodyVer}/material/v{version}"; mtrlFile = $"mt_w{id}b{bodyVer}_{part}{MtrlExtension}"; break; case XivItemType.monster: mtrlFolder = $"chara/{itemType}/m{id}/obj/body/b{bodyVer}/material/v{version}"; mtrlFile = $"mt_m{id}b{bodyVer}_{part}{MtrlExtension}"; break; case XivItemType.demihuman: mtrlFolder = $"chara/{itemType}/d{id}/obj/equipment/e{bodyVer}/material/v{version}"; mtrlFile = $"mt_d{id}e{bodyVer}_{SlotAbbreviationDictionary[itemModel.ItemSubCategory]}_{part}{MtrlExtension}"; break; case XivItemType.human: if (itemModel.ItemCategory.Equals(XivStrings.Body)) { mtrlFolder = $"chara/{itemType}/c{race}/obj/body/b{bodyVer}/material"; mtrlFile = $"mt_c{race}b{bodyVer}_{part}{MtrlExtension}"; } else if (itemModel.ItemCategory.Equals(XivStrings.Hair)) { // Hair has a version number, but no IMC, so we leave it at the default 0001 mtrlFolder = $"chara/{itemType}/c{race}/obj/hair/h{bodyVer}/material/v{version}"; mtrlFile = $"mt_c{race}h{bodyVer}_{SlotAbbreviationDictionary[itemModel.ItemSubCategory]}_{part}{MtrlExtension}"; } else if (itemModel.ItemCategory.Equals(XivStrings.Face)) { mtrlFolder = $"chara/{itemType}/c{race}/obj/face/f{bodyVer}/material"; mtrlFile = $"mt_c{race}f{bodyVer}_{SlotAbbreviationDictionary[itemModel.ItemSubCategory]}_{part}{MtrlExtension}"; } else if (itemModel.ItemCategory.Equals(XivStrings.Tail)) { mtrlFolder = $"chara/{itemType}/c{race}/obj/tail/t{bodyVer}/material"; mtrlFile = $"mt_c{race}t{bodyVer}_{part}{MtrlExtension}"; } break; case XivItemType.furniture: if (itemModel.ItemCategory.Equals(XivStrings.Furniture_Indoor)) { mtrlFolder = $"bgcommon/hou/indoor/general/{id}/material"; mtrlFile = $"fun_b0_m{id}_0{part}{MtrlExtension}"; } else if (itemModel.ItemCategory.Equals(XivStrings.Furniture_Outdoor)) { mtrlFolder = $"bgcommon/hou/outdoor/general/{id}/material"; mtrlFile = $"gar_b0_m{id}_0{part}{MtrlExtension}"; } break; default: mtrlFolder = ""; mtrlFile = ""; break; } return(mtrlFolder, mtrlFile, hasVfx); }
/// <summary> /// Gets the list of available mtrl parts for a given item /// </summary> /// <param name="itemModel">An item that contains model data</param> /// <param name="xivRace">The race for the requested data</param> /// <returns>A list of part characters</returns> public async Task <List <string> > GetTexturePartList(IItemModel itemModel, XivRace xivRace, XivDataFile dataFile, string type = "Primary") { var itemType = ItemType.GetItemType(itemModel); var version = "0001"; var id = itemModel.ModelInfo.ModelID.ToString().PadLeft(4, '0'); var bodyVer = itemModel.ModelInfo.Body.ToString().PadLeft(4, '0'); var itemCategory = itemModel.ItemCategory; if (type.Equals("Secondary")) { var xivGear = itemModel as XivGear; id = xivGear.SecondaryModelInfo.ModelID.ToString().PadLeft(4, '0'); bodyVer = xivGear.SecondaryModelInfo.Body.ToString().PadLeft(4, '0'); var imc = new Imc(_gameDirectory, xivGear.DataFile); version = (await imc.GetImcInfo(itemModel, xivGear.SecondaryModelInfo)).Version.ToString().PadLeft(4, '0'); if (imc.ChangedType) { itemType = XivItemType.equipment; xivRace = XivRace.Hyur_Midlander_Male; itemCategory = XivStrings.Hands; } } else { if (itemType != XivItemType.human && itemType != XivItemType.furniture) { // Get the mtrl version for the given item from the imc file var imc = new Imc(_gameDirectory, dataFile); version = (await imc.GetImcInfo(itemModel, itemModel.ModelInfo)).Version.ToString().PadLeft(4, '0'); } } var parts = new[] { 'a', 'b', 'c', 'd', 'e', 'f' }; var race = xivRace.GetRaceCode(); string mtrlFolder = "", mtrlFile = ""; switch (itemType) { case XivItemType.equipment: mtrlFolder = $"chara/{itemType}/e{id}/material/v{version}"; mtrlFile = $"mt_c{race}e{id}_{SlotAbbreviationDictionary[itemCategory]}_"; break; case XivItemType.accessory: mtrlFolder = $"chara/{itemType}/a{id}/material/v{version}"; mtrlFile = $"mt_c{race}a{id}_{SlotAbbreviationDictionary[itemCategory]}_"; break; case XivItemType.weapon: mtrlFolder = $"chara/{itemType}/w{id}/obj/body/b{bodyVer}/material/v{version}"; mtrlFile = $"mt_w{id}b{bodyVer}_"; break; case XivItemType.monster: mtrlFolder = $"chara/{itemType}/m{id}/obj/body/b{bodyVer}/material/v{version}"; mtrlFile = $"mt_m{id}b{bodyVer}_"; break; case XivItemType.demihuman: mtrlFolder = $"chara/{itemType}/d{id}/obj/body/e{bodyVer}/material/v{version}"; mtrlFile = $"mt_d{id}e{bodyVer}_"; break; case XivItemType.human: if (itemCategory.Equals(XivStrings.Body)) { mtrlFolder = $"chara/{itemType}/c{id}/obj/body/b{bodyVer}/material/v{version}"; mtrlFile = $"mt_c{id}b{bodyVer}_"; } else if (itemCategory.Equals(XivStrings.Hair)) { mtrlFolder = $"chara/{itemType}/c{id}/obj/body/h{bodyVer}/material/v{version}"; mtrlFile = $"mt_c{id}h{bodyVer}_{SlotAbbreviationDictionary[itemCategory]}_"; } else if (itemCategory.Equals(XivStrings.Face)) { mtrlFolder = $"chara/{itemType}/c{id}/obj/body/f{bodyVer}/material/v{version}"; mtrlFile = $"mt_c{id}f{bodyVer}_{SlotAbbreviationDictionary[itemCategory]}_"; } else if (itemCategory.Equals(XivStrings.Tail)) { mtrlFolder = $"chara/{itemType}/c{id}/obj/body/t{bodyVer}/material/v{version}"; mtrlFile = $"mt_c{id}t{bodyVer}_"; } break; case XivItemType.furniture: if (itemCategory.Equals(XivStrings.Furniture_Indoor)) { mtrlFolder = $"bgcommon/hou/indoor/general/{id}/material"; mtrlFile = $"fun_b0_m{id}_0"; } else if (itemCategory.Equals(XivStrings.Furniture_Outdoor)) { mtrlFolder = $"bgcommon/hou/outdoor/general/{id}/material"; mtrlFile = $"gar_b0_m{id}_0"; } break; default: mtrlFolder = ""; break; } // Get a list of hashed mtrl files that are in the given folder var files = await _index.GetAllHashedFilesInFolder(HashGenerator.GetHash(mtrlFolder), dataFile); // append the part char to the mtrl file and see if its hashed value is within the files list var partList = (from part in parts let mtrlCheck = mtrlFile + part + ".mtrl" where files.Contains(HashGenerator.GetHash(mtrlCheck)) select part.ToString()).ToList(); if (partList.Count < 1 && itemType == XivItemType.furniture) { if (itemCategory.Equals(XivStrings.Furniture_Indoor)) { mtrlFile = $"fun_b0_m{id}_1"; } else if (itemCategory.Equals(XivStrings.Furniture_Outdoor)) { mtrlFile = $"gar_b0_m{id}_1"; } // Get a list of hashed mtrl files that are in the given folder files = await _index.GetAllHashedFilesInFolder(HashGenerator.GetHash(mtrlFolder), dataFile); // append the part char to the mtrl file and see if its hashed value is within the files list partList = (from part in parts let mtrlCheck = mtrlFile + part + ".mtrl" where files.Contains(HashGenerator.GetHash(mtrlCheck)) select part.ToString()).ToList(); } // returns the list of parts that exist within the mtrl folder return(partList); }
/// <summary> /// Gets the list of available mtrl parts for a given item /// </summary> /// <param name="itemModel">An item that contains model data</param> /// <param name="xivRace">The race for the requested data</param> /// <returns>A list of part characters</returns> public List <string> GetTexturePartList(IItemModel itemModel, XivRace xivRace, XivDataFile dataFile) { // Get the mtrl version for the given item from the imc file var imc = new Imc(_gameDirectory, dataFile); var version = imc.GetImcInfo(itemModel, itemModel.PrimaryModelInfo).Version.ToString().PadLeft(4, '0'); var id = itemModel.PrimaryModelInfo.ModelID.ToString().PadLeft(4, '0'); var bodyVer = itemModel.PrimaryModelInfo.Body.ToString().PadLeft(4, '0'); var parts = new[] { 'a', 'b', 'c', 'd', 'e', 'f' }; var race = xivRace.GetRaceCode(); var index = new Index(_gameDirectory); var itemType = ItemType.GetItemType(itemModel); string mtrlFolder = "", mtrlFile = ""; switch (itemType) { case XivItemType.equipment: mtrlFolder = $"chara/{itemType}/e{id}/material/v{version}"; mtrlFile = $"mt_c{race}e{id}_{SlotAbbreviationDictionary[itemModel.ItemCategory]}_"; break; case XivItemType.accessory: mtrlFolder = $"chara/{itemType}/a{id}/material/v{version}"; mtrlFile = $"mt_c{race}a{id}_{SlotAbbreviationDictionary[itemModel.ItemCategory]}_"; break; case XivItemType.weapon: mtrlFolder = $"chara/{itemType}/w{id}/obj/body/b{bodyVer}/material/v{version}"; mtrlFile = $"mt_w{id}b{bodyVer}_"; break; case XivItemType.monster: mtrlFolder = $"chara/{itemType}/m{id}/obj/body/b{bodyVer}/material/v{version}"; mtrlFile = $"mt_m{id}b{bodyVer}_"; break; case XivItemType.demihuman: mtrlFolder = $"chara/{itemType}/d{id}/obj/body/e{bodyVer}/material/v{version}"; mtrlFile = $"mt_d{id}e{bodyVer}_"; break; case XivItemType.human: if (itemModel.ItemCategory.Equals(XivStrings.Body)) { mtrlFolder = $"chara/{itemType}/c{id}/obj/body/b{bodyVer}/material/v{version}"; mtrlFile = $"mt_c{id}b{bodyVer}_"; } else if (itemModel.ItemCategory.Equals(XivStrings.Hair)) { mtrlFolder = $"chara/{itemType}/c{id}/obj/body/h{bodyVer}/material/v{version}"; mtrlFile = $"mt_c{id}h{bodyVer}_{SlotAbbreviationDictionary[itemModel.ItemCategory]}_"; } else if (itemModel.ItemCategory.Equals(XivStrings.Face)) { mtrlFolder = $"chara/{itemType}/c{id}/obj/body/f{bodyVer}/material/v{version}"; mtrlFile = $"mt_c{id}f{bodyVer}_{SlotAbbreviationDictionary[itemModel.ItemCategory]}_"; } else if (itemModel.ItemCategory.Equals(XivStrings.Tail)) { mtrlFolder = $"chara/{itemType}/c{id}/obj/body/t{bodyVer}/material/v{version}"; mtrlFile = $"mt_c{id}t{bodyVer}_"; } break; default: mtrlFolder = ""; break; } // Get a list of hashed mtrl files that are in the given folder var files = index.GetAllHashedFilesInFolder(HashGenerator.GetHash(mtrlFolder), dataFile); // append the part char to the mtrl file and see if its hashed value is within the files list // returns the list of parts that exist within the mtrl folder return((from part in parts let mtrlCheck = mtrlFile + part + ".mtrl" where files.Contains(HashGenerator.GetHash(mtrlCheck)) select part.ToString()).ToList()); }
/// <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); }
/// <summary> /// Gets the mtrl folder for a given item /// </summary> /// <param name="itemModel">Item that contains model data</param> /// <param name="xivRace">The race for the requested data</param> /// <param name="itemType">The type of the item</param> /// <returns>The mtrl Folder</returns> private string GetMtrlFolder(IItemModel itemModel, XivRace xivRace, XivItemType itemType) { // The default version number var version = "0001"; if (itemType != XivItemType.human && itemType != XivItemType.furniture) { // get the items version from the imc file var imc = new Imc(_gameDirectory, _dataFile); var imcInfo = imc.GetImcInfo(itemModel, itemModel.ModelInfo); version = imcInfo.Version.ToString().PadLeft(4, '0'); } if (version.Equals("0000")) { version = "0001"; } var id = itemModel.ModelInfo.ModelID.ToString().PadLeft(4, '0'); var bodyVer = itemModel.ModelInfo.Body.ToString().PadLeft(4, '0'); var race = xivRace.GetRaceCode(); var mtrlFolder = ""; switch (itemType) { case XivItemType.equipment: mtrlFolder = $"chara/{itemType}/e{id}/material/v{version}"; break; case XivItemType.accessory: mtrlFolder = $"chara/{itemType}/a{id}/material/v{version}"; break; case XivItemType.weapon: mtrlFolder = $"chara/{itemType}/w{id}/obj/body/b{bodyVer}/material/v{version}"; break; case XivItemType.monster: mtrlFolder = $"chara/{itemType}/m{id}/obj/body/b{bodyVer}/material/v{version}"; break; case XivItemType.demihuman: mtrlFolder = $"chara/{itemType}/d{id}/obj/equipment/e{bodyVer}/material/v{version}"; break; case XivItemType.human: if (itemModel.ItemCategory.Equals(XivStrings.Body)) { mtrlFolder = $"chara/{itemType}/c{race}/obj/body/b{bodyVer}/material"; } else if (itemModel.ItemCategory.Equals(XivStrings.Hair)) { // Hair has a version number, but no IMC, so we leave it at the default 0001 mtrlFolder = $"chara/{itemType}/c{race}/obj/hair/h{bodyVer}/material/v{version}"; } else if (itemModel.ItemCategory.Equals(XivStrings.Face)) { mtrlFolder = $"chara/{itemType}/c{race}/obj/face/f{bodyVer}/material"; } else if (itemModel.ItemCategory.Equals(XivStrings.Tail)) { mtrlFolder = $"chara/{itemType}/c{race}/obj/tail/t{bodyVer}/material"; } break; case XivItemType.furniture: if (itemModel.ItemCategory.Equals(XivStrings.Furniture_Indoor)) { mtrlFolder = $"bgcommon/hou/indoor/general/{id}/material"; } else if (itemModel.ItemCategory.Equals(XivStrings.Furniture_Outdoor)) { mtrlFolder = $"bgcommon/hou/outdoor/general/{id}/material"; } break; default: mtrlFolder = ""; break; } return(mtrlFolder); }