/// <summary>Creates a node from the xml element. This method gets only called if <see cref="CanHandleElement(XElement)"/> returned true.</summary> /// <param name="element">The element to create the node from.</param> /// <param name="parent">The parent of the node.</param> /// <param name="classes">The list of classes which correspond to the node.</param> /// <param name="logger">The logger used to output messages.</param> /// <param name="node">[out] The node for the xml element.</param> /// <returns>True if a node was created, otherwise false.</returns> public bool TryCreateNodeFromElement(XElement element, ClassNode parent, IEnumerable <ClassNode> classes, ILogger logger, out BaseNode node) { node = null; var reference = NodeUuid.FromBase64String(element.Attribute(ReClassNetFile.XmlReferenceAttribute)?.Value, false); var innerClass = classes.FirstOrDefault(c => c.Uuid.Equals(reference)); if (innerClass == null) { logger.Log(LogLevel.Warning, $"Skipping node with unknown reference: {reference}"); logger.Log(LogLevel.Warning, element.ToString()); return(false); } var weakPtrNode = new WeakPtrNode { Name = element.Attribute(ReClassNetFile.XmlNameAttribute)?.Value ?? string.Empty, Comment = element.Attribute(ReClassNetFile.XmlCommentAttribute)?.Value ?? string.Empty }; weakPtrNode.ChangeInnerNode(innerClass); node = weakPtrNode; return(true); }
public bool TryCreateNodeFromElement(XElement element, ClassNode parent, IEnumerable <ClassNode> classes, ILogger logger, out BaseNode node) { node = null; var type = element.Attribute(ReClassNetFile.XmlTypeAttribute)?.Value; switch (type) { case XmlTypePrefix + "FDateTime": node = new FDateTimeNode(); break; case XmlTypePrefix + "FGuid": node = new FGuidNode(); break; case XmlTypePrefix + "FQWord": node = new FQWordNode(); break; case XmlTypePrefix + "FString": node = new FStringNode(); break; case XmlTypePrefix + "TArray": case XmlTypePrefix + "TSharedPtr": { var reference = NodeUuid.FromBase64String(element.Attribute(ReClassNetFile.XmlReferenceAttribute)?.Value, false); var innerClass = classes.FirstOrDefault(c => c.Uuid.Equals(reference)); if (innerClass == null) { logger.Log(LogLevel.Warning, $"Skipping node with unknown reference: {reference}"); logger.Log(LogLevel.Warning, element.ToString()); return(false); } if (type == XmlTypePrefix + "TArray") { node = new TArrayNode(); } else { node = new TSharedPtrNode(); } ((BaseReferenceNode)node).ChangeInnerNode(innerClass); break; } default: throw new InvalidOperationException("Can not handle node type: " + type); } node.Name = element.Attribute(ReClassNetFile.XmlNameAttribute)?.Value ?? string.Empty; node.Comment = element.Attribute(ReClassNetFile.XmlCommentAttribute)?.Value ?? string.Empty; return(true); }
public void Load(Stream input, ILogger logger) { Contract.Requires(input != null); Contract.Requires(logger != null); using (var archive = new ZipArchive(input, ZipArchiveMode.Read)) { var dataEntry = archive.GetEntry(DataFileName); if (dataEntry == null) { throw new FormatException(); } using (var entryStream = dataEntry.Open()) { var document = XDocument.Load(entryStream); var version = document.Root.Attribute(XmlVersionAttribute)?.Value; var platform = document.Root.Attribute(XmlTypeAttribute)?.Value; if (platform != Constants.Platform) { logger.Log(LogLevel.Warning, $"The platform of the file ({platform}) doesn't match the program platform ({Constants.Platform})."); } var classes = new List <Tuple <XElement, ClassNode> >(); foreach (var element in document.Root .Element(XmlClassesElement) .Elements(XmlClassElement) .DistinctBy(e => e.Attribute(XmlUuidAttribute)?.Value)) { var node = new ClassNode(false) { Uuid = NodeUuid.FromBase64String(element.Attribute(XmlUuidAttribute)?.Value, true), Name = element.Attribute(XmlNameAttribute)?.Value ?? string.Empty, Comment = element.Attribute(XmlCommentAttribute)?.Value ?? string.Empty, AddressFormula = element.Attribute(XmlAddressAttribute)?.Value ?? string.Empty }; if (!project.ContainsClass(node.Uuid)) { project.AddClass(node); classes.Add(Tuple.Create(element, node)); } } foreach (var t in classes) { ReadNodeElements( t.Item1.Elements(XmlNodeElement), t.Item2, logger ).ForEach(t.Item2.AddNode); } } } }
public void Load(Stream input, ILogger logger) { Contract.Requires(input != null); Contract.Requires(logger != null); using (var archive = new ZipArchive(input, ZipArchiveMode.Read)) { var dataEntry = archive.GetEntry(DataFileName); if (dataEntry == null) { throw new FormatException(); } using (var entryStream = dataEntry.Open()) { var document = XDocument.Load(entryStream); if (document.Root?.Element(XmlClassesElement) == null) { throw new FormatException("The data has not the correct format."); } uint.TryParse(document.Root.Attribute(XmlVersionAttribute)?.Value, out var fileVersion); if ((fileVersion & FileVersionCriticalMask) > (FileVersion & FileVersionCriticalMask)) { throw new FormatException($"The file version is unsupported. A newer {Constants.ApplicationName} version is required to read it."); } var platform = document.Root.Attribute(XmlPlatformAttribute)?.Value; if (platform != Constants.Platform) { logger.Log(LogLevel.Warning, $"The platform of the file ({platform}) doesn't match the program platform ({Constants.Platform})."); } var customDataElement = document.Root.Element(XmlCustomDataElement); if (customDataElement != null) { project.CustomData.Deserialize(customDataElement); } var typeMappingElement = document.Root.Element(XmlTypeMappingElement); if (typeMappingElement != null) { project.TypeMapping.Deserialize(typeMappingElement); } var classes = new List <Tuple <XElement, ClassNode> >(); foreach (var element in document.Root .Element(XmlClassesElement) .Elements(XmlClassElement) .DistinctBy(e => e.Attribute(XmlUuidAttribute)?.Value)) { var node = new ClassNode(false) { Uuid = NodeUuid.FromBase64String(element.Attribute(XmlUuidAttribute)?.Value, true), Name = element.Attribute(XmlNameAttribute)?.Value ?? string.Empty, Comment = element.Attribute(XmlCommentAttribute)?.Value ?? string.Empty, AddressFormula = element.Attribute(XmlAddressAttribute)?.Value ?? string.Empty }; if (!project.ContainsClass(node.Uuid)) { project.AddClass(node); classes.Add(Tuple.Create(element, node)); } } foreach (var t in classes) { var nodes = t.Item1.Elements(XmlNodeElement) .Select(e => CreateNodeFromElement(e, t.Item2, logger)) .Where(n => n != null); foreach (var node in nodes) { t.Item2.AddNode(node); } } } } }
private BaseNode CreateNodeFromElement(XElement element, BaseNode parent, ILogger logger) { Contract.Requires(element != null); Contract.Requires(logger != null); BaseNode CreateNode() { var converter = CustomNodeSerializer.GetReadConverter(element); if (converter != null) { return(converter.CreateNodeFromElement(element, parent, project.Classes, logger, CreateNodeFromElement)); } if (!buildInStringToTypeMap.TryGetValue(element.Attribute(XmlTypeAttribute)?.Value ?? string.Empty, out var nodeType)) { logger.Log(LogLevel.Error, $"Skipping node with unknown type: {element.Attribute(XmlTypeAttribute)?.Value}"); logger.Log(LogLevel.Warning, element.ToString()); return(null); } return(BaseNode.CreateInstanceFromType(nodeType, false)); } var node = CreateNode(); if (node == null) { logger.Log(LogLevel.Error, "Could not create node."); return(null); } node.ParentNode = parent; node.Name = element.Attribute(XmlNameAttribute)?.Value ?? string.Empty; node.Comment = element.Attribute(XmlCommentAttribute)?.Value ?? string.Empty; node.IsHidden = bool.TryParse(element.Attribute(XmlHiddenAttribute)?.Value, out var val) && val; if (node is BaseWrapperNode wrapperNode) { ClassNode GetClassNodeFromElementReference() { var reference = NodeUuid.FromBase64String(element.Attribute(XmlReferenceAttribute)?.Value, false); if (!project.ContainsClass(reference)) { logger.Log(LogLevel.Error, $"Skipping node with unknown reference: {reference}"); logger.Log(LogLevel.Warning, element.ToString()); return(null); } return(project.GetClassByUuid(reference)); } // Legacy Support if (node is ClassPointerNode || node is ClassInstanceArrayNode || node is ClassPointerArrayNode) { var innerClass = GetClassNodeFromElementReference(); if (innerClass == null) { return(null); } switch (node) { case BaseClassArrayNode classArrayNode: node = classArrayNode.GetEquivalentNode(0, innerClass); break; case ClassPointerNode classPointerNode: node = classPointerNode.GetEquivalentNode(innerClass); break; } } else { BaseNode innerNode = null; if (node is BaseClassWrapperNode) { innerNode = GetClassNodeFromElementReference(); if (innerNode == null) { return(null); } } else { var innerElement = element.Elements().FirstOrDefault(); if (innerElement != null) { innerNode = CreateNodeFromElement(innerElement, node, logger); } } if (wrapperNode.CanChangeInnerNodeTo(innerNode)) { var rootWrapperNode = node.GetRootWrapperNode(); if (rootWrapperNode.ShouldPerformCycleCheckForInnerNode() && innerNode is ClassNode classNode && ClassUtil.IsCyclicIfClassIsAccessibleFromParent(node.GetParentClass(), classNode, project.Classes)) { logger.Log(LogLevel.Error, $"Skipping node with cyclic class reference: {node.GetParentClass().Name}->{rootWrapperNode.Name}"); return(null); } wrapperNode.ChangeInnerNode(innerNode); } else { logger.Log(LogLevel.Error, $"The node {innerNode} is not a valid child for {node}."); } } } switch (node) { case VirtualMethodTableNode vtableNode: { var nodes = element .Elements(XmlMethodElement) .Select(e => new VirtualMethodNode { Name = e.Attribute(XmlNameAttribute)?.Value ?? string.Empty, Comment = e.Attribute(XmlCommentAttribute)?.Value ?? string.Empty, IsHidden = e.Attribute(XmlHiddenAttribute)?.Value.Equals("True") ?? false }); foreach (var vmethodNode in nodes) { vtableNode.AddNode(vmethodNode); } break; } case BaseWrapperArrayNode arrayNode: { TryGetAttributeValue(element, XmlCountAttribute, out var count, logger); arrayNode.Count = count; break; } case BaseTextNode textNode: { TryGetAttributeValue(element, XmlLengthAttribute, out var length, logger); textNode.Length = length; break; } case BitFieldNode bitFieldNode: { TryGetAttributeValue(element, XmlBitsAttribute, out var bits, logger); bitFieldNode.Bits = bits; break; } case FunctionNode functionNode: { functionNode.Signature = element.Attribute(XmlSignatureAttribute)?.Value ?? string.Empty; var reference = NodeUuid.FromBase64String(element.Attribute(XmlReferenceAttribute)?.Value, false); if (project.ContainsClass(reference)) { functionNode.BelongsToClass = project.GetClassByUuid(reference); } break; } } return(node); }
private IEnumerable <BaseNode> ReadNodeElements(IEnumerable <XElement> elements, ClassNode parent, ILogger logger) { Contract.Requires(elements != null); Contract.Requires(Contract.ForAll(elements, e => e != null)); Contract.Requires(parent != null); Contract.Requires(logger != null); foreach (var element in elements) { var converter = CustomNodeConvert.GetReadConverter(element); if (converter != null) { if (converter.TryCreateNodeFromElement(element, parent, project.Classes, logger, out var customNode)) { yield return(customNode); } continue; } if (!buildInStringToTypeMap.TryGetValue(element.Attribute(XmlTypeAttribute)?.Value ?? string.Empty, out var nodeType)) { logger.Log(LogLevel.Error, $"Skipping node with unknown type: {element.Attribute(XmlTypeAttribute)?.Value}"); logger.Log(LogLevel.Warning, element.ToString()); continue; } var node = Activator.CreateInstance(nodeType) as BaseNode; if (node == null) { logger.Log(LogLevel.Error, $"Could not create node of type: {nodeType}"); continue; } node.Name = element.Attribute(XmlNameAttribute)?.Value ?? string.Empty; node.Comment = element.Attribute(XmlCommentAttribute)?.Value ?? string.Empty; if (node is BaseReferenceNode referenceNode) { var reference = NodeUuid.FromBase64String(element.Attribute(XmlReferenceAttribute)?.Value, false); if (!project.ContainsClass(reference)) { logger.Log(LogLevel.Error, $"Skipping node with unknown reference: {reference}"); logger.Log(LogLevel.Warning, element.ToString()); continue; } var innerClassNode = project.GetClassByUuid(reference); if (referenceNode.PerformCycleCheck && !ClassUtil.IsCycleFree(parent, innerClassNode, project.Classes)) { logger.Log(LogLevel.Error, $"Skipping node with cycle reference: {parent.Name}->{node.Name}"); continue; } referenceNode.ChangeInnerNode(innerClassNode); } switch (node) { case VTableNode vtableNode: { element .Elements(XmlMethodElement) .Select(e => new VMethodNode { Name = e.Attribute(XmlNameAttribute)?.Value ?? string.Empty, Comment = e.Attribute(XmlCommentAttribute)?.Value ?? string.Empty }) .ForEach(vtableNode.AddNode); break; } case BaseArrayNode arrayNode: { TryGetAttributeValue(element, XmlCountAttribute, out var count, logger); arrayNode.Count = count; break; } case BaseTextNode textNode: { TryGetAttributeValue(element, XmlLengthAttribute, out var length, logger); textNode.Length = length; break; } case BitFieldNode bitFieldNode: { TryGetAttributeValue(element, XmlBitsAttribute, out var bits, logger); bitFieldNode.Bits = bits; break; } case FunctionNode functionNode: { functionNode.Signature = element.Attribute(XmlSignatureAttribute)?.Value ?? string.Empty; var reference = NodeUuid.FromBase64String(element.Attribute(XmlReferenceAttribute)?.Value, false); if (project.ContainsClass(reference)) { functionNode.BelongsToClass = project.GetClassByUuid(reference); } break; } } yield return(node); } }
public void Load(Stream input, ILogger logger) { Contract.Requires(input != null); Contract.Requires(logger != null); using var archive = new ZipArchive(input, ZipArchiveMode.Read); var dataEntry = archive.GetEntry(DataFileName); if (dataEntry == null) { throw new FormatException(); } using var entryStream = dataEntry.Open(); var document = XDocument.Load(entryStream); if (document.Root?.Element(XmlClassesElement) == null) { throw new FormatException("The data has not the correct format."); } uint.TryParse(document.Root.Attribute(XmlVersionAttribute)?.Value, out var fileVersion); if ((fileVersion & FileVersionCriticalMask) > (FileVersion & FileVersionCriticalMask)) { throw new FormatException($"The file version is unsupported. A newer {Constants.ApplicationName} version is required to read it."); } var platform = document.Root.Attribute(XmlPlatformAttribute)?.Value; if (platform != Constants.Platform) { logger.Log(LogLevel.Warning, $"The platform of the file ({platform}) doesn't match the program platform ({Constants.Platform})."); } var customDataElement = document.Root.Element(XmlCustomDataElement); if (customDataElement != null) { project.CustomData.Deserialize(customDataElement); } var typeMappingElement = document.Root.Element(XmlTypeMappingElement); if (typeMappingElement != null) { project.TypeMapping.Deserialize(typeMappingElement); } var enumsElement = document.Root.Element(XmlEnumsElement); if (enumsElement != null) { foreach (var enumElement in enumsElement.Elements(XmlEnumElement)) { var name = enumElement.Attribute(XmlNameAttribute)?.Value ?? string.Empty; var useFlagsMode = (bool?)enumElement.Attribute(XmlFlagsAttribute) ?? false; var size = enumElement.Attribute(XmlSizeAttribute).GetEnumValue <EnumDescription.UnderlyingTypeSize>(); var values = new Dictionary <string, long>(); foreach (var itemElement in enumElement.Elements(XmlItemElement)) { var itemName = itemElement.Attribute(XmlNameAttribute)?.Value ?? string.Empty; var itemValue = (long?)itemElement.Attribute(XmlValueAttribute) ?? 0L; values.Add(itemName, itemValue); } var @enum = new EnumDescription { Name = name }; @enum.SetData(useFlagsMode, size, values); project.AddEnum(@enum); } } var classes = new List <(XElement, ClassNode)>(); var classesElement = document.Root.Element(XmlClassesElement); if (classesElement != null) { foreach (var element in classesElement .Elements(XmlClassElement) .DistinctBy(e => e.Attribute(XmlUuidAttribute)?.Value)) { var node = new ClassNode(false) { Uuid = NodeUuid.FromBase64String(element.Attribute(XmlUuidAttribute)?.Value, true), Name = element.Attribute(XmlNameAttribute)?.Value ?? string.Empty, Comment = element.Attribute(XmlCommentAttribute)?.Value ?? string.Empty, AddressFormula = element.Attribute(XmlAddressAttribute)?.Value ?? string.Empty }; if (!project.ContainsClass(node.Uuid)) { project.AddClass(node); classes.Add((element, node)); } } } foreach (var(element, classNode) in classes) { var nodes = element.Elements(XmlNodeElement) .Select(e => CreateNodeFromElement(e, classNode, logger)) .Where(n => n != null); classNode.BeginUpdate(); classNode.AddNodes(nodes); classNode.EndUpdate(); } }