using System; using System.Collections.Generic; using System.Xml; using Oni.Imaging; using Oni.Metadata; namespace Oni.Xml { internal class RawXmlImporter : IMetaTypeVisitor { private static readonly Func floatConverter = XmlConvert.ToSingle; private static readonly Func byteConverter = XmlConvert.ToByte; private readonly XmlReader xml; private readonly BinaryWriter writer; private Stack startOffsetStack; public RawXmlImporter(XmlReader xml, BinaryWriter writer) { this.xml = xml; this.writer = writer; } protected XmlReader Xml => xml; protected BinaryWriter Writer => writer; protected void BeginStruct(int startPosition) { startOffsetStack = new Stack(); startOffsetStack.Push(startPosition); } #region IMetaTypeVisitor Members void IMetaTypeVisitor.VisitEnum(MetaEnum type) { type.XmlToBinary(xml, writer); } void IMetaTypeVisitor.VisitByte(MetaByte type) { writer.Write(XmlConvert.ToByte(xml.ReadElementContentAsString())); } void IMetaTypeVisitor.VisitInt16(MetaInt16 type) { writer.Write(XmlConvert.ToInt16(xml.ReadElementContentAsString())); } void IMetaTypeVisitor.VisitUInt16(MetaUInt16 type) { writer.Write(XmlConvert.ToUInt16(xml.ReadElementContentAsString())); } void IMetaTypeVisitor.VisitInt32(MetaInt32 type) { writer.Write(xml.ReadElementContentAsInt()); } void IMetaTypeVisitor.VisitUInt32(MetaUInt32 type) { writer.Write(XmlConvert.ToUInt32(xml.ReadElementContentAsString())); } void IMetaTypeVisitor.VisitInt64(MetaInt64 type) { writer.Write(xml.ReadElementContentAsLong()); } void IMetaTypeVisitor.VisitUInt64(MetaUInt64 type) { writer.Write(XmlConvert.ToUInt64(xml.ReadElementContentAsString())); } void IMetaTypeVisitor.VisitFloat(MetaFloat type) { writer.Write(xml.ReadElementContentAsFloat()); } void IMetaTypeVisitor.VisitColor(MetaColor type) { byte[] values = xml.ReadElementContentAsArray(byteConverter); if (values.Length > 3) writer.Write(new Color(values[0], values[1], values[2], values[3])); else writer.Write(new Color(values[0], values[1], values[2])); } void IMetaTypeVisitor.VisitVector2(MetaVector2 type) { writer.Write(xml.ReadElementContentAsArray(floatConverter, 2)); } void IMetaTypeVisitor.VisitVector3(MetaVector3 type) { writer.Write(xml.ReadElementContentAsArray(floatConverter, 3)); } void IMetaTypeVisitor.VisitMatrix4x3(MetaMatrix4x3 type) { writer.WriteMatrix4x3(xml.ReadElementContentAsMatrix43()); } void IMetaTypeVisitor.VisitPlane(MetaPlane type) { writer.Write(xml.ReadElementContentAsArray(floatConverter, 4)); } void IMetaTypeVisitor.VisitQuaternion(MetaQuaternion type) { writer.Write(xml.ReadElementContentAsQuaternion()); } void IMetaTypeVisitor.VisitBoundingSphere(MetaBoundingSphere type) { ReadFields(type.Fields); } void IMetaTypeVisitor.VisitBoundingBox(MetaBoundingBox type) { ReadFields(type.Fields); } void IMetaTypeVisitor.VisitRawOffset(MetaRawOffset type) { throw new NotImplementedException(); } void IMetaTypeVisitor.VisitSepOffset(MetaSepOffset type) { throw new NotImplementedException(); } void IMetaTypeVisitor.VisitString(MetaString type) { writer.Write(xml.ReadElementContentAsString(), type.Count); } void IMetaTypeVisitor.VisitPadding(MetaPadding type) { writer.Write(type.FillByte, type.Count); } void IMetaTypeVisitor.VisitPointer(MetaPointer type) { throw new NotImplementedException(); } void IMetaTypeVisitor.VisitStruct(MetaStruct type) { ReadFields(type.Fields); } void IMetaTypeVisitor.VisitArray(MetaArray type) { int count = ReadArray(type.ElementType, type.Count); if (count < type.Count) writer.Skip((type.Count - count) * type.ElementType.Size); } void IMetaTypeVisitor.VisitVarArray(MetaVarArray type) { int countFieldPosition = writer.Position; int count; if (type.CountField.Type == MetaType.Int16) { writer.WriteInt16(0); count = ReadArray(type.ElementType, UInt16.MaxValue); } else { writer.Write(0); count = ReadArray(type.ElementType, Int32.MaxValue); } int position = writer.Position; writer.Position = countFieldPosition; if (type.CountField.Type == MetaType.Int16) writer.WriteUInt16(count); else writer.Write(count); writer.Position = position; } #endregion private void ReadFields(IEnumerable fields) { xml.ReadStartElement(); xml.MoveToContent(); foreach (Field field in fields) { try { field.Type.Accept(this); } catch (Exception ex) { var lineInfo = xml as IXmlLineInfo; int line = lineInfo != null ? lineInfo.LineNumber : 0; throw new InvalidOperationException(string.Format("Cannot read field '{0}' (line {1})", field.Name, line), ex); } } xml.ReadEndElement(); } protected void ReadStruct(MetaStruct s) { foreach (Field field in s.Fields) { try { field.Type.Accept(this); } catch (Exception ex) { throw new InvalidOperationException(string.Format("Cannot read field '{0}'", field.Name), ex); } } } private int ReadArray(MetaType elementType, int maxCount) { if (xml.IsEmptyElement) { xml.Read(); return 0; } xml.ReadStartElement(); xml.MoveToContent(); var localName = xml.LocalName; int count = 0; for (; count < maxCount && xml.IsStartElement(localName); count++) { startOffsetStack.Push(writer.Position); elementType.Accept(this); startOffsetStack.Pop(); } xml.ReadEndElement(); return count; } } }