using System; using System.Collections.Generic; using System.Xml; using Oni.Metadata; namespace Oni.Xml { internal class ObjcXmlImporter : RawXmlImporter { private readonly Dictionary typeReaders = new Dictionary(); private int nextId; private ObjcXmlImporter(XmlReader xml, BinaryWriter writer) : base(xml, writer) { InitTypeReaders(typeReaders); } public static void Import(XmlReader xml, BinaryWriter writer) { var importer = new ObjcXmlImporter(xml, writer); importer.Import(); } private void Import() { Writer.Write(39); nextId = 1; while (Xml.IsStartElement()) { int objectStartPosition = Writer.Position; Writer.Write(0); BeginStruct(objectStartPosition); ReadObject(); Writer.Position = Utils.Align4(Writer.Position); int objectSize = Writer.Position - objectStartPosition - 4; Writer.WriteAt(objectStartPosition, objectSize); } Writer.Write(0); } private ObjectMetadata.TypeTag ReadObject() { var id = Xml.GetAttribute("Id"); int objectId = string.IsNullOrEmpty(id) ? nextId++ : XmlConvert.ToInt32(id); var objectTag = Xml.GetAttribute("Type"); if (objectTag == null) objectTag = Xml.LocalName; var objectType = MetaEnum.Parse(objectTag); Xml.ReadStartElement(); Xml.MoveToContent(); Writer.Write((int)objectType); Writer.Write(objectId); ObjectMetadata.Header.Accept(this); typeReaders[objectType](); Xml.ReadEndElement(); return objectType; } private void ReadCharacter() { ObjectMetadata.Character.Accept(this); } private void ReadCombatProfile() { ObjectMetadata.CombatProfile.Accept(this); } private void ReadConsole() { Xml.ReadStartElement(); Xml.MoveToContent(); ReadStruct(ObjectMetadata.Console); ReadEventList(); Xml.ReadEndElement(); } private void ReadDoor() { Xml.ReadStartElement(); Xml.MoveToContent(); ReadStruct(ObjectMetadata.Door); ReadEventList(); Xml.ReadEndElement(); } private void ReadFlag() { ObjectMetadata.Flag.Accept(this); } private void ReadFurniture() { ObjectMetadata.Furniture.Accept(this); } private struct MeleeMove { public int Type; public float[] Params; } private void ReadMeleeProfile() { Xml.ReadStartElement(); Xml.MoveToContent(); ReadStruct(ObjectMetadata.MeleeProfile); int countFieldsPosition = Writer.Position; Writer.Write(0); Writer.Write(0); Writer.Write(0); Writer.Write(0); int attackCount = 0; int evadeCount = 0; int maneuverCount = 0; var moves = new List(); attackCount = ReadMeleeTechniques("Attacks", moves); evadeCount = ReadMeleeTechniques("Evades", moves); maneuverCount = ReadMeleeTechniques("Maneuvers", moves); foreach (var move in moves) { Writer.Write(move.Type); Writer.Write(move.Params); } int oldPosition = Writer.Position; Writer.Position = countFieldsPosition; Writer.Write(attackCount); Writer.Write(evadeCount); Writer.Write(maneuverCount); Writer.Write(moves.Count); Writer.Position = oldPosition; Xml.ReadEndElement(); } private int ReadMeleeTechniques(string xmlName, List moves) { if (Xml.IsStartElement(xmlName) && Xml.IsEmptyElement) { Xml.Skip(); return 0; } Xml.ReadStartElement(xmlName); Xml.MoveToContent(); int techniqueCount = 0; for (; Xml.IsStartElement("Technique"); techniqueCount++) { Xml.ReadStartElement(); Xml.MoveToContent(); ReadStruct(ObjectMetadata.MeleeTechnique); int moveCountPosition = Writer.Position; Writer.Write(0); Writer.Write(moves.Count); int moveCount = 0; if (Xml.IsStartElement("Moves") && Xml.IsEmptyElement) { Xml.Skip(); } else { Xml.ReadStartElement("Moves"); Xml.MoveToContent(); for (; Xml.IsStartElement(); moveCount++) { ReadMeleeMove(moves); } Xml.ReadEndElement(); } Xml.ReadEndElement(); Writer.WriteAt(moveCountPosition, moveCount); } Xml.ReadEndElement(); return techniqueCount; } private void ReadMeleeMove(List moves) { var category = (ObjectMetadata.MeleeMoveCategory)Enum.Parse(typeof(ObjectMetadata.MeleeMoveCategory), Xml.LocalName); var typeText = Xml.GetAttribute("Type"); int type; var moveParams = new float[3]; switch (category) { default: case ObjectMetadata.MeleeMoveCategory.Attack: type = Convert.ToInt32(MetaEnum.Parse(typeText)); break; case ObjectMetadata.MeleeMoveCategory.Evade: type = Convert.ToInt32(MetaEnum.Parse(typeText)); break; case ObjectMetadata.MeleeMoveCategory.Throw: type = Convert.ToInt32(MetaEnum.Parse(typeText)); break; case ObjectMetadata.MeleeMoveCategory.Position: ObjectMetadata.MeleeMovePositionType positionType = MetaEnum.Parse(typeText); if ((ObjectMetadata.MeleeMovePositionType.RunForward <= positionType && positionType <= ObjectMetadata.MeleeMovePositionType.RunBack) || ObjectMetadata.MeleeMovePositionType.CloseForward <= positionType) { moveParams[0] = XmlConvert.ToSingle(Xml.GetAttribute("MinRunInDist")); moveParams[1] = XmlConvert.ToSingle(Xml.GetAttribute("MaxRunInDist")); moveParams[2] = XmlConvert.ToSingle(Xml.GetAttribute("ToleranceRange")); } type = Convert.ToInt32(positionType); break; case ObjectMetadata.MeleeMoveCategory.Maneuver: type = Convert.ToInt32(MetaEnum.Parse(typeText)); ObjectMetadata.MeleeMoveTypeInfo typeInfo = ObjectMetadata.MeleeMoveManeuverTypeInfo[type]; for (int k = 0; k < typeInfo.ParamNames.Length; k++) moveParams[k] = XmlConvert.ToSingle(Xml.GetAttribute(typeInfo.ParamNames[k])); break; } moves.Add(new MeleeMove() { Type = (((int)category) << 24) | (type & 0xffffff), Params = moveParams }); Xml.Skip(); } private void ReadNeutralBehavior() { Xml.ReadStartElement(); Xml.MoveToContent(); ReadStruct(ObjectMetadata.NeutralBehavior); int countFieldPosition = Writer.Position; Writer.WriteUInt16(0); ReadStruct(ObjectMetadata.NeutralBehaviorParams); Xml.ReadStartElement("DialogLines"); short count = 0; for (; Xml.IsStartElement("DialogLine"); count++) ObjectMetadata.NeutralBehaviorDialogLine.Accept(this); Xml.ReadEndElement(); Xml.ReadEndElement(); Writer.WriteAt(countFieldPosition, count); } private void ReadParticle() { ObjectMetadata.Particle.Accept(this); } private void ReadPatrolPath() { Xml.ReadStartElement(); Xml.MoveToContent(); ReadStruct(ObjectMetadata.PatrolPath); int lengthFieldPosition = Writer.Position; Writer.Write(0); ReadStruct(ObjectMetadata.PatrolPathInfo); int count = 0; bool isEmpty = Xml.IsEmptyElement; Xml.ReadStartElement("Points"); if (!isEmpty) { int loopStart = -1; for (; Xml.IsStartElement(); count++) { if (ReadPatrolPathPoint()) loopStart = count; } if (loopStart != -1) { if (loopStart == 0) { Writer.Write((int)ObjectMetadata.PatrolPathPointType.Loop); } else { Writer.Write((int)ObjectMetadata.PatrolPathPointType.LoopFrom); Writer.Write(loopStart); } if (Xml.NodeType == XmlNodeType.EndElement && Xml.LocalName == "Loop") Xml.ReadEndElement(); } Xml.ReadEndElement(); } Xml.ReadEndElement(); Writer.WriteAt(lengthFieldPosition, count); } private bool ReadPatrolPathPoint() { var pointType = MetaEnum.Parse(Xml.LocalName); switch (pointType) { case ObjectMetadata.PatrolPathPointType.Loop: if (Xml.IsEmptyElement) { Xml.Skip(); } else { Xml.ReadStartElement(); Xml.MoveToContent(); } return true; } Writer.Write((int)pointType); switch (pointType) { case ObjectMetadata.PatrolPathPointType.Stop: case ObjectMetadata.PatrolPathPointType.StopLooking: case ObjectMetadata.PatrolPathPointType.StopScanning: case ObjectMetadata.PatrolPathPointType.FreeFacing: break; case ObjectMetadata.PatrolPathPointType.IgnorePlayer: Writer.WriteByte(Xml.GetAttribute("Value") == "Yes" ? 1 : 0); break; case ObjectMetadata.PatrolPathPointType.ForkScript: case ObjectMetadata.PatrolPathPointType.CallScript: Writer.Write(XmlConvert.ToInt16(Xml.GetAttribute("ScriptId"))); break; case ObjectMetadata.PatrolPathPointType.MoveToFlag: case ObjectMetadata.PatrolPathPointType.LookAtFlag: case ObjectMetadata.PatrolPathPointType.MoveAndFaceFlag: Writer.Write(XmlConvert.ToInt16(Xml.GetAttribute("FlagId"))); break; case ObjectMetadata.PatrolPathPointType.MovementMode: Writer.Write(Convert.ToInt32(MetaEnum.Parse(Xml.GetAttribute("Mode")))); break; case ObjectMetadata.PatrolPathPointType.LockFacing: Writer.Write(Convert.ToInt32(MetaEnum.Parse(Xml.GetAttribute("Facing")))); break; case ObjectMetadata.PatrolPathPointType.Pause: Writer.Write(XmlConvert.ToInt32(Xml.GetAttribute("Frames"))); break; case ObjectMetadata.PatrolPathPointType.GlanceAtFlagFor: Writer.Write(XmlConvert.ToInt16(Xml.GetAttribute("FlagId"))); Writer.Write(XmlConvert.ToInt32(Xml.GetAttribute("Frames"))); break; case ObjectMetadata.PatrolPathPointType.Scan: Writer.Write(XmlConvert.ToInt16(Xml.GetAttribute("Frames"))); Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("Rotation"))); break; case ObjectMetadata.PatrolPathPointType.MoveThroughFlag: case ObjectMetadata.PatrolPathPointType.MoveNearFlag: Writer.Write(XmlConvert.ToInt16(Xml.GetAttribute("FlagId"))); Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("Distance"))); break; case ObjectMetadata.PatrolPathPointType.MoveToFlagLookAndWait: Writer.Write(XmlConvert.ToInt16(Xml.GetAttribute("Frames"))); Writer.Write(XmlConvert.ToInt16(Xml.GetAttribute("FlagId"))); Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("Rotation"))); break; case ObjectMetadata.PatrolPathPointType.FaceToFlagAndFire: Writer.Write(XmlConvert.ToInt16(Xml.GetAttribute("FlagId"))); Writer.Write(XmlConvert.ToInt16(Xml.GetAttribute("Frames"))); Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("Spread"))); break; case ObjectMetadata.PatrolPathPointType.LookAtPoint: case ObjectMetadata.PatrolPathPointType.MoveToPoint: Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("X"))); Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("Y"))); Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("Z"))); break; case ObjectMetadata.PatrolPathPointType.MoveThroughPoint: Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("X"))); Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("Y"))); Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("Z"))); Writer.Write(XmlConvert.ToSingle(Xml.GetAttribute("Distance"))); break; default: throw new NotSupportedException(string.Format("Unsupported path point type {0}", pointType)); } Xml.Skip(); return false; } private void ReadPowerUp() { ObjectMetadata.PowerUp.Accept(this); } private void ReadSound() { Xml.ReadStartElement(); Xml.MoveToContent(); ReadStruct(ObjectMetadata.Sound); var volumeType = MetaEnum.Parse(Xml.LocalName); Writer.Write((int)volumeType); switch (volumeType) { case ObjectMetadata.SoundVolumeType.Box: MetaType.BoundingBox.Accept(this); break; case ObjectMetadata.SoundVolumeType.Sphere: ObjectMetadata.SoundSphere.Accept(this); break; } ReadStruct(ObjectMetadata.SoundParams); if (volumeType == ObjectMetadata.SoundVolumeType.Sphere) Writer.Skip(16); Xml.ReadEndElement(); } private void ReadTriggerVolume() { ObjectMetadata.TriggerVolume.Accept(this); } private void ReadTrigger() { Xml.ReadStartElement(); Xml.MoveToContent(); ReadStruct(ObjectMetadata.Trigger); ReadEventList(); Xml.ReadEndElement(); } private void ReadTurret() { ObjectMetadata.Turret.Accept(this); } private void ReadWeapon() { ObjectMetadata.Weapon.Accept(this); } private void ReadEventList() { int countFieldPosition = Writer.Position; Writer.WriteUInt16(0); if (Xml.IsStartElement("Events") && Xml.IsEmptyElement) { Xml.ReadStartElement(); return; } Xml.ReadStartElement("Events"); Xml.MoveToContent(); short eventCount = 0; while (Xml.IsStartElement()) { var eventType = MetaEnum.Parse(Xml.Name); Writer.Write((short)eventType); switch (eventType) { case ObjectMetadata.EventType.None: break; case ObjectMetadata.EventType.Script: Writer.Write(Xml.GetAttribute("Function"), 32); break; default: Writer.Write(XmlConvert.ToInt16(Xml.GetAttribute("TargetId"))); break; } eventCount++; Xml.Skip(); } Writer.WriteAt(countFieldPosition, eventCount); Xml.ReadEndElement(); } private void InitTypeReaders(Dictionary typeReaders) { typeReaders.Add(ObjectMetadata.TypeTag.CHAR, ReadCharacter); typeReaders.Add(ObjectMetadata.TypeTag.CMBT, ReadCombatProfile); typeReaders.Add(ObjectMetadata.TypeTag.CONS, ReadConsole); typeReaders.Add(ObjectMetadata.TypeTag.DOOR, ReadDoor); typeReaders.Add(ObjectMetadata.TypeTag.FLAG, ReadFlag); typeReaders.Add(ObjectMetadata.TypeTag.FURN, ReadFurniture); typeReaders.Add(ObjectMetadata.TypeTag.MELE, ReadMeleeProfile); typeReaders.Add(ObjectMetadata.TypeTag.NEUT, ReadNeutralBehavior); typeReaders.Add(ObjectMetadata.TypeTag.PART, ReadParticle); typeReaders.Add(ObjectMetadata.TypeTag.PATR, ReadPatrolPath); typeReaders.Add(ObjectMetadata.TypeTag.PWRU, ReadPowerUp); typeReaders.Add(ObjectMetadata.TypeTag.SNDG, ReadSound); typeReaders.Add(ObjectMetadata.TypeTag.TRGV, ReadTriggerVolume); typeReaders.Add(ObjectMetadata.TypeTag.TRIG, ReadTrigger); typeReaders.Add(ObjectMetadata.TypeTag.TURR, ReadTurret); typeReaders.Add(ObjectMetadata.TypeTag.WEAP, ReadWeapon); } } }