internal void PrepareResourceIndices(SavingContext ctx) { foreach (AxmlAttribute attribute in Attributes) { attribute.PrepareResourceIndices(ctx); } foreach (AxmlElement element in Children) { element.PrepareResourceIndices(ctx); } }
internal void PreparePooling(SavingContext ctx) { // First we need to write the namespaces declared within this element foreach (KeyValuePair <string, Uri> pair in DeclaredNamespaces) { ctx.StringPool.Add(pair.Key); ctx.StringPool.Add(pair.Value.ToString()); } if (NamespaceUri != null) { ctx.StringPool.Add(NamespaceUri.ToString()); } ctx.StringPool.Add(Name); // Sort the attributes in order of increasing resource Id, and alphabetical order in terms of the namespaces // Attributes with a namespace will also come before attributes without a namespace Attributes.Sort((a, b) => { int resourceIdDiff = (a.ResourceId ?? -1) - (b.ResourceId ?? -1); if (resourceIdDiff != 0) { return(resourceIdDiff); } if (a.Namespace == null) { return(b.Namespace == null ? 0 : -1); } else { return(b.Namespace == null ? 1 : String.CompareOrdinal(a.Namespace.ToString(), b.Namespace.ToString())); } }); foreach (AxmlAttribute attribute in Attributes) { attribute.PreparePooling(ctx); } foreach (AxmlElement element in Children) { element.PreparePooling(ctx); } }
/// <summary> /// Saves the given root element to the given stream as AXML. /// </summary> /// <param name="stream">Stream to save to</param> /// <param name="rootElement">Root element of the document</param> public static void SaveDocument(Stream stream, AxmlElement rootElement) { BinaryWriter mainOutput = new BinaryWriter(stream); // Write the main elements chunk of the file to a MemoryStream first SavingContext ctx = new SavingContext(); rootElement.PreparePooling(ctx); string[] stringPool = ctx.StringPool.PrepareForSavePhase(ctx.ResourceMap); rootElement.Save(ctx); MemoryStream mainChunkStream = (MemoryStream)ctx.Writer.BaseStream; int[] resourcePool = ctx.ResourceMap.Save(); int stringPoolLength = StringPoolSerializer.CalculatePoolLength(stringPool); int stringPoolPadding = (4 - stringPoolLength % 4) % 4; stringPoolLength += stringPoolPadding; // Add padding to four bytes int resourcePoolLength = resourcePool.Length * 4; // Each pool item is an integer // The length of the main xml tag is that of the whole file, so also including the string pool, resource pool, and main chunk. (+ extra 8 + 8 = 16 bytes for string pool and resource pool header) mainOutput.WriteChunkHeader(ResourceType.Xml, stringPoolLength + resourcePoolLength + (int)mainChunkStream.Position + 16); mainOutput.WriteChunkHeader(ResourceType.StringPool, stringPoolLength); StringPoolSerializer.SaveStringPool(stringPool, mainOutput); for (int i = 0; i < stringPoolPadding; i++) { mainOutput.Write((byte)0); } mainOutput.WriteChunkHeader(ResourceType.XmlResourceMap, resourcePoolLength); foreach (int resource in resourcePool) { mainOutput.Write(resource); } // Save the main chunk of the file mainChunkStream.Position = 0; mainChunkStream.CopyTo(stream); }
internal void Save(SavingContext ctx) { // First we need to write the namespaces declared within this element foreach (KeyValuePair <string, Uri> pair in DeclaredNamespaces) { ctx.Writer.WriteChunkHeader(ResourceType.XmlStartNamespace, 16); // Each namespace tag is 3 integers, so 3 * 4 = 12 bytes ctx.Writer.Write(TextLineNumber); ctx.Writer.Write(0xFFFFFFFF); ctx.Writer.Write(ctx.StringPool.GetIndex(pair.Key)); ctx.Writer.Write(ctx.StringPool.GetIndex(pair.Value.ToString())); } ctx.Writer.WriteChunkHeader(ResourceType.XmlStartElement, 28 + 20 * Attributes.Count); // Each attribute is 5 integers, so 5 * 4 = 20 bytes of the tag ctx.Writer.Write(TextLineNumber); ctx.Writer.Write(0xFFFFFFFF); ctx.Writer.Write(NamespaceUri == null ? -1 : ctx.StringPool.GetIndex(NamespaceUri.ToString())); ctx.Writer.Write(ctx.StringPool.GetIndex(Name)); ctx.Writer.Write(0x00140014); // Find the ID, class and style attribute indices if they exist short idAttributeIndex = -1; short classAttributeIndex = -1; short styleAttributeIndex = -1; for (short i = 0; i < Attributes.Count; i++) { WrappedValue?wrappedValue = Attributes[i].Value as WrappedValue; if (wrappedValue == null) { continue; } // Make sure to prevent multiple of these attributes, as this will save incorrectly switch (wrappedValue.Type) { case WrappedValueType.Id: if (idAttributeIndex != -1) { throw new InvalidDataException("Cannot have multiple ID attributes on one element"); } idAttributeIndex = i; break; case WrappedValueType.Class: if (classAttributeIndex != -1) { throw new InvalidDataException("Cannot have multiple class attributes on one element"); } classAttributeIndex = i; break; case WrappedValueType.Style: if (styleAttributeIndex != -1) { throw new InvalidDataException("Cannot have multiple style attributes on one element"); } styleAttributeIndex = i; break; } } ctx.Writer.Write((short)Attributes.Count); // Stored indices are one above the actual ones ctx.Writer.Write((short)(idAttributeIndex + 1)); ctx.Writer.Write((short)(classAttributeIndex + 1)); ctx.Writer.Write((short)(styleAttributeIndex + 1)); foreach (AxmlAttribute attribute in Attributes) { attribute.Save(ctx); } foreach (AxmlElement child in Children) { child.Save(ctx); } ctx.Writer.WriteChunkHeader(ResourceType.XmlEndElement, 16); ctx.Writer.Write(-1); ctx.Writer.Write(0xFFFFFFFF); ctx.Writer.Write(NamespaceUri == null ? -1 : ctx.StringPool.GetIndex(NamespaceUri.ToString())); ctx.Writer.Write(ctx.StringPool.GetIndex(Name)); // End the namespaces stated by this element, as we have exited it foreach (KeyValuePair <string, Uri> pair in DeclaredNamespaces.Reverse()) { ctx.Writer.WriteChunkHeader(ResourceType.XmlEndNamespace, 16); // Each namespace tag is 3 integers, so 3 * 4 = 12 bytes ctx.Writer.Write(TextLineNumber); ctx.Writer.Write(0xFFFFFFFF); ctx.Writer.Write(ctx.StringPool.GetIndex(pair.Key)); ctx.Writer.Write(ctx.StringPool.GetIndex(pair.Value.ToString())); } }