/// <summary> /// Get the gear entries list, optionally with a substring filter. /// </summary> /// <param name="substring"></param> /// <returns></returns> public async Task <List <XivGear> > GetCachedGearList(string substring = null) { WhereClause where = null; if (substring != null) { where = new WhereClause(); where.Comparer = WhereClause.ComparisonType.Like; where.Column = "name"; where.Value = "%" + substring + "%"; } List <XivGear> mainHands = new List <XivGear>(); List <XivGear> offHands = new List <XivGear>(); var list = await BuildListFromTable("items", where, async (reader) => { var primaryMi = new XivGearModelInfo(); var item = new XivGear { ExdID = reader.GetInt32("exd_id"), PrimaryCategory = XivStrings.Gear, SecondaryCategory = reader.GetString("slot_full"), ModelInfo = primaryMi, }; item.Name = reader.GetString("name"); item.IconNumber = (uint)reader.GetInt32("icon_id"); primaryMi.IsWeapon = reader.GetBoolean("is_weapon"); primaryMi.PrimaryID = reader.GetInt32("primary_id"); primaryMi.SecondaryID = reader.GetInt32("secondary_id"); primaryMi.ImcSubsetID = reader.GetInt32("imc_variant"); if (item.Name.Contains(XivStrings.Main_Hand)) { mainHands.Add(item); } else if (item.Name.Contains(XivStrings.Off_Hand)) { offHands.Add(item); } return(item); }); // Assign pairs based on items that came out of the same EXD row. foreach (var item in mainHands) { var pair = offHands.FirstOrDefault(x => x.ExdID == item.ExdID); if (pair != null) { pair.PairedItem = item; item.PairedItem = pair; } } return(list); }
/// <summary> /// A getter for available gear in the Item exd files /// </summary> /// <returns>A list containing XivGear data</returns> public async Task <List <XivGear> > GetUnCachedGearList() { // These are the offsets to relevant data // These will need to be changed if data gets added or removed with a patch const int modelDataCheckOffset = 30; int dataLength = 160; const int nameDataOffset = 14; const int modelDataOffset = 24; const int iconDataOffset = 136; int slotDataOffset = 154; if (_xivLanguage == XivLanguage.Chinese) { dataLength = 168; slotDataOffset = 156; } if (_xivLanguage == XivLanguage.Korean) { dataLength = 160; slotDataOffset = 154; } var xivGearList = new List <XivGear>(); xivGearList.AddRange(GetMissingGear()); var ex = new Ex(_gameDirectory, _xivLanguage); var itemDictionary = await ex.ReadExData(XivEx.item); // Loops through all the items in the item exd files // Item files start at 0 and increment by 500 for each new file // Item_0, Item_500, Item_1000, etc. await Task.Run(() => Parallel.ForEach(itemDictionary, (item) => { try { // This checks whether there is any model data present in the current item if (item.Value[modelDataCheckOffset] <= 0 && item.Value[modelDataCheckOffset + 1] <= 0) { return; } var primaryMi = new XivGearModelInfo(); var secondaryMi = new XivGearModelInfo(); var hasSecondary = false; var xivGear = new XivGear { ExdID = item.Key, PrimaryCategory = XivStrings.Gear, ModelInfo = primaryMi, }; /* Used to determine if the given model is a weapon * This is important because the data is formatted differently * The model data is a 16 byte section separated into two 8 byte parts (primary model, secondary model) * Format is 8 bytes in length with 2 bytes per data point [short, short, short, short] * Gear: primary model [blank, blank, variant, ID] nothing in secondary model * Weapon: primary model [blank, variant, body, ID] secondary model [blank, variant, body, ID] */ var isWeapon = false; // Big Endian Byte Order using (var br = new BinaryReaderBE(new MemoryStream(item.Value))) { br.BaseStream.Seek(nameDataOffset, SeekOrigin.Begin); var nameOffset = br.ReadInt16(); // Model Data br.BaseStream.Seek(modelDataOffset, SeekOrigin.Begin); // Primary Model Key primaryMi.ModelKey = Quad.Read(br.ReadBytes(8), 0); br.BaseStream.Seek(-8, SeekOrigin.Current); // Primary Blank var unused = br.ReadInt16(); // Primary Variant for weapon, blank otherwise var weaponVariant = br.ReadInt16(); if (weaponVariant != 0) { primaryMi.ImcSubsetID = weaponVariant; primaryMi.IsWeapon = true; isWeapon = true; } // Primary Body if weapon, Variant otherwise if (isWeapon) { primaryMi.SecondaryID = br.ReadInt16(); } else { primaryMi.ImcSubsetID = br.ReadInt16(); } // Primary Model ID primaryMi.PrimaryID = br.ReadInt16(); // Secondary Model Key isWeapon = false; secondaryMi.ModelKey = Quad.Read(br.ReadBytes(8), 0); br.BaseStream.Seek(-8, SeekOrigin.Current); // Secondary Blank var unused2 = br.ReadInt16(); // Secondary Variant for weapon, blank otherwise weaponVariant = br.ReadInt16(); if (weaponVariant != 0) { secondaryMi.ImcSubsetID = weaponVariant; secondaryMi.IsWeapon = true; isWeapon = true; } // Secondary Body if weapon, Variant otherwise if (isWeapon) { secondaryMi.SecondaryID = br.ReadInt16(); } else { secondaryMi.ImcSubsetID = br.ReadInt16(); } // Secondary Model ID secondaryMi.PrimaryID = br.ReadInt16(); // Icon br.BaseStream.Seek(iconDataOffset, SeekOrigin.Begin); xivGear.IconNumber = br.ReadUInt16(); // Gear Slot/Category br.BaseStream.Seek(slotDataOffset, SeekOrigin.Begin); int slotNum = br.ReadByte(); // Waist items do not have texture or model data if (slotNum == 6) { return; } xivGear.EquipSlotCategory = slotNum; xivGear.SecondaryCategory = _slotNameDictionary.ContainsKey(slotNum) ? _slotNameDictionary[slotNum] : "Unknown"; // Gear Name var gearNameOffset = dataLength + nameOffset; var gearNameLength = item.Value.Length - gearNameOffset; br.BaseStream.Seek(gearNameOffset, SeekOrigin.Begin); var nameString = Encoding.UTF8.GetString(br.ReadBytes(gearNameLength)).Replace("\0", ""); xivGear.Name = new string(nameString.Where(c => !char.IsControl(c)).ToArray()); xivGear.Name = xivGear.Name.Trim(); // If we have a secondary model XivGear secondaryItem = null; if (secondaryMi.PrimaryID != 0) { // Make a new item for it. secondaryItem = (XivGear)xivGear.Clone(); secondaryItem.ModelInfo = secondaryMi; xivGear.Name += " - " + XivStrings.Main_Hand; secondaryItem.Name += " - " + XivStrings.Off_Hand; xivGear.PairedItem = secondaryItem; secondaryItem.PairedItem = xivGear; xivGear.SecondaryCategory = XivStrings.Dual_Wield; secondaryItem.SecondaryCategory = XivStrings.Dual_Wield; } lock (_gearLock) { xivGearList.Add(xivGear); if (secondaryItem != null) { xivGearList.Add(secondaryItem); } } } } catch (Exception ex) { throw; } })); xivGearList.Sort(); return(xivGearList); }