using System; using System.Collections.Generic; using System.Globalization; using System.Xml; using Oni.Imaging; using Oni.Metadata; namespace Oni.Xml { internal class GenericXmlWriter : IMetaTypeVisitor { private readonly Action exportChild; private readonly XmlWriter xml; private readonly Stack startOffsetStack; private InstanceFile instanceFile; private BinaryReader reader; public static void Write(XmlWriter xml, Action exportChild, InstanceDescriptor descriptor) { var writer = new GenericXmlWriter(xml, exportChild); writer.WriteInstance(descriptor); } private GenericXmlWriter(XmlWriter xml, Action exportChild) { this.exportChild = exportChild; this.xml = xml; this.startOffsetStack = new Stack(); } private void WriteInstance(InstanceDescriptor descriptor) { using (reader = descriptor.OpenRead()) { BeginXmlInstance(descriptor); descriptor.Template.Type.Accept(this); EndXmlInstance(); } } private void BeginXmlInstance(InstanceDescriptor descriptor) { instanceFile = descriptor.File; startOffsetStack.Push(reader.Position - 8); string tag = descriptor.Template.Tag.ToString(); xml.WriteStartElement(tag); xml.WriteAttributeString("id", XmlConvert.ToString(descriptor.Index)); } private void EndXmlInstance() { startOffsetStack.Pop(); xml.WriteEndElement(); } #region IMetaTypeVisitor Members void IMetaTypeVisitor.VisitEnum(MetaEnum type) { type.BinaryToXml(reader, xml); } void IMetaTypeVisitor.VisitByte(MetaByte type) { xml.WriteValue(reader.ReadByte()); } void IMetaTypeVisitor.VisitInt16(MetaInt16 type) { xml.WriteValue(reader.ReadInt16()); } void IMetaTypeVisitor.VisitUInt16(MetaUInt16 type) { xml.WriteValue(reader.ReadUInt16()); } void IMetaTypeVisitor.VisitInt32(MetaInt32 type) { xml.WriteValue(reader.ReadInt32()); } void IMetaTypeVisitor.VisitUInt32(MetaUInt32 type) { xml.WriteValue(reader.ReadUInt32()); } void IMetaTypeVisitor.VisitInt64(MetaInt64 type) { xml.WriteValue(reader.ReadInt64()); } void IMetaTypeVisitor.VisitUInt64(MetaUInt64 type) { xml.WriteValue(XmlConvert.ToString(reader.ReadUInt64())); } void IMetaTypeVisitor.VisitFloat(MetaFloat type) { xml.WriteValue(reader.ReadSingle()); } void IMetaTypeVisitor.VisitVector2(MetaVector2 type) { xml.WriteFloatArray(reader.ReadSingleArray(2)); } void IMetaTypeVisitor.VisitVector3(MetaVector3 type) { xml.WriteFloatArray(reader.ReadSingleArray(3)); } void IMetaTypeVisitor.VisitMatrix4x3(MetaMatrix4x3 type) { xml.WriteFloatArray(reader.ReadSingleArray(12)); } void IMetaTypeVisitor.VisitPlane(MetaPlane type) { xml.WriteFloatArray(reader.ReadSingleArray(4)); } void IMetaTypeVisitor.VisitQuaternion(MetaQuaternion type) { xml.WriteFloatArray(reader.ReadSingleArray(4)); } void IMetaTypeVisitor.VisitBoundingSphere(MetaBoundingSphere type) { WriteFields(type.Fields); } void IMetaTypeVisitor.VisitBoundingBox(MetaBoundingBox type) { WriteFields(type.Fields); } void IMetaTypeVisitor.VisitColor(MetaColor type) { Color color = reader.ReadColor(); if (color.A == 255) xml.WriteValue(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", color.R, color.G, color.B)); else xml.WriteValue(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", color.R, color.G, color.B, color.A)); } void IMetaTypeVisitor.VisitRawOffset(MetaRawOffset type) { xml.WriteValue(reader.ReadInt32()); } void IMetaTypeVisitor.VisitSepOffset(MetaSepOffset type) { xml.WriteValue(reader.ReadInt32()); } void IMetaTypeVisitor.VisitPointer(MetaPointer type) { int id = reader.ReadInt32(); if (id == 0) { xml.WriteString(string.Empty); return; } exportChild(instanceFile.GetDescriptor(id)); } void IMetaTypeVisitor.VisitString(MetaString type) { xml.WriteValue(reader.ReadString(type.Count)); } void IMetaTypeVisitor.VisitPadding(MetaPadding type) { reader.Skip(type.Count); } void IMetaTypeVisitor.VisitStruct(MetaStruct type) { WriteFields(type.Fields); } void IMetaTypeVisitor.VisitArray(MetaArray type) { WriteArray(type.ElementType, type.Count); } void IMetaTypeVisitor.VisitVarArray(MetaVarArray type) { int count; if (type.CountField.Type == MetaType.Int16) count = reader.ReadInt16(); else count = reader.ReadInt32(); WriteArray(type.ElementType, count); } #endregion private void WriteFields(IEnumerable fields) { foreach (var field in fields) { if (field.Type is MetaPadding) { field.Type.Accept(this); continue; } string name = field.Name; if (string.IsNullOrEmpty(name)) { int fieldOffset = reader.Position - startOffsetStack.Peek(); name = string.Format(CultureInfo.InvariantCulture, "Offset_{0:X4}", fieldOffset); } xml.WriteStartElement(name); field.Type.Accept(this); xml.WriteEndElement(); } } private void WriteArray(MetaType elementType, int count) { bool simpleArray = elementType.IsBlittable; simpleArray = false; for (int i = 0; i < count; i++) { startOffsetStack.Push(reader.Position); if (!simpleArray) xml.WriteStartElement(elementType.Name); elementType.Accept(this); if (!simpleArray) xml.WriteEndElement(); else if (i != count - 1) xml.WriteWhitespace(" \n"); startOffsetStack.Pop(); } } } }