internal void Parse(string fileName, TrainBase Train, ref UnifiedObject[] CarObjects, ref UnifiedObject[] BogieObjects, ref UnifiedObject[] CouplerObjects, out bool[] interiorVisible) { //The current XML file to load XmlDocument currentXML = new XmlDocument(); //Load the marker's XML file currentXML.Load(fileName); currentPath = System.IO.Path.GetDirectoryName(fileName); if (System.IO.File.Exists(OpenBveApi.Path.CombineFile(currentPath, "train.dat"))) { for (int i = 0; i < Train.Cars.Length; i++) { if (Train.Cars[i].Specs.IsMotorCar) { AccelerationCurves = new BveAccelerationCurve[Train.Cars[i].Specs.AccelerationCurves.Length]; for (int j = 0; j < Train.Cars[i].Specs.AccelerationCurves.Length; j++) { BveAccelerationCurve c = (BveAccelerationCurve)Train.Cars[i].Specs.AccelerationCurves[j]; AccelerationCurves[j] = c.Clone(c.Multiplier); } } } } CarObjectsReversed = new bool[Train.Cars.Length]; BogieObjectsReversed = new bool[Train.Cars.Length * 2]; interiorVisible = new bool[Train.Cars.Length]; if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Train/*[self::Car or self::Coupler]"); if (DocumentNodes == null || DocumentNodes.Count == 0) { Plugin.currentHost.AddMessage(MessageType.Error, false, "No car nodes defined in XML file " + fileName); //If we have no appropriate nodes specified, return false and fallback to loading the legacy Sound.cfg file throw new Exception("Empty train.xml file"); } int carIndex = 0; //Use the index here for easy access to the car count for (int i = 0; i < DocumentNodes.Count; i++) { if (carIndex > Train.Cars.Length) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "WARNING: A total of " + DocumentNodes.Count + " cars were specified in XML file " + fileName + " whilst only " + Train.Cars.Length + " were specified in the train.dat file."); break; } if (DocumentNodes[i].ChildNodes.OfType <XmlElement>().Any()) { if (DocumentNodes[i].Name == "Car") { ParseCarNode(DocumentNodes[i], fileName, carIndex, ref Train, ref CarObjects, ref BogieObjects, ref interiorVisible[carIndex]); } else { if (carIndex - 1 > Train.Cars.Length - 2) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Unexpected extra coupler encountered in XML file " + fileName); continue; } foreach (XmlNode c in DocumentNodes[i].ChildNodes) { switch (c.Name.ToLowerInvariant()) { case "minimum": if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out Train.Cars[carIndex - 1].Coupler.MinimumDistanceBetweenCars)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "MinimumDistanceBetweenCars is invalid for coupler " + carIndex + "in XML file " + fileName); } break; case "maximum": if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out Train.Cars[carIndex - 1].Coupler.MaximumDistanceBetweenCars)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "MaximumDistanceBetweenCars is invalid for coupler " + carIndex + "in XML file " + fileName); } break; case "object": if (string.IsNullOrEmpty(c.InnerText)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid object path for Coupler " + (carIndex - 1) + " in XML file " + fileName); break; } string f = OpenBveApi.Path.CombineFile(currentPath, c.InnerText); if (System.IO.File.Exists(f)) { Plugin.currentHost.LoadObject(f, Encoding.Default, out CouplerObjects[carIndex - 1]); } break; } } } } else if (!String.IsNullOrEmpty(DocumentNodes[i].InnerText)) { try { string childFile = OpenBveApi.Path.CombineFile(currentPath, DocumentNodes[i].InnerText); XmlDocument childXML = new XmlDocument(); childXML.Load(childFile); XmlNodeList childNodes = childXML.DocumentElement.SelectNodes("/openBVE/Car"); //We need to save and restore the current path to make relative paths within the child file work correctly string savedPath = currentPath; currentPath = System.IO.Path.GetDirectoryName(childFile); ParseCarNode(childNodes[0], fileName, i, ref Train, ref CarObjects, ref BogieObjects, ref interiorVisible[carIndex]); currentPath = savedPath; } catch { Plugin.currentHost.AddMessage(MessageType.Error, false, "Failed to load the child Car XML file specified in " + DocumentNodes[i].InnerText); } } if (i == DocumentNodes.Count && carIndex < Train.Cars.Length) { //If this is the case, the number of motor cars is the primary thing which may get confused.... //Not a lot to be done about this until a full replacement is built for the train.dat file & we can dump it entirely Plugin.currentHost.AddMessage(MessageType.Warning, false, "WARNING: The number of cars specified in the train.xml file does not match that in the train.dat- Some properties may be invalid."); } if (DocumentNodes[i].Name == "Car") { carIndex++; } } if (Train.Cars[Train.DriverCar].CameraRestrictionMode != CameraRestrictionMode.NotSpecified) { Plugin.Renderer.Camera.CurrentRestriction = Train.Cars[Train.DriverCar].CameraRestrictionMode; } DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Train/NotchDescriptions"); if (DocumentNodes != null && DocumentNodes.Count > 0) { //Optional section for (int i = 0; i < DocumentNodes.Count; i++) { if (DocumentNodes[i].ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode c in DocumentNodes[i].ChildNodes) { switch (c.Name.ToLowerInvariant()) { case "power": Train.Handles.Power.NotchDescriptions = c.InnerText.Split(';'); for (int j = 0; j < Train.Handles.Power.NotchDescriptions.Length; j++) { Size s = Plugin.Renderer.Fonts.NormalFont.MeasureString(Train.Handles.Power.NotchDescriptions[j]); if (s.Width > Train.Handles.Power.MaxWidth) { Train.Handles.Power.MaxWidth = s.Width; } } break; case "brake": Train.Handles.Brake.NotchDescriptions = c.InnerText.Split(';'); for (int j = 0; j < Train.Handles.Brake.NotchDescriptions.Length; j++) { Size s = Plugin.Renderer.Fonts.NormalFont.MeasureString(Train.Handles.Brake.NotchDescriptions[j]); if (s.Width > Train.Handles.Brake.MaxWidth) { Train.Handles.Brake.MaxWidth = s.Width; } } break; case "locobrake": if (Train.Handles.LocoBrake == null) { continue; } Train.Handles.LocoBrake.NotchDescriptions = c.InnerText.Split(';'); for (int j = 0; j < Train.Handles.LocoBrake.NotchDescriptions.Length; j++) { Size s = Plugin.Renderer.Fonts.NormalFont.MeasureString(Train.Handles.LocoBrake.NotchDescriptions[j]); if (s.Width > Train.Handles.LocoBrake.MaxWidth) { Train.Handles.LocoBrake.MaxWidth = s.Width; } } break; case "reverser": Train.Handles.Reverser.NotchDescriptions = c.InnerText.Split(';'); for (int j = 0; j < Train.Handles.Reverser.NotchDescriptions.Length; j++) { Size s = Plugin.Renderer.Fonts.NormalFont.MeasureString(Train.Handles.Reverser.NotchDescriptions[j]); if (s.Width > Train.Handles.Reverser.MaxWidth) { Train.Handles.Reverser.MaxWidth = s.Width; } } break; } } } } } for (int i = 0; i < Train.Cars.Length; i++) { if (CarObjects[i] != null) { if (CarObjectsReversed[i]) { { // reverse axle positions double temp = Train.Cars[i].FrontAxle.Position; Train.Cars[i].FrontAxle.Position = -Train.Cars[i].RearAxle.Position; Train.Cars[i].RearAxle.Position = -temp; } if (CarObjects[i] is StaticObject) { StaticObject obj = (StaticObject)CarObjects[i].Clone(); obj.ApplyScale(-1.0, 1.0, -1.0); CarObjects[i] = obj; } else if (CarObjects[i] is AnimatedObjectCollection) { AnimatedObjectCollection obj = (AnimatedObjectCollection)CarObjects[i].Clone(); obj.Reverse(); CarObjects[i] = obj; } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... for (int i = 0; i < Train.Cars.Length * 2; i++) { bool IsOdd = (i % 2 != 0); int CarIndex = i / 2; if (BogieObjects[i] != null) { if (BogieObjectsReversed[i]) { { // reverse axle positions if (IsOdd) { double temp = Train.Cars[CarIndex].FrontBogie.FrontAxle.Position; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = -Train.Cars[CarIndex].FrontBogie.RearAxle.Position; Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -temp; } else { double temp = Train.Cars[CarIndex].RearBogie.FrontAxle.Position; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = -Train.Cars[CarIndex].RearBogie.RearAxle.Position; Train.Cars[CarIndex].RearBogie.RearAxle.Position = -temp; } } if (BogieObjects[i] is StaticObject) { StaticObject obj = (StaticObject)BogieObjects[i].Clone(); obj.ApplyScale(-1.0, 1.0, -1.0); BogieObjects[i] = obj; } else if (BogieObjects[i] is AnimatedObjectCollection) { AnimatedObjectCollection obj = (AnimatedObjectCollection)BogieObjects[i].Clone(); obj.Reverse(); BogieObjects[i] = obj; } else { throw new NotImplementedException(); } } } } } }
private void ParseCarNode(XmlNode Node, string fileName, int Car, ref TrainBase Train, ref UnifiedObject[] CarObjects, ref UnifiedObject[] BogieObjects, ref bool visibleFromInterior) { string interiorFile = string.Empty; ReadhesionDeviceType readhesionDevice = Train.Cars[0].ReAdhesionDevice.DeviceType; bool CopyAccelerationCurves = true; foreach (XmlNode c in Node.ChildNodes) { //Note: Don't use the short-circuiting operator, as otherwise we need another if switch (c.Name.ToLowerInvariant()) { case "camerarestriction": Train.Cars[Car].CameraRestrictionMode = CameraRestrictionMode.Restricted3D; foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "backwards": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out Train.Cars[Car].CameraRestriction.BottomLeft.Z)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid backwards camera restriction defined for Car " + Car + " in XML file " + fileName); } break; case "forwards": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out Train.Cars[Car].CameraRestriction.TopRight.Z)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid forwards camera restriction defined for Car " + Car + " in XML file " + fileName); } break; case "left": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out Train.Cars[Car].CameraRestriction.BottomLeft.X)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid left camera restriction defined for Car " + Car + " in XML file " + fileName); } break; case "right": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out Train.Cars[Car].CameraRestriction.TopRight.X)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid right camera restriction defined for Car " + Car + " in XML file " + fileName); } break; case "down": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out Train.Cars[Car].CameraRestriction.BottomLeft.Y)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid down camera restriction defined for Car " + Car + " in XML file " + fileName); } break; case "up": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out Train.Cars[Car].CameraRestriction.TopRight.Y)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid up camera restriction defined for Car " + Car + " in XML file " + fileName); } break; } } break; case "brake": Train.Cars[Car].CarBrake.brakeType = BrakeType.Auxiliary; if (c.ChildNodes.OfType <XmlElement>().Any()) { ParseBrakeNode(c, fileName, Car, ref Train); } else if (!String.IsNullOrEmpty(c.InnerText)) { try { string childFile = OpenBveApi.Path.CombineFile(currentPath, c.InnerText); XmlDocument childXML = new XmlDocument(); childXML.Load(childFile); XmlNodeList childNodes = childXML.DocumentElement.SelectNodes("/openBVE/Brake"); //We need to save and restore the current path to make relative paths within the child file work correctly string savedPath = currentPath; currentPath = System.IO.Path.GetDirectoryName(childFile); ParseBrakeNode(childNodes[0], fileName, Car, ref Train); currentPath = savedPath; } catch { Plugin.currentHost.AddMessage(MessageType.Error, false, "Failed to load the child Brake XML file specified in " + c.InnerText); } } break; case "length": double l; if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out l) | l <= 0.0) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid length defined for Car " + Car + " in XML file " + fileName); break; } Train.Cars[Car].Length = l; break; case "width": double w; if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out w) | w <= 0.0) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid width defined for Car " + Car + " in XML file " + fileName); break; } Train.Cars[Car].Width = w; break; case "height": double h; if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out h) | h <= 0.0) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid height defined for Car " + Car + " in XML file " + fileName); break; } Train.Cars[Car].Height = h; break; case "motorcar": if (c.InnerText.ToLowerInvariant() == "1" || c.InnerText.ToLowerInvariant() == "true") { Train.Cars[Car].Specs.IsMotorCar = true; if (!CopyAccelerationCurves) { //We've already set the acceleration curves elsewhere in the XML, so don't copy the default ones break; } Train.Cars[Car].Specs.AccelerationCurves = new AccelerationCurve[AccelerationCurves.Length]; for (int i = 0; i < AccelerationCurves.Length; i++) { Train.Cars[Car].Specs.AccelerationCurves[i] = AccelerationCurves[i].Clone(AccelerationCurves[i].Multiplier); } } else { Train.Cars[Car].Specs.AccelerationCurves = new AccelerationCurve[] { }; Train.Cars[Car].Specs.IsMotorCar = false; } break; case "mass": double m; if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out m) | m <= 0.0) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid mass defined for Car " + Car + " in XML file " + fileName); break; } Train.Cars[Car].EmptyMass = m; Train.Cars[Car].CargoMass = 0; break; case "frontaxle": if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out Train.Cars[Car].FrontAxle.Position)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid front axle position defined for Car " + Car + " in XML file " + fileName); } break; case "rearaxle": if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out Train.Cars[Car].RearAxle.Position)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid rear axle position defined for Car " + Car + " in XML file " + fileName); } break; case "object": if (string.IsNullOrEmpty(c.InnerText)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid object path for Car " + Car + " in XML file " + fileName); break; } string f = OpenBveApi.Path.CombineFile(currentPath, c.InnerText); if (System.IO.File.Exists(f)) { Plugin.currentHost.LoadObject(f, Encoding.Default, out CarObjects[Car]); } break; case "reversed": int n; NumberFormats.TryParseIntVb6(c.InnerText, out n); if (n == 1 || c.InnerText.ToLowerInvariant() == "true") { CarObjectsReversed[Car] = true; } break; case "loadingsway": int nm; NumberFormats.TryParseIntVb6(c.InnerText, out nm); if (nm == 1 || c.InnerText.ToLowerInvariant() == "true") { Train.Cars[Car].EnableLoadingSway = true; } else { Train.Cars[Car].EnableLoadingSway = false; } break; case "frontbogie": if (c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "frontaxle": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out Train.Cars[Car].FrontBogie.FrontAxle.Position)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid front bogie, front axle position defined for Car " + Car + " in XML file " + fileName); } break; case "rearaxle": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out Train.Cars[Car].FrontBogie.RearAxle.Position)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid front bogie, rear axle position defined for Car " + Car + " in XML file " + fileName); } break; case "object": if (string.IsNullOrEmpty(cc.InnerText)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid front bogie object path for Car " + Car + " in XML file " + fileName); break; } string fb = OpenBveApi.Path.CombineFile(currentPath, cc.InnerText); if (System.IO.File.Exists(fb)) { Plugin.currentHost.LoadObject(fb, Encoding.Default, out BogieObjects[Car * 2]); } break; case "reversed": int nn; NumberFormats.TryParseIntVb6(cc.InnerText, out nn); if (cc.InnerText.ToLowerInvariant() == "true" || nn == 1) { BogieObjectsReversed[Car * 2] = true; } break; } } } break; case "rearbogie": if (c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "frontaxle": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out Train.Cars[Car].RearBogie.FrontAxle.Position)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid rear bogie, front axle position defined for Car " + Car + " in XML file " + fileName); } break; case "rearaxle": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out Train.Cars[Car].RearBogie.RearAxle.Position)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid rear bogie, rear axle position defined for Car " + Car + " in XML file " + fileName); } break; case "object": if (string.IsNullOrEmpty(cc.InnerText)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid rear bogie object path for Car " + Car + " in XML file " + fileName); break; } string fb = OpenBveApi.Path.CombineFile(currentPath, cc.InnerText); if (System.IO.File.Exists(fb)) { Plugin.currentHost.LoadObject(fb, Encoding.Default, out BogieObjects[Car * 2 + 1]); } break; case "reversed": int nn; NumberFormats.TryParseIntVb6(cc.InnerText, out nn); if (cc.InnerText.ToLowerInvariant() == "true" || nn == 1) { BogieObjectsReversed[Car * 2 + 1] = true; } break; } } } break; case "driverposition": string[] splitText = c.InnerText.Split(','); if (splitText.Length != 3) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Driver position must have three arguments for Car " + Car + " in XML file " + fileName); break; } Train.Cars[Car].Driver = new Vector3(); double driverZ; if (!NumberFormats.TryParseDoubleVb6(splitText[0], out Train.Cars[Car].Driver.X)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Driver position X was invalid for Car " + Car + " in XML file " + fileName); } if (!NumberFormats.TryParseDoubleVb6(splitText[1], out Train.Cars[Car].Driver.Y)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Driver position Y was invalid for Car " + Car + " in XML file " + fileName); } if (!NumberFormats.TryParseDoubleVb6(splitText[2], out driverZ)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Driver position X was invalid for Car " + Car + " in XML file " + fileName); } Train.Cars[Car].Driver.Z = 0.5 * Train.Cars[Car].Length + driverZ; break; case "interiorview": if (!Train.IsPlayerTrain) { break; } Train.Cars[Car].HasInteriorView = true; Train.Cars[Car].CarSections = new CarSection[1]; Train.Cars[Car].CarSections[0] = new CarSection(Plugin.currentHost, ObjectType.Overlay, true); string cv = OpenBveApi.Path.CombineFile(currentPath, c.InnerText); if (!System.IO.File.Exists(cv)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Interior view file was invalid for Car " + Car + " in XML file " + fileName); break; } interiorFile = cv; break; case "readhesiondevice": switch (c.InnerText.ToLowerInvariant()) { case "typea": case "a": readhesionDevice = ReadhesionDeviceType.TypeA; break; case "typeb": case "b": readhesionDevice = ReadhesionDeviceType.TypeB; break; case "typec": case "c": readhesionDevice = ReadhesionDeviceType.TypeC; break; case "typed": case "d": readhesionDevice = ReadhesionDeviceType.TypeD; break; default: readhesionDevice = ReadhesionDeviceType.NotFitted; break; } break; case "visiblefrominterior": if (c.InnerText.ToLowerInvariant() == "1" || c.InnerText.ToLowerInvariant() == "true") { visibleFromInterior = true; } break; case "soundtable": if (c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "bve5": string powerFreq = string.Empty, powerVol = string.Empty; string brakeFreq = string.Empty, brakeVol = string.Empty; foreach (XmlNode sc in cc.ChildNodes) { switch (sc.Name.ToLowerInvariant()) { case "powerfreq": powerFreq = OpenBveApi.Path.CombineFile(currentPath, sc.InnerText); break; case "powervol": powerVol = OpenBveApi.Path.CombineFile(currentPath, sc.InnerText); break; case "brakefreq": brakeFreq = OpenBveApi.Path.CombineFile(currentPath, sc.InnerText); break; case "brakevol": brakeVol = OpenBveApi.Path.CombineFile(currentPath, sc.InnerText); break; } } Train.Cars[Car].Sounds.Motor = Bve5MotorSoundTableParser.Parse(Train.Cars[Car], powerFreq, powerVol, brakeFreq, brakeVol); break; } } } break; case "accelerationcurves": CopyAccelerationCurves = false; if (c.ChildNodes.OfType <XmlElement>().Any()) { List <AccelerationCurve> accelerationCurves = new List <AccelerationCurve>(); foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "openbve": // don't support legacy BVE2 curves in XML, but at the same time specify that this is deliberately BVE4 / OpenBVE format BveAccelerationCurve curve = new BveAccelerationCurve(); foreach (XmlNode sc in cc.ChildNodes) { switch (sc.Name.ToLowerInvariant()) { case "stagezeroacceleration": if (!NumberFormats.TryParseDoubleVb6(sc.InnerText, out curve.StageZeroAcceleration)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Stage zero acceleration was invalid for curve " + accelerationCurves.Count + " in XML file " + fileName); } curve.StageZeroAcceleration *= 0.277777777777778; break; case "stageoneacceleration": if (!NumberFormats.TryParseDoubleVb6(sc.InnerText, out curve.StageOneAcceleration)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Stage one acceleration was invalid for curve " + accelerationCurves.Count + " in XML file " + fileName); } curve.StageOneAcceleration *= 0.277777777777778; break; case "stageonespeed": if (!NumberFormats.TryParseDoubleVb6(sc.InnerText, out curve.StageOneSpeed)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Stage one speed was invalid for curve " + accelerationCurves.Count + " in XML file " + fileName); } curve.StageOneSpeed *= 0.277777777777778; break; case "stagetwospeed": if (!NumberFormats.TryParseDoubleVb6(sc.InnerText, out curve.StageTwoSpeed)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Stage two speed was invalid for curve " + accelerationCurves.Count + " in XML file " + fileName); } curve.StageTwoSpeed *= 0.277777777777778; break; case "stagetwoexponent": if (!NumberFormats.TryParseDoubleVb6(sc.InnerText, out curve.StageTwoExponent)) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Stage two exponent was invalid for curve " + accelerationCurves.Count + " in XML file " + fileName); } break; } } accelerationCurves.Add(curve); break; } } Train.Cars[Car].Specs.AccelerationCurves = accelerationCurves.ToArray(); } break; } } /* * As there is no set order for XML tags to be presented in, these must be * done after the end of the loop * */ //Assign interior view if (interiorFile != String.Empty) { if (interiorFile.ToLowerInvariant().EndsWith(".xml")) { XDocument CurrentXML = XDocument.Load(interiorFile, LoadOptions.SetLineInfo); // Check for null if (CurrentXML.Root == null) { // We couldn't find any valid XML, so return false throw new System.IO.InvalidDataException(); } IEnumerable <XElement> DocumentElements = CurrentXML.Root.Elements("PanelAnimated"); if (DocumentElements != null && DocumentElements.Count() != 0) { string t = Train.TrainFolder; Train.TrainFolder = currentPath; Plugin.PanelAnimatedXmlParser.ParsePanelAnimatedXml(interiorFile, Train, Car); Train.TrainFolder = t; if (Train.Cars[Car].CameraRestrictionMode != CameraRestrictionMode.Restricted3D) { Train.Cars[Car].CameraRestrictionMode = CameraRestrictionMode.NotAvailable; } return; } DocumentElements = CurrentXML.Root.Elements("Panel"); if (DocumentElements != null && DocumentElements.Count() != 0) { string t = Train.TrainFolder; Train.TrainFolder = currentPath; Plugin.PanelXmlParser.ParsePanelXml(interiorFile, Train, Car); Train.TrainFolder = t; Train.Cars[Car].CameraRestrictionMode = CameraRestrictionMode.On; return; } } else if (interiorFile.ToLowerInvariant().EndsWith(".cfg")) { //Only supports panel2.cfg format Plugin.Panel2CfgParser.ParsePanel2Config(System.IO.Path.GetFileName(interiorFile), System.IO.Path.GetDirectoryName(interiorFile), Train.Cars[Train.DriverCar]); Train.Cars[Car].CameraRestrictionMode = CameraRestrictionMode.On; } else if (interiorFile.ToLowerInvariant().EndsWith(".animated")) { Plugin.currentHost.LoadObject(interiorFile, Encoding.UTF8, out var currentObject); var a = (AnimatedObjectCollection)currentObject; if (a != null) { try { for (int i = 0; i < a.Objects.Length; i++) { Plugin.currentHost.CreateDynamicObject(ref a.Objects[i].internalObject); } Train.Cars[Car].CarSections[0].Groups[0].Elements = a.Objects; if (Train.Cars[Car].CameraRestrictionMode != CameraRestrictionMode.Restricted3D) { Train.Cars[Car].CameraRestrictionMode = CameraRestrictionMode.NotAvailable; } } catch { Plugin.Cancel = true; } } } else { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Interior view file is not supported for Car " + Car + " in XML file " + fileName); } } Train.Cars[Car].ReAdhesionDevice = new CarReAdhesionDevice(Train.Cars[Car], readhesionDevice); }