/// <summary> /// Logs the information embedded in the root node of the OTB tree. /// </summary> private static void ParseOTBTreeRootNode(OTBNode rootNode) { if (rootNode == null) { throw new ArgumentNullException(nameof(rootNode)); } if (rootNode.Children.Count != 1) { throw new InvalidOperationException(); } var parsingStream = new OTBParsingStream(rootNode.Data); var headerVersion = parsingStream.ReadUInt32(); //if (headerVersion == 0 || headerVersion > 2) // throw new InvalidOperationException(); var worldWidth = parsingStream.ReadUInt16(); var worldHeight = parsingStream.ReadUInt16(); var itemEncodingMajorVersion = parsingStream.ReadUInt32(); //if (itemEncodingMajorVersion != SupportedItemEncodingMajorVersion) // throw new InvalidOperationException(); var itemEncodingMinorVersion = parsingStream.ReadUInt32(); //if (itemEncodingMinorVersion < SupportedItemEncodingMinorVersion) // throw new InvalidOperationException(); _logger.Info($"OTBM header version: {headerVersion}."); _logger.Info($"World width: {worldWidth}."); _logger.Info($"World height: {worldHeight}."); _logger.Info($"Item encoding major version: {itemEncodingMajorVersion}."); _logger.Info($"Item encoding minor version: {itemEncodingMinorVersion}."); }
/// <summary> /// Updates the <paramref name="world"/> with the data contained /// in <paramref name="tileAreaNode"/>. /// </summary> private static void ParseTileAreaNode(OTBNode tileAreaNode, World world) { if (tileAreaNode == null) { throw new ArgumentNullException(nameof(tileAreaNode)); } if (tileAreaNode.Type != OTBNodeType.TileArea) { throw new InvalidOperationException(); } var stream = new OTBParsingStream(tileAreaNode.Data); var areaStartX = stream.ReadUInt16(); var areaStartY = stream.ReadUInt16(); var areaZ = (sbyte)stream.ReadByte(); var areaStartPosition = new Coordinate( x: areaStartX, y: areaStartY, z: areaZ); foreach (var tileNode in tileAreaNode.Children) { ParseTileNode( tilesAreaStartPosition: areaStartPosition, tileNode: tileNode, world: world); } }
private static UInt16 GetItemIdAndConvertPvpFieldsToPermanentFields(OTBParsingStream stream) { var originalItemId = stream.ReadUInt16(); var newItemId = originalItemId; switch (originalItemId) { case (UInt16)OTBMWorldItemId.FireFieldPvpLarge: newItemId = (UInt16)OTBMWorldItemId.FireFieldPersistentLarge; break; case (UInt16)OTBMWorldItemId.FireFieldPvpMedium: newItemId = (UInt16)OTBMWorldItemId.FireFieldPersistentMedium; break; case (UInt16)OTBMWorldItemId.FireFieldPvpSmall: newItemId = (UInt16)OTBMWorldItemId.FireFieldPersistentSmall; break; case (UInt16)OTBMWorldItemId.EnergyFieldPvp: newItemId = (UInt16)OTBMWorldItemId.EnergyFieldPersistent; break; case (UInt16)OTBMWorldItemId.PoisonFieldPvp: newItemId = (UInt16)OTBMWorldItemId.PoisonFieldPersistent; break; case (UInt16)OTBMWorldItemId.MagicWall: newItemId = (UInt16)OTBMWorldItemId.MagicWallPersistent; break; case (UInt16)OTBMWorldItemId.WildGrowth: newItemId = (UInt16)OTBMWorldItemId.WildGrowthPersistent; break; default: break; } if (newItemId != originalItemId) { _logger.Warn($"Converted {(OTBMWorldItemId)originalItemId} to {(OTBMWorldItemId)newItemId}."); } return(newItemId); }
public void ReadByte_WithoutEscapeBytes(byte[] buffer, byte[] expectedBytes) { var stream = new OTBParsingStream(buffer); var bytesRead = new List <byte>(); while (!stream.IsOver) { bytesRead.Add(stream.ReadByte()); } var actualBytes = bytesRead.ToArray(); Assert.AreEqual( expected: expectedBytes, actual: actualBytes); }
private static Item ParseItemData(OTBParsingStream stream) { var newItemId = GetItemIdAndConvertPvpFieldsToPermanentFields(stream); var item = ItemFactory.Create(newItemId); if (item == null) { _logger.Warn($"{nameof(ItemFactory)} was unable to create a item with id: {newItemId}."); return(null); } //if(!stream.IsOver && item.Count > 0) { // var itemAttribute = (OTBMWorldItemAttribute)stream.ReadByte(); // itemAttribute = (OTBMWorldItemAttribute)stream.ReadByte(); // itemAttribute = (OTBMWorldItemAttribute)stream.ReadByte(); // itemAttribute = (OTBMWorldItemAttribute)stream.ReadByte(); // while (!stream.IsOver && itemAttribute != OTBMWorldItemAttribute.None && itemAttribute != OTBMWorldItemAttribute.None2) { // switch (itemAttribute) { // case OTBMWorldItemAttribute.Count: // case OTBMWorldItemAttribute.RuneCharges: // var count = stream.ReadByte(); // item.SetAmount(count); // break; // case OTBMWorldItemAttribute.ActionId: // var actionId = stream.ReadUInt16(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.ActionId)} not implemented."); // break; // case OTBMWorldItemAttribute.UniqueId: // var uniqueId = stream.ReadUInt16(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.UniqueId)} not implemented."); // break; // case OTBMWorldItemAttribute.Text: // var text = stream.ReadString(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Text)} not implemented."); // break; // case OTBMWorldItemAttribute.WrittenDate: // var writtenDate = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.WrittenDate)} not implemented."); // break; // case OTBMWorldItemAttribute.WrittenBy: // var writtenBy = stream.ReadString(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.WrittenBy)} not implemented."); // break; // case OTBMWorldItemAttribute.Description: // var anotherDescription = stream.ReadString(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Description)} not implemented."); // break; // case OTBMWorldItemAttribute.Charges: // var charges = stream.ReadUInt16(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Charges)} not implemented."); // break; // case OTBMWorldItemAttribute.Duration: // var duration = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Duration)} not implemented."); // break; // case OTBMWorldItemAttribute.DecayingState: // var decayingState = stream.ReadByte(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.DecayingState)} not implemented."); // break; // case OTBMWorldItemAttribute.Name: // var name = stream.ReadString(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Name)} not implemented."); // break; // case OTBMWorldItemAttribute.Article: // var article = stream.ReadString(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Article)} not implemented."); // break; // case OTBMWorldItemAttribute.PluralName: // var pluralName = stream.ReadString(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Article)} not implemented."); // break; // case OTBMWorldItemAttribute.Weight: // var weight = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Weight)} not implemented."); // break; // case OTBMWorldItemAttribute.Attack: // var attack = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Attack)} not implemented."); // break; // case OTBMWorldItemAttribute.Defense: // var defense = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Defense)} not implemented."); // break; // case OTBMWorldItemAttribute.ExtraDefense: // var extraDefense = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.ExtraDefense)} not implemented."); // break; // case OTBMWorldItemAttribute.Armor: // var armor = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.Armor)} not implemented."); // break; // case OTBMWorldItemAttribute.HitChance: // var hitChance = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.HitChance)} not implemented."); // break; // case OTBMWorldItemAttribute.ShootRange: // var shootRange = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.ShootRange)} not implemented."); // break; // case OTBMWorldItemAttribute.DepotId: // var depotId = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.DepotId)} not implemented."); // break; // case OTBMWorldItemAttribute.HouseDorId: // var hourDoorId = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.HouseDorId)} not implemented."); // break; // case OTBMWorldItemAttribute.SleeperGUID: // var sleeperGUID = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.SleeperGUID)} not implemented."); // break; // case OTBMWorldItemAttribute.SleepStart: // var sleepStart = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.SleepStart)} not implemented."); // break; // case OTBMWorldItemAttribute.TeleportDestination: // var teleportDestination = stream.ReadUInt32(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.TeleportDestination)} not implemented."); // break; // case OTBMWorldItemAttribute.ContainerItems: // throw new InvalidOperationException("TFS threw an exception here, so we're throwing too."); // case OTBMWorldItemAttribute.CustomAttributes: // var size = stream.ReadUInt64(); // _logger.Warn($"OTBM {nameof(OTBMWorldItemAttribute.CustomAttributes)} not implemented."); // for (UInt64 i = 0; i < size; i++) { // var key = stream.ReadString(); // var pos = (OTBMCustomAttributeValueType)stream.ReadByte(); // object value; // switch (pos) { // case OTBMCustomAttributeValueType.String: // value = stream.ReadString(); // break; // case OTBMCustomAttributeValueType.Int64: // value = stream.ReadUInt64(); // break; // case OTBMCustomAttributeValueType.Double: // value = stream.ReadDouble(); // break; // case OTBMCustomAttributeValueType.Bool: // value = stream.ReadBool(); // break; // default: // value = new object(); // break; // } // } // break; // case OTBMWorldItemAttribute.AnotherDescription: // throw new InvalidOperationException("TFS didn't implement this."); // case OTBMWorldItemAttribute.ExtensionFile: // throw new InvalidOperationException("TFS didn't implement this."); // case OTBMWorldItemAttribute.TileFlags: // throw new InvalidOperationException("TFS didn't implement this."); // case OTBMWorldItemAttribute.Item: // throw new InvalidOperationException("TFS didn't implement this."); // case OTBMWorldItemAttribute.ExtensionFileForSpawns: // throw new InvalidOperationException("TFS didn't implement this."); // case OTBMWorldItemAttribute.ExtensionFileForHouses: // throw new InvalidOperationException("TFS didn't implement this."); // default: // throw new InvalidOperationException($"Unknown {nameof(OTBMWorldItemAttribute)}."); // } // } //} return(item); }
public Dictionary <ushort, ItemType> LoadOTItems() { var itemDictionary = new Dictionary <UInt16, ItemType>(); var attrsNotSuported = 0; var attrsNotValid = 0; var fileTree = OTBDeserializer.DeserializeOTBData(new ReadOnlyMemory <byte>(ServerResourcesManager.GetItemsBytes("items.otb"))); foreach (var itemChildren in fileTree.Children) { var current = new ItemType(); var itemStream = new OTBParsingStream(itemChildren.Data); var flags = itemStream.ReadUInt32(); current.ParseOTFlags(flags); while (!itemStream.IsOver) { var attr = itemStream.ReadByte(); var dataSize = itemStream.ReadUInt16(); switch ((ItemAttributes)attr) { case ItemAttributes.ServerId: var serverId = itemStream.ReadUInt16(); if (serverId == 4535) { serverId = 4535; } if (serverId > 30000 && serverId < 30100) { serverId -= 30000; } current.SetId(serverId); break; case ItemAttributes.ClientId: current.SetClientId(itemStream.ReadUInt16()); break; default: itemStream.Skip(dataSize); break; } } itemDictionary.Add(current.TypeId, current); } var rootElement = XElement.Load(ServerResourcesManager.GetItems("items.xml"), LoadOptions.SetLineInfo); foreach (var element in rootElement.Elements("item")) { var id = element.Attribute("id"); var fromId = element.Attribute("fromid"); var toId = element.Attribute("toid"); // Malformed element, missing id information, ignore it if (id == null && (fromId == null || toId == null)) { continue; } ushort serverId = 0; ushort aplyTo = 1; if (id == null) { // Ignore if can't parse the values or if fromId >= toId if (!ushort.TryParse(fromId.Value, out serverId) || !ushort.TryParse(toId.Value, out aplyTo) || serverId >= aplyTo) { continue; } aplyTo -= serverId; } else { if (!ushort.TryParse(id.Value, out serverId)) { continue; } } for (ushort key = serverId; key < serverId + aplyTo; key++) { if (!itemDictionary.TryGetValue(key, out ItemType current)) { continue; } var name = element.Attribute("name"); if (name != null) { current.SetName(name.Value); } foreach (var attribute in element.Elements("attribute")) { var attrName = attribute.Attribute("key"); var attrValue = attribute.Attribute("value"); if (attrName == null || attrValue == null) { continue; } if (attrName.Value == "description") { current.SetDescription(attrValue.Value); continue; } var lineInfo = (IXmlLineInfo)attribute; var attr = OpenTibiaTranslationMap.TranslateAttributeName(attrName.Value, out bool success); if (success) { int value = -1; bool setAttr = true; switch (attrName.Value) { case "weaponType": success = current.ParseOTWeaponType(attrValue.Value); setAttr = false; break; case "fluidSource": value = OpenTibiaTranslationMap.TranslateLiquidType(attrValue.Value, out success); break; case "corpseType": value = OpenTibiaTranslationMap.TranslateCorpseType(attrValue.Value, out success); break; case "slotType": value = OpenTibiaTranslationMap.TranslateSlotType(attrValue.Value, out success); break; default: success = int.TryParse(attrValue.Value, out value); break; } if (!success) { attrsNotValid++; //Console.WriteLine($"[{Path.GetFileName(itemExtensionFilePath)}:{lineInfo.LineNumber}] \"{attrValue.Value}\" is not a valid value for attribute \"{attrName.Value}\""); } else if (setAttr) { current.SetAttribute(attr, value); } } else { attrsNotSuported++; //Console.WriteLine($"[{Path.GetFileName(itemExtensionFilePath)}:{lineInfo.LineNumber}] Attribute \"{attrName.Value}\" is not supported!"); } } } } foreach (var type in itemDictionary) { type.Value.LockChanges(); } Console.WriteLine($"Items with attributes not supported: {attrsNotSuported}"); Console.WriteLine($"Not valid attributes: {attrsNotSuported}"); return(itemDictionary); }