/// <summary> /// Gets the atex paths for a given item /// </summary> /// <param name="itemModel">The item to get the atex paths for</param> /// <returns>A list of TexTypePath containing the atex info</returns> public List <TexTypePath> GetAtexPaths(IItemModel itemModel) { var atexTexTypePathList = new List <TexTypePath>(); var index = new Index(_gameDirectory); var avfx = new Avfx(_gameDirectory, _dataFile); var itemType = ItemType.GetItemType(itemModel); var vfxPath = GetVfxPath(itemModel, itemType); var vfxOffset = index.GetDataOffset(HashGenerator.GetHash(vfxPath.Folder), HashGenerator.GetHash(vfxPath.File), _dataFile); if (vfxOffset == 0) { throw new Exception($"Could not find offset for vfx path {vfxPath.Folder}/{vfxPath.File}"); } var aTexPaths = avfx.GetATexPaths(vfxOffset); foreach (var atexPath in aTexPaths) { var ttp = new TexTypePath { DataFile = _dataFile, Name = Path.GetFileNameWithoutExtension(atexPath), Path = atexPath }; atexTexTypePathList.Add(ttp); } return(atexTexTypePathList); }
/// <summary> /// Gets the MTRL data for the given item /// </summary> /// <remarks> /// It requires a race (The default is usually <see cref="XivRace.Hyur_Midlander_Male"/>) /// It also requires an mtrl part <see cref="GearInfo.GetPartList(IItemModel, XivRace)"/> (default is 'a') /// </remarks> /// <param name="itemModel">Item that contains model data</param> /// <param name="race">The race for the requested data</param> /// <param name="mtrlFile">The Mtrl file</param> /// <returns>XivMtrl containing all the mtrl data</returns> public XivMtrl GetMtrlData(IItemModel itemModel, XivRace race, string mtrlFile, int dxVersion) { var index = new Index(_gameDirectory); var itemType = ItemType.GetItemType(itemModel); // Get mtrl path var mtrlFolder = GetMtrlFolder(itemModel, race, itemType); var mtrlStringPath = $"{mtrlFolder}/{mtrlFile}"; if (itemType == XivItemType.furniture) { mtrlStringPath = $"b{mtrlFile}"; mtrlFolder = Path.GetDirectoryName(mtrlStringPath).Replace("\\", "/"); mtrlFile = Path.GetFileName(mtrlStringPath); } // Get mtrl offset var mtrlOffset = index.GetDataOffset(HashGenerator.GetHash(mtrlFolder), HashGenerator.GetHash(mtrlFile), _dataFile); if (mtrlOffset == 0) { throw new Exception($"Could not find offest for {mtrlStringPath}"); } var mtrlData = GetMtrlData(mtrlOffset, mtrlStringPath, dxVersion); return(mtrlData); }
/// <summary> /// Gets the relevant IMC information for a given item /// </summary> /// <param name="item">The item to get the version for</param> /// <param name="modelInfo">The model info of the item</param> /// <returns>The XivImc Data</returns> public XivImc GetImcInfo(IItemModel item, XivModelInfo modelInfo) { var xivImc = new XivImc(); // These are the offsets to relevant data // These will need to be changed if data gets added or removed with a patch const int headerLength = 4; const int variantLength = 6; const int variantSetLength = 30; var index = new Index(_gameDirectory); var dat = new Dat(_gameDirectory); var itemType = ItemType.GetItemType(item); var imcPath = GetImcPath(modelInfo, itemType); var imcOffset = index.GetDataOffset(HashGenerator.GetHash(imcPath.Folder), HashGenerator.GetHash(imcPath.File), _dataFile); if (imcOffset == 0) { throw new Exception($"Could not find offest for {imcPath.Folder}/{imcPath.File}"); } var imcData = dat.GetType2Data(imcOffset, _dataFile); using (var br = new BinaryReader(new MemoryStream(imcData))) { int variantOffset; if (itemType == XivItemType.weapon || itemType == XivItemType.monster) { // weapons and monsters do not have variant sets variantOffset = (modelInfo.Variant * variantLength) + headerLength; } else { // Variant Sets contain 5 variants for each slot // These can be Head, Body, Hands, Legs, Feet or Ears, Neck, Wrists, LRing, RRing // This skips to the correct variant set, then to the correct slot within that set for the item variantOffset = (modelInfo.Variant * variantSetLength) + (_slotOffsetDictionary[item.ItemCategory] * variantLength) + headerLength; } br.BaseStream.Seek(variantOffset, SeekOrigin.Begin); xivImc.Version = br.ReadByte(); var unknown = br.ReadByte(); xivImc.Mask = br.ReadUInt16(); xivImc.Vfx = br.ReadUInt16(); } return(xivImc); }
/// <summary> /// Gets the MTRL data for the given item /// </summary> /// <remarks> /// It requires a race (The default is usually <see cref="XivRace.Hyur_Midlander_Male"/>) /// It also requires an mtrl part <see cref="GearInfo.GetPartList(IItemModel, XivRace)"/> (default is 'a') /// </remarks> /// <param name="itemModel">Item that contains model data</param> /// <param name="race">The race for the requested data</param> /// <param name="part">The Mtrl part </param> /// <returns>XivMtrl containing all the mtrl data</returns> public XivMtrl GetMtrlData(IItemModel itemModel, XivRace race, char part, int dxVersion) { var index = new Index(_gameDirectory); var itemType = ItemType.GetItemType(itemModel); // Get mtrl path var mtrlPath = GetMtrlPath(itemModel, race, part, itemType); var mtrlStringPath = $"{mtrlPath.Folder}/{mtrlPath.File}"; // Get mtrl offset var mtrlOffset = index.GetDataOffset(HashGenerator.GetHash(mtrlPath.Folder), HashGenerator.GetHash(mtrlPath.File), _dataFile); if (mtrlOffset == 0 && itemType == XivItemType.furniture) { mtrlPath.File = mtrlPath.File.Replace("_0", "_1"); mtrlStringPath = $"{mtrlPath.Folder}/{mtrlPath.File}"; // Get mtrl offset mtrlOffset = index.GetDataOffset(HashGenerator.GetHash(mtrlPath.Folder), HashGenerator.GetHash(mtrlPath.File), _dataFile); } if (mtrlOffset == 0) { throw new Exception($"Could not find offest for {mtrlStringPath}"); } var mtrlData = GetMtrlData(mtrlOffset, mtrlStringPath, dxVersion); if (mtrlPath.HasVfx) { var atex = new ATex(_gameDirectory, _dataFile); mtrlData.TextureTypePathList.AddRange(atex.GetAtexPaths(itemModel)); } return(mtrlData); }
/// <summary> /// Gets the full IMC information for a given item /// </summary> /// <param name="item"></param> /// <param name="modelInfo"></param> /// <returns>The ImcData data</returns> public async Task <ImcData> GetFullImcInfo(IItemModel item, XivModelInfo modelInfo) { var index = new Index(_gameDirectory); var dat = new Dat(_gameDirectory); var itemType = ItemType.GetItemType(item); var imcPath = GetImcPath(modelInfo, itemType); var imcOffset = await index.GetDataOffset(HashGenerator.GetHash(imcPath.Folder), HashGenerator.GetHash(imcPath.File), _dataFile); if (imcOffset == 0) { throw new Exception($"Could not find offset for {imcPath.Folder}/{imcPath.File}"); } var imcByteData = await dat.GetType2Data(imcOffset, _dataFile); return(await Task.Run(() => { using (var br = new BinaryReader(new MemoryStream(imcByteData))) { var imcData = new ImcData() { VariantCount = br.ReadInt16(), Unknown = br.ReadInt16(), GearVariantList = new List <VariantSet>() }; //weapons and monsters do not have variant sets if (itemType == XivItemType.weapon || itemType == XivItemType.monster) { imcData.OtherVariantList = new List <XivImc>(); imcData.DefaultVariant = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }; for (var i = 0; i < imcData.VariantCount; i++) { imcData.OtherVariantList.Add(new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }); } } else { imcData.GearVariantList = new List <VariantSet>(); imcData.DefaultVariantSet = new VariantSet { Slot1 = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }, Slot2 = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }, Slot3 = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }, Slot4 = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }, Slot5 = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }, }; for (var i = 0; i < imcData.VariantCount; i++) { // gets the data for each slot in the current variant set var imcGear = new VariantSet { Slot1 = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }, Slot2 = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }, Slot3 = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }, Slot4 = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }, Slot5 = new XivImc { Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16() }, }; imcData.GearVariantList.Add(imcGear); } } return imcData; } })); }
/// <summary> /// Gets the relevant IMC information for a given item /// </summary> /// <param name="item">The item to get the version for</param> /// <param name="modelInfo">The model info of the item</param> /// <returns>The XivImc Data</returns> public async Task <XivImc> GetImcInfo(IItemModel item, XivModelInfo modelInfo) { var xivImc = new XivImc(); // These are the offsets to relevant data // These will need to be changed if data gets added or removed with a patch const int headerLength = 4; const int variantLength = 6; const int variantSetLength = 30; var itemType = ItemType.GetItemType(item); var imcPath = GetImcPath(modelInfo, itemType); var itemCategory = item.ItemCategory; var imcOffset = await _modding.Index.GetDataOffset(HashGenerator.GetHash(imcPath.Folder), HashGenerator.GetHash(imcPath.File), _dataFile); if (imcOffset == 0) { if (item.ItemCategory == XivStrings.Two_Handed) { itemCategory = XivStrings.Hands; itemType = XivItemType.equipment; imcPath = GetImcPath(modelInfo, itemType); imcOffset = await _modding.Index.GetDataOffset(HashGenerator.GetHash(imcPath.Folder), HashGenerator.GetHash(imcPath.File), _dataFile); if (imcOffset == 0) { throw new Exception($"Could not find offset for {imcPath.Folder}/{imcPath.File}"); } ChangedType = true; } else { throw new Exception($"Could not find offset for {imcPath.Folder}/{imcPath.File}"); } } var imcData = await _modding.Dat.GetType2Data(imcOffset, _dataFile); await Task.Run(() => { using (var br = new BinaryReader(new MemoryStream(imcData))) { int variantOffset; if (itemType == XivItemType.weapon || itemType == XivItemType.monster) { // weapons and monsters do not have variant sets variantOffset = (modelInfo.Variant *variantLength) + headerLength; // use default if offset is out of range if (variantOffset >= imcData.Length) { variantOffset = headerLength; } } else { // Variant Sets contain 5 variants for each slot // These can be Head, Body, Hands, Legs, Feet or Ears, Neck, Wrists, LRing, RRing // This skips to the correct variant set, then to the correct slot within that set for the item variantOffset = (modelInfo.Variant *variantSetLength) + (_slotOffsetDictionary[itemCategory] * variantLength) + headerLength; // use defalut if offset is out of range if (variantOffset >= imcData.Length) { variantOffset = (_slotOffsetDictionary[itemCategory] * variantLength) + headerLength; } } br.BaseStream.Seek(variantOffset, SeekOrigin.Begin); // if(variantOffset) xivImc.Version = br.ReadByte(); var unknown = br.ReadByte(); xivImc.Mask = br.ReadUInt16(); xivImc.Vfx = br.ReadByte(); var unknown1 = br.ReadByte(); } }); return(xivImc); }
/// <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 available races that contain model data for the given gear /// </summary> /// <remarks> /// This checks to see if the mdl file for each race exists in the mdl folder /// It creates a list of the races which do have an available mdl file /// </remarks> /// <param name="xivGear">A gear item</param> /// <returns>A list of XivRace data</returns> public async Task <List <XivRace> > GetRacesForModels(XivGear xivGear, XivDataFile dataFile) { var itemType = ItemType.GetItemType(xivGear); var modelID = xivGear.ModelInfo.ModelID.ToString().PadLeft(4, '0'); var raceList = new List <XivRace>(); if (itemType == XivItemType.weapon) { return(new List <XivRace> { XivRace.All_Races }); } string mdlFolder; var id = xivGear.ModelInfo.ModelID.ToString().PadLeft(4, '0'); switch (itemType) { case XivItemType.equipment: mdlFolder = $"chara/{itemType}/e{id}/model"; break; case XivItemType.accessory: mdlFolder = $"chara/{itemType}/a{id}/model"; break; default: mdlFolder = ""; break; } var testFilesDictionary = new Dictionary <int, string>(); // loop through each race ID to create a dictionary containing [Hashed file name, race ID] foreach (var ID in IDRaceDictionary.Keys) { string mdlFile; switch (itemType) { case XivItemType.equipment: mdlFile = $"c{ID}e{modelID}_{SlotAbbreviationDictionary[xivGear.ItemCategory]}.mdl"; break; case XivItemType.accessory: mdlFile = $"c{ID}a{modelID}_{SlotAbbreviationDictionary[xivGear.ItemCategory]}.mdl"; break; default: mdlFile = ""; break; } testFilesDictionary.Add(HashGenerator.GetHash(mdlFile), ID); } // get the list of hashed file names from the mtrl folder var files = await _index.GetAllHashedFilesInFolder(HashGenerator.GetHash(mdlFolder), dataFile); // Loop through each entry in the dictionary foreach (var testFile in testFilesDictionary) { // if the file in the dictionary entry is contained in the list of files from the folder // add that race to the race list if (files.Contains(testFile.Key)) { raceList.Add(IDRaceDictionary[testFile.Value]); } } return(raceList); }
public static ItemType ConvertItemType(int i) { return(ItemType.GetItemType(i)); }
/// <summary> /// Gets the available races that contain texture data for the given gear /// </summary> /// <remarks> /// This checks to see if the mtrl file for each race exists in the mtrl folder /// It creates a list of the races which do have an available mtrl folder /// </remarks> /// <param name="xivGear">A gear item</param> /// <returns>A list of XivRace data</returns> public List <XivRace> GetRacesForTextures(XivGear xivGear, XivDataFile dataFile) { // Get the material version for the item from the imc file var imc = new Imc(_gameDirectory, dataFile); var gearVersion = imc.GetImcInfo(xivGear, xivGear.PrimaryModelInfo).Version.ToString().PadLeft(4, '0'); var modelID = xivGear.PrimaryModelInfo.ModelID.ToString().PadLeft(4, '0'); var raceList = new List <XivRace>(); var index = new Index(_gameDirectory); var itemType = ItemType.GetItemType(xivGear); string mtrlFolder; if (itemType == XivItemType.weapon) { return(new List <XivRace> { XivRace.All_Races }); } switch (itemType) { case XivItemType.equipment: mtrlFolder = $"chara/{itemType}/e{modelID}/material/v{gearVersion}"; break; case XivItemType.accessory: mtrlFolder = $"chara/{itemType}/a{modelID}/material/v{gearVersion}"; break; default: mtrlFolder = ""; break; } var testFilesDictionary = new Dictionary <int, string>(); // loop through each race ID to create a dictionary containing [Hashed file name, race ID] foreach (var ID in IDRaceDictionary.Keys) { string mtrlFile; switch (itemType) { case XivItemType.equipment: mtrlFile = $"mt_c{ID}e{modelID}_{SlotAbbreviationDictionary[xivGear.ItemCategory]}_a.mtrl"; break; case XivItemType.accessory: mtrlFile = $"mt_c{ID}a{modelID}_{SlotAbbreviationDictionary[xivGear.ItemCategory]}_a.mtrl"; break; default: mtrlFile = ""; break; } testFilesDictionary.Add(HashGenerator.GetHash(mtrlFile), ID); } // get the list of hashed file names from the mtrl folder var files = index.GetAllHashedFilesInFolder(HashGenerator.GetHash(mtrlFolder), dataFile); // Loop through each entry in the dictionary foreach (var testFile in testFilesDictionary) { // if the file in the dictionary entry is contained in the list of files from the folder // add that race to the race list if (files.Contains(testFile.Key)) { raceList.Add(IDRaceDictionary[testFile.Value]); } } return(raceList); }
/// <summary> /// Gets the MTRL data for the given item /// </summary> /// <remarks> /// It requires a race (The default is usually <see cref="XivRace.Hyur_Midlander_Male"/>) /// It also requires an mtrl part <see cref="GearInfo.GetPartList(IItemModel, XivRace)"/> (default is 'a') /// </remarks> /// <param name="itemModel">Item that contains model data</param> /// <param name="race">The race for the requested data</param> /// <param name="part">The Mtrl part </param> /// <returns></returns> public XivMtrl GetMtrlData(IItemModel itemModel, XivRace race, char part) { var index = new Index(_gameDirectory); var dat = new Dat(_gameDirectory); var itemType = ItemType.GetItemType(itemModel); // Get mtrl path var mtrlPath = GetMtrlPath(itemModel, race, part, itemType); // Get mtrl offset var mtrlOffset = index.GetDataOffset(HashGenerator.GetHash(mtrlPath.Folder), HashGenerator.GetHash(mtrlPath.File), _dataFile); if (mtrlOffset == 0) { throw new Exception($"Could not find offest for {mtrlPath.Folder}/{mtrlPath.File}"); } // Get uncompressed mtrl data var mtrlData = dat.GetType2Data(mtrlOffset, _dataFile); XivMtrl xivMtrl; using (var br = new BinaryReader(new MemoryStream(mtrlData))) { xivMtrl = new XivMtrl { Signature = br.ReadInt32(), FileSize = br.ReadInt16(), ColorSetDataSize = br.ReadInt16(), MaterialDataSize = br.ReadInt16(), TexturePathsDataSize = br.ReadByte(), Unknown = br.ReadByte(), TextureCount = br.ReadByte(), MapCount = br.ReadByte(), ColorSetCount = br.ReadByte(), Unknown1 = br.ReadByte(), TextureTypePathList = new List <TexTypePath>(), MTRLPath = $"{mtrlPath.Folder}/{mtrlPath.File}" }; var pathSizeList = new List <int>(); // get the texture path offsets xivMtrl.TexturePathOffsetList = new List <int>(xivMtrl.TextureCount); for (var i = 0; i < xivMtrl.TextureCount; i++) { xivMtrl.TexturePathOffsetList.Add(br.ReadInt16()); br.ReadBytes(2); // add the size of the paths if (i > 0) { pathSizeList.Add(xivMtrl.TexturePathOffsetList[i] - xivMtrl.TexturePathOffsetList[i - 1]); } } // get the map path offsets xivMtrl.MapPathOffsetList = new List <int>(xivMtrl.MapCount); for (var i = 0; i < xivMtrl.MapCount; i++) { xivMtrl.MapPathOffsetList.Add(br.ReadInt16()); br.ReadBytes(2); // add the size of the paths if (i > 0) { pathSizeList.Add(xivMtrl.MapPathOffsetList[i] - xivMtrl.MapPathOffsetList[i - 1]); } else { pathSizeList.Add(xivMtrl.MapPathOffsetList[i] - xivMtrl.TexturePathOffsetList[xivMtrl.TextureCount - 1]); } } // get the color set offsets xivMtrl.ColorSetPathOffsetList = new List <int>(xivMtrl.ColorSetCount); for (var i = 0; i < xivMtrl.ColorSetCount; i++) { xivMtrl.ColorSetPathOffsetList.Add(br.ReadInt16()); br.ReadBytes(2); // add the size of the paths if (i > 0) { pathSizeList.Add(xivMtrl.ColorSetPathOffsetList[i] - xivMtrl.ColorSetPathOffsetList[i - 1]); } else { pathSizeList.Add(xivMtrl.ColorSetPathOffsetList[i] - xivMtrl.MapPathOffsetList[xivMtrl.MapCount - 1]); } } pathSizeList.Add(xivMtrl.TexturePathsDataSize - xivMtrl.ColorSetPathOffsetList[xivMtrl.ColorSetCount - 1]); var count = 0; // get the texture path strings xivMtrl.TexturePathList = new List <string>(xivMtrl.TextureCount); for (var i = 0; i < xivMtrl.TextureCount; i++) { xivMtrl.TexturePathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", "")); count++; } // add the textures to the TextureTypePathList xivMtrl.TextureTypePathList.AddRange(GetTexNames(xivMtrl.TexturePathList, _dataFile)); // get the map path strings xivMtrl.MapPathList = new List <string>(xivMtrl.MapCount); for (var i = 0; i < xivMtrl.MapCount; i++) { xivMtrl.MapPathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", "")); count++; } // get the color set path strings xivMtrl.ColorSetPathList = new List <string>(xivMtrl.ColorSetCount); for (var i = 0; i < xivMtrl.ColorSetCount; i++) { xivMtrl.ColorSetPathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", "")); count++; } // If the mtrl file contains a color set, add it to the TextureTypePathList if (xivMtrl.ColorSetDataSize > 0) { var ttp = new TexTypePath { Path = mtrlPath.Folder + "/" + mtrlPath.File, Type = XivTexType.ColorSet, DataFile = _dataFile }; xivMtrl.TextureTypePathList.Add(ttp); } var shaderPathSize = xivMtrl.MaterialDataSize - xivMtrl.TexturePathsDataSize; xivMtrl.Shader = Encoding.UTF8.GetString(br.ReadBytes(shaderPathSize)).Replace("\0", ""); xivMtrl.Unknown2 = br.ReadInt32(); xivMtrl.ColorSetData = new List <Half>(); for (var i = 0; i < xivMtrl.ColorSetDataSize / 2; i++) { xivMtrl.ColorSetData.Add(new Half(br.ReadUInt16())); } xivMtrl.AdditionalDataSize = br.ReadInt16(); xivMtrl.DataStruct1Count = br.ReadInt16(); xivMtrl.DataStruct2Count = br.ReadInt16(); xivMtrl.ParameterStructCount = br.ReadInt16(); xivMtrl.ShaderNumber = br.ReadInt16(); xivMtrl.Unknown3 = br.ReadInt16(); xivMtrl.DataStruct1List = new List <DataStruct1>(xivMtrl.DataStruct1Count); for (var i = 0; i < xivMtrl.DataStruct1Count; i++) { xivMtrl.DataStruct1List.Add(new DataStruct1 { ID = br.ReadUInt32(), Unknown1 = br.ReadUInt32() }); } xivMtrl.DataStruct2List = new List <DataStruct2>(xivMtrl.DataStruct2Count); for (var i = 0; i < xivMtrl.DataStruct2Count; i++) { xivMtrl.DataStruct2List.Add(new DataStruct2 { ID = br.ReadUInt32(), Offset = br.ReadInt16(), Size = br.ReadInt16() }); } xivMtrl.ParameterStructList = new List <ParameterStruct>(xivMtrl.ParameterStructCount); for (var i = 0; i < xivMtrl.ParameterStructCount; i++) { xivMtrl.ParameterStructList.Add(new ParameterStruct { ID = br.ReadUInt32(), Unknown1 = br.ReadInt16(), Unknown2 = br.ReadInt16(), TextureIndex = br.ReadUInt32() }); } xivMtrl.AdditionalData = br.ReadBytes(xivMtrl.AdditionalDataSize); } return(xivMtrl); }