internal static void Parse(string fileName, TrainManager.Train Train, ref UnifiedObject[] CarObjects, ref UnifiedObject[] BogieObjects, ref UnifiedObject[] CouplerObjects) { //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 TrainManager.BveAccelerationCurve[Train.Cars[i].Specs.AccelerationCurves.Length]; for (int j = 0; j < Train.Cars[i].Specs.AccelerationCurves.Length; j++) { TrainManager.BveAccelerationCurve c = (TrainManager.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]; if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Train/*[self::Car or self::Coupler]"); if (DocumentNodes == null || DocumentNodes.Count == 0) { Interface.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) { Interface.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); } else { if (carIndex - 1 > Train.Cars.Length - 2) { Interface.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)) { Interface.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)) { Interface.AddMessage(MessageType.Error, false, "MaximumDistanceBetweenCars is invalid for coupler " + carIndex + "in XML file " + fileName); } break; case "object": if (string.IsNullOrEmpty(c.InnerText)) { Interface.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)) { Program.CurrentHost.LoadObject(f, System.Text.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); currentPath = savedPath; } catch { Interface.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 Interface.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) { Program.Renderer.Camera.CurrentRestriction = Train.Cars[Train.DriverCar].CameraRestrictionMode; Program.Renderer.UpdateViewingDistances(Program.CurrentRoute.CurrentBackground.BackgroundImageDistance); } 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.PowerNotchDescriptions = c.InnerText.Split(new char[] { ';' }); for (int j = 0; j < Train.PowerNotchDescriptions.Length; j++) { Size s = Fonts.NormalFont.MeasureString(Train.PowerNotchDescriptions[j]); if (s.Width > Train.MaxPowerNotchWidth) { Train.MaxPowerNotchWidth = s.Width; } } break; case "brake": Train.BrakeNotchDescriptions = c.InnerText.Split(new char[] { ';' }); for (int j = 0; j < Train.BrakeNotchDescriptions.Length; j++) { Size s = Fonts.NormalFont.MeasureString(Train.BrakeNotchDescriptions[j]); if (s.Width > Train.MaxBrakeNotchWidth) { Train.MaxBrakeNotchWidth = s.Width; } } break; case "locobrake": Train.LocoBrakeNotchDescriptions = c.InnerText.Split(new char[] { ';' }); for (int j = 0; j < Train.LocoBrakeNotchDescriptions.Length; j++) { Size s = Fonts.NormalFont.MeasureString(Train.LocoBrakeNotchDescriptions[j]); if (s.Width > Train.MaxLocoBrakeNotchWidth) { Train.MaxLocoBrakeNotchWidth = s.Width; } } break; case "reverser": Train.ReverserDescriptions = c.InnerText.Split(new char[] { ';' }); for (int j = 0; j < Train.ReverserDescriptions.Length; j++) { Size s = Fonts.NormalFont.MeasureString(Train.ReverserDescriptions[j]); if (s.Width > Train.MaxReverserWidth) { Train.MaxReverserWidth = 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]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (CarObjects[i] is AnimatedObjectCollection) { AnimatedObjectCollection obj = (AnimatedObjectCollection)CarObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Prototype.ApplyScale(-1.0, 1.0, -1.0); Matrix4D t = obj.Objects[j].States[h].Translation; t.Row3.X *= -1.0f; t.Row3.Z *= -1.0f; obj.Objects[j].States[h].Translation = t; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int bogieObjects = 0; for (int i = 0; i < Train.Cars.Length * 2; i++) { bool IsOdd = (i % 2 != 0); int CarIndex = i / 2; if (BogieObjects[i] != null) { bogieObjects++; 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]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (BogieObjects[i] is AnimatedObjectCollection) { AnimatedObjectCollection obj = (AnimatedObjectCollection)BogieObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Prototype.ApplyScale(-1.0, 1.0, -1.0); Matrix4D t = obj.Objects[j].States[h].Translation; t.Row3.X *= -1.0f; t.Row3.Z *= -1.0f; obj.Objects[j].States[h].Translation = t; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } } }
private static void ParseCarNode(XmlNode Node, string fileName, int Car, ref TrainManager.Train Train, ref UnifiedObject[] CarObjects, ref UnifiedObject[] BogieObjects) { string interiorFile = string.Empty; TrainManager.ReadhesionDeviceType readhesionDevice = Train.Specs.ReadhesionDeviceType; 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 "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 { Interface.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) { Interface.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) { Interface.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) { Interface.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; Train.Cars[Car].Specs.AccelerationCurves = new TrainManager.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 TrainManager.AccelerationCurve[] { }; Train.Cars[Car].Specs.IsMotorCar = false; Train.Cars[Car].Specs.ReAdhesionDevice = new TrainManager.CarReAdhesionDevice(Train.Cars[Car]); } break; case "mass": double m; if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out m) | m <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid mass defined for Car " + Car + " in XML file " + fileName); break; } Train.Cars[Car].Specs.MassEmpty = m; Train.Cars[Car].Specs.MassCurrent = m; break; case "frontaxle": if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out Train.Cars[Car].FrontAxle.Position)) { Interface.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)) { Interface.AddMessage(MessageType.Warning, false, "Invalid rear axle position defined for Car " + Car + " in XML file " + fileName); } break; case "object": if (string.IsNullOrEmpty(c.InnerText)) { Interface.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)) { CarObjects[Car] = ObjectManager.LoadObject(f, System.Text.Encoding.Default, false, false, false); } break; case "reversed": int n; NumberFormats.TryParseIntVb6(c.InnerText, out n); if (n == 1 || c.InnerText.ToLowerInvariant() == "true") { CarObjectsReversed[Car] = true; } 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)) { Interface.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)) { Interface.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)) { Interface.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)) { BogieObjects[Car * 2] = ObjectManager.LoadObject(fb, System.Text.Encoding.Default, false, false, false); } 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)) { Interface.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)) { Interface.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)) { Interface.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)) { BogieObjects[Car * 2 + 1] = ObjectManager.LoadObject(fb, System.Text.Encoding.Default, false, false, false); } 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) { Interface.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)) { Interface.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)) { Interface.AddMessage(MessageType.Warning, false, "Driver position Y was invalid for Car " + Car + " in XML file " + fileName); } if (!NumberFormats.TryParseDoubleVb6(splitText[2], out driverZ)) { Interface.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 != TrainManager.PlayerTrain) { break; } Train.Cars[Car].HasInteriorView = true; Train.Cars[Car].CarSections = new TrainManager.CarSection[1]; Train.Cars[Car].CarSections[0] = new TrainManager.CarSection { Groups = new TrainManager.ElementsGroup[1] }; Train.Cars[Car].CarSections[0].Groups[0] = new TrainManager.ElementsGroup { Elements = new ObjectManager.AnimatedObject[] { }, Overlay = true }; string cv = OpenBveApi.Path.CombineFile(currentPath, c.InnerText); if (!System.IO.File.Exists(cv)) { Interface.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 = TrainManager.ReadhesionDeviceType.TypeA; break; case "typeb": case "b": readhesionDevice = TrainManager.ReadhesionDeviceType.TypeB; break; case "typec": case "c": readhesionDevice = TrainManager.ReadhesionDeviceType.TypeC; break; case "typed": case "d": readhesionDevice = TrainManager.ReadhesionDeviceType.TypeD; break; default: readhesionDevice = TrainManager.ReadhesionDeviceType.NotFitted; break; } 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) { PanelAnimatedXmlParser.ParsePanelAnimatedXml(interiorFile, currentPath, Train, Car); Train.Cars[Car].CameraRestrictionMode = Camera.RestrictionMode.NotAvailable; return; } DocumentElements = CurrentXML.Root.Elements("Panel"); if (DocumentElements != null && DocumentElements.Count() != 0) { PanelXmlParser.ParsePanelXml(interiorFile, currentPath, Train, Car); Train.Cars[Car].CameraRestrictionMode = Camera.RestrictionMode.On; return; } } else if (interiorFile.ToLowerInvariant().EndsWith(".cfg")) { //Only supports panel2.cfg format Panel2CfgParser.ParsePanel2Config(System.IO.Path.GetFileName(interiorFile), System.IO.Path.GetDirectoryName(interiorFile), Encoding.UTF8, Train, Car); Train.Cars[Car].CameraRestrictionMode = Camera.RestrictionMode.On; } else if (interiorFile.ToLowerInvariant().EndsWith(".animated")) { ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(interiorFile, Encoding.UTF8); try { for (int i = 0; i < a.Objects.Length; i++) { a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject(); } Train.Cars[Car].CarSections[0].Groups[0].Elements = a.Objects; Train.Cars[Car].CameraRestrictionMode = Camera.RestrictionMode.NotAvailable; } catch { Program.RestartArguments = " "; Loading.Cancel = true; } } else { Interface.AddMessage(MessageType.Warning, false, "Interior view file is not supported for Car " + Car + " in XML file " + fileName); } } //Assign readhesion device properties if (Train.Cars[Car].Specs.IsMotorCar) { switch (readhesionDevice) { case TrainManager.ReadhesionDeviceType.TypeA: Train.Cars[Car].Specs.ReAdhesionDevice.UpdateInterval = 1.0; Train.Cars[Car].Specs.ReAdhesionDevice.ApplicationFactor = 0.0; Train.Cars[Car].Specs.ReAdhesionDevice.ReleaseInterval = 1.0; Train.Cars[Car].Specs.ReAdhesionDevice.ReleaseFactor = 8.0; break; case TrainManager.ReadhesionDeviceType.TypeB: Train.Cars[Car].Specs.ReAdhesionDevice.UpdateInterval = 0.1; Train.Cars[Car].Specs.ReAdhesionDevice.ApplicationFactor = 0.9935; Train.Cars[Car].Specs.ReAdhesionDevice.ReleaseInterval = 4.0; Train.Cars[Car].Specs.ReAdhesionDevice.ReleaseFactor = 1.125; break; case TrainManager.ReadhesionDeviceType.TypeC: Train.Cars[Car].Specs.ReAdhesionDevice.UpdateInterval = 0.1; Train.Cars[Car].Specs.ReAdhesionDevice.ApplicationFactor = 0.965; Train.Cars[Car].Specs.ReAdhesionDevice.ReleaseInterval = 2.0; Train.Cars[Car].Specs.ReAdhesionDevice.ReleaseFactor = 1.5; break; case TrainManager.ReadhesionDeviceType.TypeD: Train.Cars[Car].Specs.ReAdhesionDevice.UpdateInterval = 0.05; Train.Cars[Car].Specs.ReAdhesionDevice.ApplicationFactor = 0.935; Train.Cars[Car].Specs.ReAdhesionDevice.ReleaseInterval = 0.3; Train.Cars[Car].Specs.ReAdhesionDevice.ReleaseFactor = 2.0; break; default: // no readhesion device Train.Cars[Car].Specs.ReAdhesionDevice.UpdateInterval = 1.0; Train.Cars[Car].Specs.ReAdhesionDevice.ApplicationFactor = 1.0; Train.Cars[Car].Specs.ReAdhesionDevice.ReleaseInterval = 1.0; Train.Cars[Car].Specs.ReAdhesionDevice.ReleaseFactor = 99.0; break; } } }
private static void ParseCarNode(XmlNode Node, string fileName, int Car, ref TrainManager.Train Train, ref ObjectManager.UnifiedObject[] CarObjects, ref ObjectManager.UnifiedObject[] BogieObjects) { double driverZ = 0.0; string interiorFile = string.Empty; 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 "length": double l; if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out l) | l <= 0.0) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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) { Interface.AddMessage(Interface.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; } else { Train.Cars[Car].Specs.IsMotorCar = false; } break; case "mass": double m; if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out m) | m <= 0.0) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid mass defined for Car " + Car + " in XML file " + fileName); break; } Train.Cars[Car].Specs.MassEmpty = m; Train.Cars[Car].Specs.MassCurrent = m; break; case "frontaxle": if (!NumberFormats.TryParseDoubleVb6(c.InnerText, out Train.Cars[Car].FrontAxle.Position)) { Interface.AddMessage(Interface.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)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid rear axle position defined for Car " + Car + " in XML file " + fileName); } break; case "object": string f = OpenBveApi.Path.CombineFile(currentPath, c.InnerText); if (System.IO.File.Exists(f)) { CarObjects[Car] = ObjectManager.LoadObject(f, System.Text.Encoding.Default, ObjectManager.ObjectLoadMode.Normal, false, false, false); } break; case "reversed": if (c.InnerText.ToLowerInvariant() == "1" || c.InnerText.ToLowerInvariant() == "true") { CarObjectsReversed[Car] = true; } 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)) { Interface.AddMessage(Interface.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)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid front bogie, rear axle position defined for Car " + Car + " in XML file " + fileName); } break; case "object": string fb = OpenBveApi.Path.CombineFile(currentPath, cc.InnerText); if (System.IO.File.Exists(fb)) { BogieObjects[Car * 2] = ObjectManager.LoadObject(fb, System.Text.Encoding.Default, ObjectManager.ObjectLoadMode.Normal, false, false, false); } break; case "reversed": 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)) { Interface.AddMessage(Interface.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)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Invalid rear bogie, rear axle position defined for Car " + Car + " in XML file " + fileName); } break; case "object": string fb = OpenBveApi.Path.CombineFile(currentPath, cc.InnerText); if (System.IO.File.Exists(fb)) { BogieObjects[Car * 2 + 1] = ObjectManager.LoadObject(fb, System.Text.Encoding.Default, ObjectManager.ObjectLoadMode.Normal, false, false, false); } break; case "reversed": BogieObjectsReversed[Car * 2 + 1] = true; break; } } } break; case "driverposition": string[] splitText = c.InnerText.Split(','); if (splitText.Length != 3) { Interface.AddMessage(Interface.MessageType.Warning, false, "Driver position must have three arguments for Car " + Car + " in XML file " + fileName); break; } Train.Cars[Car].Driver = new Vector3(); if (!NumberFormats.TryParseDoubleVb6(splitText[0], out Train.Cars[Car].Driver.X)) { Interface.AddMessage(Interface.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)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Driver position Y was invalid for Car " + Car + " in XML file " + fileName); } if (!NumberFormats.TryParseDoubleVb6(splitText[2], out driverZ)) { Interface.AddMessage(Interface.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 != TrainManager.PlayerTrain) { break; } Train.Cars[Car].HasInteriorView = true; if (Car != Train.DriverCar) { Train.Cars[Car].CarSections = new TrainManager.CarSection[1]; Train.Cars[Car].CarSections[0] = new TrainManager.CarSection(); Train.Cars[Car].CarSections[0].Elements = new ObjectManager.AnimatedObject[] { }; Train.Cars[Car].CarSections[0].Overlay = true; } string cv = OpenBveApi.Path.CombineFile(currentPath, c.InnerText); if (!System.IO.File.Exists(cv)) { Interface.AddMessage(Interface.MessageType.Warning, false, "Interior view file was invalid for Car " + Car + " in XML file " + fileName); break; } interiorFile = cv; break; } } //Driver position is measured from the front of the car //As there is no set order, this needs to be done after the loop if (interiorFile != String.Empty) { if (interiorFile.ToLowerInvariant().EndsWith(".cfg")) { //Only supports panel2.cfg format Panel2CfgParser.ParsePanel2Config(System.IO.Path.GetFileName(interiorFile), System.IO.Path.GetDirectoryName(interiorFile), Encoding.UTF8, Train, Car); Train.Cars[Car].CameraRestrictionMode = World.CameraRestrictionMode.On; } else if (interiorFile.ToLowerInvariant().EndsWith(".animated")) { ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(interiorFile, Encoding.UTF8, ObjectManager.ObjectLoadMode.DontAllowUnloadOfTextures); try { for (int i = 0; i < a.Objects.Length; i++) { a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject(); } Train.Cars[Car].CarSections[0].Elements = a.Objects; Train.Cars[Car].CameraRestrictionMode = World.CameraRestrictionMode.NotAvailable; } catch { Program.RestartArguments = " "; Loading.Cancel = true; } } else { Interface.AddMessage(Interface.MessageType.Warning, false, "Interior view file is not supported for Car " + Car + " in XML file " + fileName); } } }
internal static void Parse(string fileName, TrainManager.Train Train, ref ObjectManager.UnifiedObject[] CarObjects, ref ObjectManager.UnifiedObject[] BogieObjects) { //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); CarObjectsReversed = new bool[Train.Cars.Length]; BogieObjectsReversed = new bool[Train.Cars.Length * 2]; if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Train/Car"); if (DocumentNodes == null || DocumentNodes.Count == 0) { Interface.AddMessage(Interface.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"); } //Use the index here for easy access to the car count for (int i = 0; i < DocumentNodes.Count; i++) { if (i > Train.Cars.Length) { Interface.AddMessage(Interface.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()) { ParseCarNode(DocumentNodes[i], fileName, i, ref Train, ref CarObjects, ref BogieObjects); } 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); currentPath = savedPath; } catch { Interface.AddMessage(Interface.MessageType.Error, false, "Failed to load the child Car XML file specified in " + DocumentNodes[i].InnerText); } } if (i == DocumentNodes.Count && i < 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 Interface.AddMessage(Interface.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 (Train.Cars[Train.DriverCar].CameraRestrictionMode != World.CameraRestrictionMode.NotSpecified) { World.CameraRestriction = Train.Cars[Train.DriverCar].CameraRestrictionMode; World.UpdateViewingDistances(); } 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.PowerNotchDescriptions = c.InnerText.Split(';'); for (int j = 0; j < Train.PowerNotchDescriptions.Length; j++) { Size s = Renderer.MeasureString(Fonts.NormalFont, Train.PowerNotchDescriptions[j]); if (s.Width > Train.MaxPowerNotchWidth) { Train.MaxPowerNotchWidth = s.Width; } } break; case "brake": Train.BrakeNotchDescriptions = c.InnerText.Split(';'); for (int j = 0; j < Train.BrakeNotchDescriptions.Length; j++) { Size s = Renderer.MeasureString(Fonts.NormalFont, Train.BrakeNotchDescriptions[j]); if (s.Width > Train.MaxBrakeNotchWidth) { Train.MaxBrakeNotchWidth = s.Width; } } break; case "reverser": Train.ReverserDescriptions = c.InnerText.Split(';'); for (int j = 0; j < Train.ReverserDescriptions.Length; j++) { Size s = Renderer.MeasureString(Fonts.NormalFont, Train.ReverserDescriptions[j]); if (s.Width > Train.MaxReverserWidth) { Train.MaxReverserWidth = 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 ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)CarObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int bogieObjects = 0; for (int i = 0; i < Train.Cars.Length * 2; i++) { bool IsOdd = (i % 2 != 0); int CarIndex = i / 2; if (BogieObjects[i] != null) { bogieObjects++; 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 ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)BogieObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (BogieObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)BogieObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } } }
private static void ParsePanelAnimatedNode(XElement Element, string FileName, string TrainPath, TrainManager.Train Train, int Car, CarSection CarSection, int GroupIndex) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; int currentSectionElement = 0; int numberOfSectionElements = Element.Elements().Count(); double invfac = numberOfSectionElements == 0 ? Loading.TrainProgressCurrentWeight : Loading.TrainProgressCurrentWeight / (double)numberOfSectionElements; foreach (XElement SectionElement in Element.Elements()) { Loading.TrainProgress = Loading.TrainProgressCurrentSum + invfac * (double)currentSectionElement; if ((currentSectionElement & 4) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) { return; } } string Section = SectionElement.Name.LocalName; switch (SectionElement.Name.LocalName.ToLowerInvariant()) { case "group": if (GroupIndex == 0) { int n = 0; foreach (XElement KeyNode in SectionElement.Elements()) { string Key = KeyNode.Name.LocalName; string Value = KeyNode.Value; int LineNumber = ((IXmlLineInfo)KeyNode).LineNumber; switch (Key.ToLowerInvariant()) { case "number": if (Value.Length != 0 && !NumberFormats.TryParseIntVb6(Value, out n)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; } } if (n + 1 >= CarSection.Groups.Length) { Array.Resize(ref CarSection.Groups, n + 2); CarSection.Groups[n + 1] = new ElementsGroup(ObjectType.Overlay); } ParsePanelAnimatedNode(SectionElement, FileName, TrainPath, Train, Car, CarSection, n + 1); } break; case "touch": if (GroupIndex > 0) { Vector3 Position = Vector3.Zero; Vector3 Size = Vector3.Zero; int JumpScreen = GroupIndex - 1; List <int> SoundIndices = new List <int>(); List <TrainManager.CommandEntry> CommandEntries = new List <TrainManager.CommandEntry>(); TrainManager.CommandEntry CommandEntry = new TrainManager.CommandEntry(); foreach (XElement KeyNode in SectionElement.Elements()) { string Key = KeyNode.Name.LocalName; string Value = KeyNode.Value; int LineNumber = ((IXmlLineInfo)KeyNode).LineNumber; switch (Key.ToLowerInvariant()) { case "position": if (!Vector3.TryParse(KeyNode.Value, ',', out Position)) { Interface.AddMessage(MessageType.Error, false, "Position is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "size": if (!Vector3.TryParse(KeyNode.Value, ',', out Size)) { Interface.AddMessage(MessageType.Error, false, "Size is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "jumpscreen": if (Value.Length != 0 && !NumberFormats.TryParseIntVb6(Value, out JumpScreen)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "soundindex": if (Value.Length != 0) { int SoundIndex; if (!NumberFormats.TryParseIntVb6(Value, out SoundIndex)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } SoundIndices.Add(SoundIndex); } break; case "command": { if (!CommandEntries.Contains(CommandEntry)) { CommandEntries.Add(CommandEntry); } if (string.Compare(Value, "N/A", StringComparison.InvariantCultureIgnoreCase) == 0) { break; } int i; for (i = 0; i < Translations.CommandInfos.Length; i++) { if (string.Compare(Value, Translations.CommandInfos[i].Name, StringComparison.OrdinalIgnoreCase) == 0) { break; } } if (i == Translations.CommandInfos.Length || Translations.CommandInfos[i].Type != Translations.CommandType.Digital) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } else { CommandEntry.Command = Translations.CommandInfos[i].Command; } } break; case "commandoption": if (!CommandEntries.Contains(CommandEntry)) { CommandEntries.Add(CommandEntry); } if (Value.Length != 0 && !NumberFormats.TryParseIntVb6(Value, out CommandEntry.Option)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "soundentries": if (!KeyNode.HasElements) { Interface.AddMessage(MessageType.Error, false, $"An empty list of touch sound indices was defined at line {((IXmlLineInfo)KeyNode).LineNumber} in XML file {FileName}"); break; } ParseTouchSoundEntryNode(FileName, KeyNode, SoundIndices); break; case "commandentries": if (!KeyNode.HasElements) { Interface.AddMessage(MessageType.Error, false, $"An empty list of touch commands was defined at line {((IXmlLineInfo)KeyNode).LineNumber} in XML file {FileName}"); break; } ParseTouchCommandEntryNode(FileName, KeyNode, CommandEntries); break; } } CreateTouchElement(CarSection.Groups[GroupIndex], Position, Size, JumpScreen, SoundIndices.ToArray(), CommandEntries.ToArray()); } break; case "include": { foreach (XElement KeyNode in SectionElement.Elements()) { string Key = KeyNode.Name.LocalName; string Value = KeyNode.Value; int LineNumber = ((IXmlLineInfo)KeyNode).LineNumber; switch (Key.ToLowerInvariant()) { case "filename": { string File = OpenBveApi.Path.CombineFile(TrainPath, Value); if (System.IO.File.Exists(File)) { System.Text.Encoding e = TextEncoding.GetSystemEncodingFromFile(File); UnifiedObject currentObject; Program.CurrentHost.LoadObject(File, e, out currentObject); var a = currentObject as AnimatedObjectCollection; if (a != null) { for (int i = 0; i < a.Objects.Length; i++) { Program.CurrentHost.CreateDynamicObject(ref a.Objects[i].internalObject); } CarSection.Groups[GroupIndex].Elements = a.Objects; } else { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } } } break; } } } break; } } }
private static void ParseTouchCommandEntryNode(string fileName, XElement parent, ICollection <TrainManager.CommandEntry> entries) { foreach (XElement childNode in parent.Elements()) { if (childNode.Name.LocalName.ToLowerInvariant() != "entry") { Interface.AddMessage(MessageType.Error, false, $"Invalid entry node {childNode.Name.LocalName} in XML node {parent.Name.LocalName} at line {((IXmlLineInfo)childNode).LineNumber}"); } else { TrainManager.CommandEntry entry = new TrainManager.CommandEntry(); System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.InvariantCulture; string section = childNode.Name.LocalName; foreach (XElement keyNode in childNode.Elements()) { string key = keyNode.Name.LocalName; string value = keyNode.Value; int lineNumber = ((IXmlLineInfo)keyNode).LineNumber; switch (keyNode.Name.LocalName.ToLowerInvariant()) { case "name": if (string.Compare(value, "N/A", StringComparison.InvariantCultureIgnoreCase) == 0) { break; } int i; for (i = 0; i < Translations.CommandInfos.Length; i++) { if (string.Compare(value, Translations.CommandInfos[i].Name, StringComparison.OrdinalIgnoreCase) == 0) { break; } } if (i == Translations.CommandInfos.Length || Translations.CommandInfos[i].Type != Translations.CommandType.Digital) { Interface.AddMessage(MessageType.Error, false, $"value is invalid in {key} in {section} at line {lineNumber.ToString(culture)} in {fileName}"); } else { entry.Command = Translations.CommandInfos[i].Command; } break; case "option": if (value.Any()) { int option; if (!NumberFormats.TryParseIntVb6(value, out option)) { Interface.AddMessage(MessageType.Error, false, $"value is invalid in {key} in {section} at line {lineNumber.ToString(culture)} in {fileName}"); } else { entry.Option = option; } } break; } } entries.Add(entry); } } }
private static void ParseBrakeNode(XmlNode Node, string fileName, int Car, ref TrainManager.Train Train) { double compressorRate = 5000.0, compressorMinimumPressure = 690000.0, compressorMaximumPressure = 780000.0; double auxiliaryReservoirChargeRate = 200000.0; double equalizingReservoirChargeRate = 200000.0, equalizingReservoirServiceRate = 50000.0, equalizingReservoirEmergencyRate = 250000.0; double brakePipeNormalPressure = 0.0, brakePipeChargeRate = 10000000.0, brakePipeServiceRate = 1500000.0, brakePipeEmergencyRate = 5000000.0; double straightAirPipeServiceRate = 300000.0, straightAirPipeEmergencyRate = 400000.0, straightAirPipeReleaseRate = 200000.0; double brakeCylinderServiceMaximumPressure = 440000.0, brakeCylinderEmergencyMaximumPressure = 440000.0, brakeCylinderEmergencyRate = 300000.0, brakeCylinderReleaseRate = 200000.0; 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 "compressor": Train.Cars[Car].CarBrake.brakeType = BrakeType.Main; //We have a compressor so must be a main brake type if (c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "rate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out compressorRate) | compressorRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid compression rate defined for Car " + Car + " in XML file " + fileName); compressorRate = 5000.0; } break; } } } break; case "mainreservoir": if (c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "minimumpressure": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out compressorMinimumPressure) | compressorMinimumPressure <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid main reservoir minumum pressure defined for Car " + Car + " in XML file " + fileName); compressorMinimumPressure = 690000.0; } break; case "maximumpressure": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out compressorMaximumPressure) | compressorMaximumPressure <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid main reservoir maximum pressure defined for Car " + Car + " in XML file " + fileName); compressorMaximumPressure = 780000.0; } break; } } } break; case "auxiliaryreservoir": if (c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "chargerate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out auxiliaryReservoirChargeRate) | auxiliaryReservoirChargeRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid auxiliary reservoir charge rate defined for Car " + Car + " in XML file " + fileName); auxiliaryReservoirChargeRate = 200000.0; } break; } } } break; case "equalizingreservoir": if (c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "chargerate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out equalizingReservoirChargeRate) | equalizingReservoirChargeRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid equalizing reservoir charge rate defined for Car " + Car + " in XML file " + fileName); equalizingReservoirChargeRate = 50000.0; } break; case "servicerate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out equalizingReservoirServiceRate) | equalizingReservoirServiceRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid equalizing reservoir service rate defined for Car " + Car + " in XML file " + fileName); equalizingReservoirServiceRate = 50000.0; } break; case "emergencyrate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out equalizingReservoirEmergencyRate) | equalizingReservoirEmergencyRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid equalizing reservoir emergency rate defined for Car " + Car + " in XML file " + fileName); equalizingReservoirEmergencyRate = 50000.0; } break; } } } break; case "brakepipe": if (c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "normalpressure": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out brakePipeNormalPressure) | brakePipeNormalPressure <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid brake pipe normal pressure defined for Car " + Car + " in XML file " + fileName); brakePipeNormalPressure = 0.0; } break; case "chargerate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out brakePipeChargeRate) | brakePipeChargeRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid brake pipe charge rate defined for Car " + Car + " in XML file " + fileName); brakePipeChargeRate = 10000000.0; } break; case "servicerate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out brakePipeServiceRate) | brakePipeServiceRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid brake pipe service rate defined for Car " + Car + " in XML file " + fileName); brakePipeServiceRate = 1500000.0; } break; case "emergencyrate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out brakePipeEmergencyRate) | brakePipeEmergencyRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid brake pipe emergency rate defined for Car " + Car + " in XML file " + fileName); brakePipeEmergencyRate = 400000.0; } break; } } } break; case "straightairpipe": if (c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "servicerate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out straightAirPipeServiceRate) | straightAirPipeServiceRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid straight air pipe service rate defined for Car " + Car + " in XML file " + fileName); straightAirPipeServiceRate = 300000.0; } break; case "emergencyrate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out straightAirPipeEmergencyRate) | straightAirPipeEmergencyRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid straight air pipe emergency rate defined for Car " + Car + " in XML file " + fileName); straightAirPipeEmergencyRate = 400000.0; } break; case "releaserate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out straightAirPipeReleaseRate) | straightAirPipeReleaseRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid straight air pipe emergency rate defined for Car " + Car + " in XML file " + fileName); straightAirPipeReleaseRate = 200000.0; } break; } } } break; case "brakecylinder": if (c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "servicemaximumpressure": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out brakeCylinderServiceMaximumPressure) | brakeCylinderServiceMaximumPressure <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid brake cylinder service pressure defined for Car " + Car + " in XML file " + fileName); brakeCylinderServiceMaximumPressure = 440000.0; } break; case "emergencymaximumpressure": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out brakeCylinderEmergencyMaximumPressure) | brakeCylinderEmergencyMaximumPressure <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid brake cylinder emergency pressure defined for Car " + Car + " in XML file " + fileName); brakeCylinderEmergencyMaximumPressure = 440000.0; } break; case "emergencyrate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out brakeCylinderEmergencyRate) | brakeCylinderEmergencyRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid brake cylinder emergency pressure defined for Car " + Car + " in XML file " + fileName); brakeCylinderEmergencyRate = 300000.0; } break; case "releaserate": if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out brakeCylinderReleaseRate) | brakeCylinderReleaseRate <= 0.0) { Interface.AddMessage(MessageType.Warning, false, "Invalid brake cylinder emergency pressure defined for Car " + Car + " in XML file " + fileName); brakeCylinderReleaseRate = 200000.0; } break; } } } break; } } Train.Cars[Car].CarBrake.airCompressor = new Compressor(compressorRate); Train.Cars[Car].CarBrake.mainReservoir = new MainReservoir(compressorMinimumPressure, compressorMaximumPressure, 0.01, (Train.Handles.Brake is TrainManager.AirBrakeHandle ? 0.25 : 0.075) / Train.Cars.Length); Train.Cars[Car].CarBrake.equalizingReservoir = new EqualizingReservoir(equalizingReservoirServiceRate, equalizingReservoirEmergencyRate, equalizingReservoirChargeRate); Train.Cars[Car].CarBrake.equalizingReservoir.NormalPressure = 1.005 * brakePipeNormalPressure; Train.Cars[Car].CarBrake.brakePipe = new BrakePipe(brakePipeNormalPressure, brakePipeChargeRate, brakePipeServiceRate, brakePipeEmergencyRate, Train.Cars[0].CarBrake is ElectricCommandBrake); { double r = 200000.0 / brakeCylinderEmergencyMaximumPressure - 1.0; if (r < 0.1) { r = 0.1; } if (r > 1.0) { r = 1.0; } Train.Cars[Car].CarBrake.auxiliaryReservoir = new AuxiliaryReservoir(0.975 * brakePipeNormalPressure, auxiliaryReservoirChargeRate, 0.5, r); } Train.Cars[Car].CarBrake.brakeCylinder = new BrakeCylinder(brakeCylinderServiceMaximumPressure, brakeCylinderEmergencyMaximumPressure, Train.Handles.Brake is TrainManager.AirBrakeHandle ? brakeCylinderEmergencyRate : 0.3 * brakeCylinderEmergencyRate, brakeCylinderEmergencyRate, brakeCylinderReleaseRate); Train.Cars[Car].CarBrake.straightAirPipe = new StraightAirPipe(straightAirPipeServiceRate, straightAirPipeEmergencyRate, straightAirPipeReleaseRate); }
internal void ExportFiles() { try { switch (CurrentTrainFileType) { case TrainFileType.OldFormat: if (!string.IsNullOrEmpty(TrainDatExportLocation)) { TrainDat.Write(TrainDatExportLocation, Train); } if (!string.IsNullOrEmpty(ExtensionsCfgExportLocation)) { ExtensionsCfg.Write(ExtensionsCfgExportLocation, Train); } break; default: throw new ArgumentOutOfRangeException(); } } catch (Exception e) { Interface.AddMessage(MessageType.Error, false, $"{e.GetType().FullName}: {e.Message} at {e.StackTrace}"); } try { switch (CurrentPanelFileType) { case PanelFileType.Panel2Cfg: if (!string.IsNullOrEmpty(Panel2CfgExportLocation)) { PanelCfgBve4.Write(Panel2CfgExportLocation, Panel); } break; case PanelFileType.PanelXml: if (!string.IsNullOrEmpty(PanelXmlExportLocation)) { PanelCfgXml.Write(PanelXmlExportLocation, Panel); } break; default: throw new ArgumentOutOfRangeException(); } } catch (Exception e) { Interface.AddMessage(MessageType.Error, false, $"{e.GetType().FullName}: {e.Message} at {e.StackTrace}"); } try { switch (CurrentSoundFileType) { case SoundFileType.NoSettingFile: break; case SoundFileType.SoundCfg: if (!string.IsNullOrEmpty(SoundCfgExportLocation)) { SoundCfgBve4.Write(SoundCfgExportLocation, Sound); } break; case SoundFileType.SoundXml: if (!string.IsNullOrEmpty(SoundXmlExportLocation)) { SoundCfgXml.Write(SoundXmlExportLocation, Sound); } break; default: throw new ArgumentOutOfRangeException(); } } catch (Exception e) { Interface.AddMessage(MessageType.Error, false, $"{e.GetType().FullName}: {e.Message} at {e.StackTrace}"); } }
internal void ImportFiles() { try { switch (CurrentTrainFileType) { case TrainFileType.OldFormat: if (!string.IsNullOrEmpty(TrainDatImportLocation)) { TrainDat.Parse(TrainDatImportLocation, out train); OnPropertyChanged(new PropertyChangedEventArgs(nameof(Train))); } if (!string.IsNullOrEmpty(ExtensionsCfgImportLocation)) { ExtensionsCfg.Parse(ExtensionsCfgImportLocation, Train); } break; default: throw new ArgumentOutOfRangeException(); } CreateItem(); } catch (Exception e) { Interface.AddMessage(MessageType.Error, false, $"{e.GetType().FullName}: {e.Message} at {e.StackTrace}"); } try { switch (CurrentPanelFileType) { case PanelFileType.Panel2Cfg: if (!string.IsNullOrEmpty(Panel2CfgImportLocation)) { PanelCfgBve4.Parse(Panel2CfgImportLocation, out panel); OnPropertyChanged(new PropertyChangedEventArgs(nameof(Panel))); } break; case PanelFileType.PanelXml: if (!string.IsNullOrEmpty(PanelXmlImportLocation)) { PanelCfgXml.Parse(PanelXmlImportLocation, out panel); OnPropertyChanged(new PropertyChangedEventArgs(nameof(Panel))); } break; default: throw new ArgumentOutOfRangeException(); } Panel.CreateTreeItem(); Panel.SelectedTreeItem = Panel.TreeItem; } catch (Exception e) { Interface.AddMessage(MessageType.Error, false, $"{e.GetType().FullName}: {e.Message} at {e.StackTrace}"); } try { switch (CurrentSoundFileType) { case SoundFileType.NoSettingFile: if (!string.IsNullOrEmpty(TrainFolderImportLocation)) { SoundCfgBve2.Parse(TrainFolderImportLocation, out sound); OnPropertyChanged(new PropertyChangedEventArgs(nameof(Sound))); } break; case SoundFileType.SoundCfg: if (!string.IsNullOrEmpty(SoundCfgImportLocation)) { SoundCfgBve4.Parse(SoundCfgImportLocation, out sound); OnPropertyChanged(new PropertyChangedEventArgs(nameof(Sound))); } break; case SoundFileType.SoundXml: if (!string.IsNullOrEmpty(SoundXmlImportLocation)) { SoundCfgXml.Parse(SoundXmlImportLocation, out sound); OnPropertyChanged(new PropertyChangedEventArgs(nameof(Sound))); } break; default: throw new ArgumentOutOfRangeException(); } Sound.CreateTreeItem(); Sound.SelectedTreeItem = Sound.TreeItem; } catch (Exception e) { Interface.AddMessage(MessageType.Error, false, $"{e.GetType().FullName}: {e.Message} at {e.StackTrace}"); } }