static public Dictionary <uint, FFXI_Item> ReadItemListFromDatFile(uint DatFileId) { Dictionary <uint, FFXI_Item> res = new Dictionary <uint, FFXI_Item>(); if (!FFXI_FTable.TryGetValue(DatFileId, out var FileDatInfo)) { return(res); } if (!File.Exists(FileDatInfo.FullFileName)) { return(res); } var FS = new FileStream(FileDatInfo.FullFileName, FileMode.Open, FileAccess.Read); // needs to be in 3072 byte block size, and a minimum of 16 entries if (((FS.Length % 0x0C00) != 0) || (FS.Length < 0xC000)) { return(res); } var BR = new BinaryReader(FS); FFXI_DeduceItemType(BR, out var ExpectedItemType); var itemCount = BR.BaseStream.Length / 0x0C00; for (var i = 0; i < itemCount; i++) { BR.BaseStream.Seek(i * 0x0C00, SeekOrigin.Begin); byte[] ItemBytes = BR.ReadBytes(0xC00); FFXIEncryption.Rotate(ItemBytes, 5); var MemBR = new BinaryReader(new MemoryStream(ItemBytes, false)); FFXI_Item item = new FFXI_Item(); // Common Fields (14 bytes) item.Id = MemBR.ReadUInt32(); item.Flags = (FFXI_ItemFlags)MemBR.ReadUInt16(); item.StackSize = MemBR.ReadUInt16(); // 0xe0ff for Currency, which kinda suggests this is really 2 separate bytes item.Type = (FFXI_ItemType)MemBR.ReadUInt16(); MemBR.ReadUInt16(); // item.ResourceID_ = MemBR.ReadUInt16(); MemBR.ReadUInt16(); // item.ValidTargets_ = (ValidTarget)MemBR.ReadUInt16(); // Extra Fields (22/30/10/6/2 bytes for Armor/Weapon/Puppet/Item/UsableItem) if (item.Type == FFXI_ItemType.Nothing) { continue; } switch (ExpectedItemType) { case FFXI_ItemDatFileTypes.Armor: case FFXI_ItemDatFileTypes.Weapon: MemBR.ReadUInt16(); // item.Level_ = MemBR.ReadUInt16(); MemBR.ReadUInt16(); // item.Slots_ = (EquipmentSlot)MemBR.ReadUInt16(); MemBR.ReadUInt16(); // item.Races_ = (Race)MemBR.ReadUInt16(); MemBR.ReadUInt32(); // item.Jobs_ = (Job)MemBR.ReadUInt32(); MemBR.ReadUInt16(); //item.SuperiorLevel_ = MemBR.ReadUInt16(); if (ExpectedItemType == FFXI_ItemDatFileTypes.Armor) { MemBR.ReadUInt16(); // item.ShieldSize_ = MemBR.ReadUInt16(); } else { // Weapon MemBR.ReadUInt16(); // item.Unknown4_ = MemBR.ReadUInt16(); MemBR.ReadUInt16(); // item.Damage_ = MemBR.ReadUInt16(); MemBR.ReadInt16(); // item.Delay_ = MemBR.ReadInt16(); MemBR.ReadUInt16(); // item.DPS_ = MemBR.ReadUInt16(); MemBR.ReadByte(); // item.Skill_ = (Skill)MemBR.ReadByte(); MemBR.ReadByte(); // item.JugSize_ = MemBR.ReadByte(); MemBR.ReadUInt32(); // item.Unknown1_ = MemBR.ReadUInt32(); } MemBR.ReadByte(); // item.MaxCharges_ = MemBR.ReadByte(); MemBR.ReadByte(); // item.CastingTime_ = MemBR.ReadByte(); MemBR.ReadUInt16(); // item.UseDelay_ = MemBR.ReadUInt16(); MemBR.ReadUInt32(); // item.ReuseDelay_ = MemBR.ReadUInt32(); MemBR.ReadUInt16(); // item.Unknown2_ = MemBR.ReadUInt16(); MemBR.ReadUInt16(); // item.iLevel_ = MemBR.ReadUInt16(); MemBR.ReadUInt32(); // item.Unknown3_ = MemBR.ReadUInt32(); break; case FFXI_ItemDatFileTypes.PuppetItem: MemBR.ReadUInt16(); // item.PuppetSlot_ = (PuppetSlot)MemBR.ReadUInt16(); MemBR.ReadUInt32(); // item.ElementCharge_ = MemBR.ReadUInt32(); MemBR.ReadUInt32(); // item.Unknown3_ = MemBR.ReadUInt32(); break; case FFXI_ItemDatFileTypes.Instinct: MemBR.ReadUInt32(); MemBR.ReadUInt32(); MemBR.ReadUInt16(); MemBR.ReadUInt16(); // item.InstinctCost_ = MemBR.ReadUInt16(); MemBR.ReadUInt16(); MemBR.ReadUInt32(); MemBR.ReadUInt32(); MemBR.ReadUInt32(); break; case FFXI_ItemDatFileTypes.Item: switch (item.Type) { case FFXI_ItemType.Flowerpot: case FFXI_ItemType.Furnishing: case FFXI_ItemType.Mannequin: MemBR.ReadUInt16(); // item.Element_ = (Element)MemBR.ReadUInt16(); MemBR.ReadInt32(); // item.StorageSlots_ = MemBR.ReadInt32(); MemBR.ReadUInt32(); // item.Unknown3_ = MemBR.ReadUInt32(); break; default: MemBR.ReadUInt16(); // item.Unknown2_ = MemBR.ReadUInt16(); MemBR.ReadUInt32(); // item.Unknown3_ = MemBR.ReadUInt32(); MemBR.ReadUInt32(); // item.Unknown3_ = MemBR.ReadUInt32(); break; } break; case FFXI_ItemDatFileTypes.UsableItem: MemBR.ReadUInt16(); // item.ActivationTime_ = MemBR.ReadUInt16(); MemBR.ReadUInt32(); // item.Unknown1_ = MemBR.ReadUInt32(); MemBR.ReadUInt32(); // item.Unknown3_ = MemBR.ReadUInt32(); MemBR.ReadUInt32(); // item.Unknown4_ = MemBR.ReadUInt32(); break; case FFXI_ItemDatFileTypes.Currency: MemBR.ReadUInt16(); // item.Unknown2_ = MemBR.ReadUInt16(); break; case FFXI_ItemDatFileTypes.ItemSlip: MemBR.ReadUInt16(); // item.Unknown1_ = MemBR.ReadUInt16(); for (int counter = 0; counter < 17; counter++) { MemBR.ReadUInt32(); } break; case FFXI_ItemDatFileTypes.Monipulator: MemBR.ReadUInt16(); // item.Unknown1_ = MemBR.ReadUInt16(); for (int counter = 0; counter < 24; counter++) { MemBR.ReadInt32(); } break; default: // If this is a unknown expected file type, ignore and exit return(res); } if (item.Type > FFXI_ItemType.Max) { // Invalid item type ? continue; } // Next Up: Strings (variable size) long StringBase = MemBR.BaseStream.Position; uint StringCount = MemBR.ReadUInt32(); if (StringCount > 9) { // Sanity check, for safety - 0 strings is fine for now // item.Clear(); continue; } FFXIEncoding E = new FFXIEncoding(); string[] Strings = new string[StringCount]; for (byte iStrings = 0; iStrings < StringCount; iStrings++) { long Offset = StringBase + MemBR.ReadUInt32(); uint Flag = MemBR.ReadUInt32(); if (Offset < 0 || Offset + 0x20 > 0x280 || (Flag != 0 && Flag != 1)) { // item.Clear(); StringCount = 0; break; } // Flag seems to be 1 if the offset is not actually an offset. Could just be padding to make StringCount unique per language, or it could be an indication // of the pronoun to use (a/an/the/...). The latter makes sense because of the increased number of such flags for french and german. if (Flag == 0) { MemBR.BaseStream.Position = Offset; Strings[iStrings] = FFXI_ReadString(MemBR, E); if (Strings[iStrings] == null) { // item.Clear(); StringCount = 0; break; } MemBR.BaseStream.Position = StringBase + 4 + 8 * (iStrings + 1); } } // Assign the strings to the proper fields switch (StringCount) { case 0: // Temporary hack until I can figure out what the hell is wrong with reading the strings if ((Strings.Length > 1) && (Strings[0] != null)) { item.Name = Strings[0]; } else { item.Name = "<no name found>"; } // item.Name = item.Type.ToString() + " - " + item.Id.ToString(); break; case 1: item.Name = Strings[0]; break; case 2: // Japanese item.Name = Strings[0]; item.Description = Strings[1]; break; case 5: // English item.Name = Strings[0]; // unused: Strings[1] item.NameSingle = Strings[2]; item.NameMultiple = Strings[3]; item.Description = Strings[4]; break; case 6: // French item.Name = Strings[0]; // unused: Strings[1] // unused: Strings[2] item.NameSingle = Strings[3]; item.NameMultiple = Strings[4]; item.Description = Strings[5]; break; case 9: // German item.Name = Strings[0]; // unused: Strings[1] // unused: Strings[2] // unused: Strings[3] item.NameSingle = Strings[4]; // unused: Strings[5] // unused: Strings[6] item.NameMultiple = Strings[7]; item.Description = Strings[8]; break; } MemBR.Close(); if ((item.Name != null) && (item.Name != string.Empty) && (item.Name != ".")) { res.Add(item.Id, item); } } return(res); }
static public List <FFXI_Item> ReadItemListFromXML(string ItemXmlFile) { List <FFXI_Item> res = new List <FFXI_Item>(); XmlDocument D = new XmlDocument(); D.Load(ItemXmlFile); foreach (XmlNode itemNode in D.DocumentElement.ChildNodes) { if (itemNode.Name == "thing") { var newItem = new FFXI_Item(); foreach (XmlNode itemElement in itemNode.ChildNodes) { if (itemElement.Name == "field") { var fieldName = itemElement.Attributes["name"]?.Value.ToLower(); var fieldValueText = itemElement?.InnerText; uint.TryParse(fieldValueText, out uint fieldValueUInt); uint.TryParse(fieldValueText, System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint fieldValueUIntHex); switch (fieldName) { case "id": newItem.Id = fieldValueUInt; break; case "flags": newItem.Flags = (FFXI_ItemFlags)fieldValueUIntHex; break; case "stack-size": newItem.StackSize = fieldValueUInt; break; case "type": newItem.Type = (FFXI_ItemType)fieldValueUIntHex; break; case "name": newItem.Name = fieldValueText; break; case "description": newItem.Description = fieldValueText; break; case "log-name-singular": newItem.NameSingle = fieldValueText; break; case "log-name-plural": newItem.NameMultiple = fieldValueText; break; } } } // Only add a item if it has some usefull info in it if ((newItem.Id > 0) && (newItem.Name != null) && (newItem.Name != string.Empty) && (newItem.Name != ".")) { res.Add(newItem); } } } return(res); }