using System; using System.Collections.Generic; using System.Globalization; using System.Xml; using Oni.Metadata; namespace Oni.Xml { internal class ObjcXmlExporter : RawXmlExporter { private readonly Dictionary typeWriters = new Dictionary(); private int objectEndPosition; private ObjcXmlExporter(BinaryReader reader, XmlWriter xml) : base(reader, xml) { InitTypeWriters(typeWriters); } public static void Export(BinaryReader reader, XmlWriter xml) { var exporter = new ObjcXmlExporter(reader, xml); exporter.Export(); } private void Export() { int size = Reader.ReadInt32(); int version = Reader.ReadInt32(); Xml.WriteStartElement("Objects"); while (true) { int objectSize = Reader.ReadInt32(); if (objectSize == 0) break; int readerPosition = Reader.Position; objectEndPosition = readerPosition + objectSize; BeginStruct(Reader.Position); var objectType = (ObjectMetadata.TypeTag)Reader.ReadInt32(); int objectId = Reader.ReadInt32(); Xml.WriteStartElement(objectType.ToString()); Xml.WriteAttributeString("Id", XmlConvert.ToString(objectId)); Xml.WriteStartElement("Header"); ObjectMetadata.Header.Accept(this); Xml.WriteEndElement(); Xml.WriteStartElement("OSD"); typeWriters[objectType](); Xml.WriteEndElement(); Xml.WriteEndElement(); Reader.Position = objectEndPosition; } Xml.WriteEndElement(); } private void WriteCharacter() { ObjectMetadata.Character.Accept(this); } private void WriteCombatProfile() { ObjectMetadata.CombatProfile.Accept(this); } private void WriteConsole() { ObjectMetadata.Console.Accept(this); WriteEventList(); } private void WriteDoor() { ObjectMetadata.Door.Accept(this); WriteEventList(); } private void WriteFlag() { ObjectMetadata.Flag.Accept(this); } private void WriteFurniture() { ObjectMetadata.Furniture.Accept(this); } private void WriteMeleeProfile() { ObjectMetadata.MeleeProfile.Accept(this); int attackCount = Reader.ReadInt32(); int evadeCount = Reader.ReadInt32(); int maneuverCount = Reader.ReadInt32(); int moveCount = Reader.ReadInt32(); int moveTablePosition = Reader.Position + (attackCount + evadeCount + maneuverCount) * 88; Xml.WriteStartElement("Attacks"); for (int i = 0; i < attackCount; i++) WriteMeleeTechnique(moveTablePosition); Xml.WriteEndElement(); Xml.WriteStartElement("Evades"); for (int i = 0; i < evadeCount; i++) WriteMeleeTechnique(moveTablePosition); Xml.WriteEndElement(); Xml.WriteStartElement("Maneuvers"); for (int i = 0; i < maneuverCount; i++) WriteMeleeTechnique(moveTablePosition); Xml.WriteEndElement(); } private void WriteMeleeTechnique(int moveTablePosition) { Xml.WriteStartElement("Technique"); ObjectMetadata.MeleeTechnique.Accept(this); int moveCount = Reader.ReadInt32(); int moveStart = Reader.ReadInt32(); int oldPosition = Reader.Position; Reader.Position = moveTablePosition + moveStart * 16; Xml.WriteStartElement("Moves"); for (int j = 0; j < moveCount; j++) WriteMeleeMove(); Xml.WriteEndElement(); Xml.WriteEndElement(); Reader.Position = oldPosition; } private void WriteMeleeMove() { int categoryType = Reader.ReadInt32(); float[] moveParams = Reader.ReadSingleArray(3); var category = (ObjectMetadata.MeleeMoveCategory)(categoryType >> 24); Xml.WriteStartElement(category.ToString()); switch (category) { default: case ObjectMetadata.MeleeMoveCategory.Attack: Xml.WriteAttributeString("Type", ((ObjectMetadata.MeleeMoveAttackType)(categoryType & 0xffffff)).ToString()); break; case ObjectMetadata.MeleeMoveCategory.Evade: Xml.WriteAttributeString("Type", ((ObjectMetadata.MeleeMoveEvadeType)(categoryType & 0xffffff)).ToString()); break; case ObjectMetadata.MeleeMoveCategory.Throw: Xml.WriteAttributeString("Type", ((ObjectMetadata.MeleeMoveThrowType)(categoryType & 0xffffff)).ToString()); break; case ObjectMetadata.MeleeMoveCategory.Maneuver: ObjectMetadata.MeleeMoveTypeInfo typeInfo = ObjectMetadata.MeleeMoveManeuverTypeInfo[categoryType & 0xffffff]; Xml.WriteAttributeString("Type", typeInfo.Type.ToString()); for (int k = 0; k < typeInfo.ParamNames.Length; k++) Xml.WriteAttributeString(typeInfo.ParamNames[k], XmlConvert.ToString(moveParams[k])); break; case ObjectMetadata.MeleeMoveCategory.Position: ObjectMetadata.MeleeMovePositionType moveType = (ObjectMetadata.MeleeMovePositionType)(categoryType & 0xffffff); Xml.WriteAttributeString("Type", moveType.ToString()); if ((ObjectMetadata.MeleeMovePositionType.RunForward <= moveType && moveType <= ObjectMetadata.MeleeMovePositionType.RunBack) || ObjectMetadata.MeleeMovePositionType.CloseForward <= moveType) { Xml.WriteAttributeString("MinRunInDist", XmlConvert.ToString(moveParams[0])); Xml.WriteAttributeString("MaxRunInDist", XmlConvert.ToString(moveParams[1])); Xml.WriteAttributeString("ToleranceRange", XmlConvert.ToString(moveParams[2])); } break; } Xml.WriteEndElement(); } private void WriteNeutralBehavior() { ObjectMetadata.NeutralBehavior.Accept(this); int lineCount = Reader.ReadInt16(); ObjectMetadata.NeutralBehaviorParams.Accept(this); Xml.WriteStartElement("DialogLines"); MetaType.Array(lineCount, ObjectMetadata.NeutralBehaviorDialogLine).Accept(this); Xml.WriteEndElement(); } private void WriteParticle() { ObjectMetadata.Particle.Accept(this); } private void WritePatrolPath() { ObjectMetadata.PatrolPath.Accept(this); int length = Reader.ReadInt32(); ObjectMetadata.PatrolPathInfo.Accept(this); int startPosition = Reader.Position; int loopStartPoint = -1; var pointType = (ObjectMetadata.PatrolPathPointType)Reader.ReadInt32(); for (int i = 0; i < length; i++) { if (pointType == ObjectMetadata.PatrolPathPointType.Loop) loopStartPoint = 0; else if (pointType == ObjectMetadata.PatrolPathPointType.LoopFrom) loopStartPoint = Reader.ReadInt32(); else Reader.Position += ObjectMetadata.GetPatrolPathPointSize(pointType); pointType = (ObjectMetadata.PatrolPathPointType)Reader.ReadInt32(); } Reader.Position = startPosition; Xml.WriteStartElement("Points"); for (int i = 0; i < length; i++) { if (loopStartPoint == i) Xml.WriteStartElement("Loop"); WritePatrolPathPoint(); } if (loopStartPoint != -1) Xml.WriteEndElement(); Xml.WriteEndElement(); } private void WritePatrolPathPoint() { var pointType = (ObjectMetadata.PatrolPathPointType)Reader.ReadInt32(); switch (pointType) { case ObjectMetadata.PatrolPathPointType.Loop: return; case ObjectMetadata.PatrolPathPointType.LoopFrom: Reader.Skip(4); return; } Xml.WriteStartElement(pointType.ToString()); switch (pointType) { case ObjectMetadata.PatrolPathPointType.Stop: case ObjectMetadata.PatrolPathPointType.StopLooking: case ObjectMetadata.PatrolPathPointType.StopScanning: case ObjectMetadata.PatrolPathPointType.FreeFacing: break; case ObjectMetadata.PatrolPathPointType.IgnorePlayer: Xml.WriteAttributeString("Value", Reader.ReadBoolean() ? "Yes" : "No"); break; case ObjectMetadata.PatrolPathPointType.MoveToFlag: case ObjectMetadata.PatrolPathPointType.LookAtFlag: case ObjectMetadata.PatrolPathPointType.MoveAndFaceFlag: Xml.WriteAttributeString("FlagId", XmlConvert.ToString(Reader.ReadInt16())); break; case ObjectMetadata.PatrolPathPointType.ForkScript: case ObjectMetadata.PatrolPathPointType.CallScript: Xml.WriteAttributeString("ScriptId", XmlConvert.ToString(Reader.ReadInt16())); break; case ObjectMetadata.PatrolPathPointType.Pause: Xml.WriteAttributeString("Frames", XmlConvert.ToString(Reader.ReadInt32())); break; case ObjectMetadata.PatrolPathPointType.MovementMode: Xml.WriteAttributeString("Mode", ((ObjectMetadata.PatrolPathMovementMode)Reader.ReadInt32()).ToString()); break; case ObjectMetadata.PatrolPathPointType.LockFacing: Xml.WriteAttributeString("Facing", ((ObjectMetadata.PatrolPathFacing)Reader.ReadInt32()).ToString()); break; case ObjectMetadata.PatrolPathPointType.MoveThroughFlag: case ObjectMetadata.PatrolPathPointType.MoveNearFlag: Xml.WriteAttributeString("FlagId", XmlConvert.ToString(Reader.ReadInt16())); Xml.WriteAttributeString("Distance", XmlConvert.ToString(Reader.ReadSingle())); break; case ObjectMetadata.PatrolPathPointType.GlanceAtFlagFor: Xml.WriteAttributeString("FlagId", XmlConvert.ToString(Reader.ReadInt16())); Xml.WriteAttributeString("Frames", XmlConvert.ToString(Reader.ReadInt32())); break; case ObjectMetadata.PatrolPathPointType.Scan: Xml.WriteAttributeString("Frames", XmlConvert.ToString(Reader.ReadInt16())); Xml.WriteAttributeString("Rotation", XmlConvert.ToString(Reader.ReadSingle())); break; case ObjectMetadata.PatrolPathPointType.MoveToFlagLookAndWait: Xml.WriteAttributeString("Frames", XmlConvert.ToString(Reader.ReadInt16())); Xml.WriteAttributeString("FlagId", XmlConvert.ToString(Reader.ReadInt16())); Xml.WriteAttributeString("Rotation", XmlConvert.ToString(Reader.ReadSingle())); break; case ObjectMetadata.PatrolPathPointType.FaceToFlagAndFire: Xml.WriteAttributeString("FlagId", XmlConvert.ToString(Reader.ReadInt16())); Xml.WriteAttributeString("Frames", XmlConvert.ToString(Reader.ReadInt16())); Xml.WriteAttributeString("Spread", XmlConvert.ToString(Reader.ReadSingle())); break; case ObjectMetadata.PatrolPathPointType.LookAtPoint: case ObjectMetadata.PatrolPathPointType.MoveToPoint: Xml.WriteAttributeString("X", XmlConvert.ToString(Reader.ReadSingle())); Xml.WriteAttributeString("Y", XmlConvert.ToString(Reader.ReadSingle())); Xml.WriteAttributeString("Z", XmlConvert.ToString(Reader.ReadSingle())); break; case ObjectMetadata.PatrolPathPointType.MoveThroughPoint: Xml.WriteAttributeString("X", XmlConvert.ToString(Reader.ReadSingle())); Xml.WriteAttributeString("Y", XmlConvert.ToString(Reader.ReadSingle())); Xml.WriteAttributeString("Z", XmlConvert.ToString(Reader.ReadSingle())); Xml.WriteAttributeString("Distance", XmlConvert.ToString(Reader.ReadSingle())); break; default: throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Unsupported path point type {0}", pointType)); } Xml.WriteEndElement(); } private void WritePowerUp() { ObjectMetadata.PowerUp.Accept(this); } private void WriteSound() { ObjectMetadata.Sound.Accept(this); var volumeType = (ObjectMetadata.SoundVolumeType)Reader.ReadInt32(); switch (volumeType) { case ObjectMetadata.SoundVolumeType.Box: Xml.WriteStartElement("Box"); MetaType.BoundingBox.Accept(this); Xml.WriteEndElement(); break; case ObjectMetadata.SoundVolumeType.Sphere: Xml.WriteStartElement("Sphere"); ObjectMetadata.SoundSphere.Accept(this); Xml.WriteEndElement(); break; } ObjectMetadata.SoundParams.Accept(this); } private void WriteTriggerVolume() { ObjectMetadata.TriggerVolume.Accept(this); } private void WriteTrigger() { ObjectMetadata.Trigger.Accept(this); WriteEventList(); } private void WriteTurret() { ObjectMetadata.Turret.Accept(this); } private void WriteWeapon() { ObjectMetadata.Weapon.Accept(this); } private void WriteEventList() { Xml.WriteStartElement("Events"); int count = Reader.ReadInt16(); for (int i = 0; i < count; i++) { var eventType = (ObjectMetadata.EventType)Reader.ReadInt16(); Xml.WriteStartElement(eventType.ToString()); switch (eventType) { case ObjectMetadata.EventType.None: break; case ObjectMetadata.EventType.Script: Xml.WriteAttributeString("Function", Reader.ReadString(32)); break; default: Xml.WriteAttributeString("TargetId", XmlConvert.ToString(Reader.ReadInt16())); break; } Xml.WriteEndElement(); } Xml.WriteEndElement(); } private void InitTypeWriters(Dictionary typeWriters) { typeWriters.Add(ObjectMetadata.TypeTag.CHAR, WriteCharacter); typeWriters.Add(ObjectMetadata.TypeTag.CMBT, WriteCombatProfile); typeWriters.Add(ObjectMetadata.TypeTag.CONS, WriteConsole); typeWriters.Add(ObjectMetadata.TypeTag.DOOR, WriteDoor); typeWriters.Add(ObjectMetadata.TypeTag.FLAG, WriteFlag); typeWriters.Add(ObjectMetadata.TypeTag.FURN, WriteFurniture); typeWriters.Add(ObjectMetadata.TypeTag.MELE, WriteMeleeProfile); typeWriters.Add(ObjectMetadata.TypeTag.NEUT, WriteNeutralBehavior); typeWriters.Add(ObjectMetadata.TypeTag.PART, WriteParticle); typeWriters.Add(ObjectMetadata.TypeTag.PATR, WritePatrolPath); typeWriters.Add(ObjectMetadata.TypeTag.PWRU, WritePowerUp); typeWriters.Add(ObjectMetadata.TypeTag.SNDG, WriteSound); typeWriters.Add(ObjectMetadata.TypeTag.TRGV, WriteTriggerVolume); typeWriters.Add(ObjectMetadata.TypeTag.TRIG, WriteTrigger); typeWriters.Add(ObjectMetadata.TypeTag.TURR, WriteTurret); typeWriters.Add(ObjectMetadata.TypeTag.WEAP, WriteWeapon); } } }