/// <summary>Parses an XML motor table node</summary> /// <param name="node">The node</param> /// <param name="Tables">The motor sound tables to assign this node's contents to</param> /// <param name="Position">The default sound position</param> /// <param name="Radius">The default sound radius</param> private static void ParseMotorSoundTableNode(XmlNode node, ref TrainManager.MotorSoundTable[] Tables, Vector3 Position, double Radius) { foreach (XmlNode c in node.ChildNodes) { int idx; if (!NumberFormats.TryParseIntVb6(c.Name, out idx)) { continue; } for (int i = 0; i < Tables.Length; i++) { Tables[i].Buffer = null; Tables[i].Source = null; for (int j = 0; j > Tables[i].Entries.Length; j++) { if (idx == Tables[i].Entries[j].SoundIndex) { ParseNode(c, out Tables[i].Entries[j].Buffer, ref Position, Radius); } } } } }
/// <summary> /// Function to parse the contents of TravelData class /// </summary> /// <param name="FileName">The filename of the containing XML file</param> /// <param name="SectionElement">The XElement to parse</param> /// <param name="Data">Travel data to which the parse results apply</param> private static void ParseTravelDataNode(string FileName, XElement SectionElement, TravelData Data) { string Section = SectionElement.Name.LocalName; double Decelerate = 0.0; double Accelerate = 0.0; double TargetSpeed = 0.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 "decelerate": if (Value.Any() && !NumberFormats.TryParseDoubleVb6(Value, out Decelerate) || Decelerate < 0.0) { Interface.AddMessage(MessageType.Error, false, $"Value is expected to be a non-negative floating-point number in {Key} in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); } break; case "position": case "stopposition": if (Value.Any() && !NumberFormats.TryParseDoubleVb6(Value, out Data.Position)) { Interface.AddMessage(MessageType.Error, false, $"Value is invalid in {Key} in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); } break; case "accelerate": if (Value.Any() && !NumberFormats.TryParseDoubleVb6(Value, out Accelerate) || Accelerate < 0.0) { Interface.AddMessage(MessageType.Error, false, $"Value is expected to be a non-negative floating-point number in {Key} in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); } break; case "targetspeed": if (Value.Any() && !NumberFormats.TryParseDoubleVb6(Value, out TargetSpeed) || TargetSpeed < 0.0) { Interface.AddMessage(MessageType.Error, false, $"Value is expected to be a non-negative floating-point number in {Key} in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); } break; case "rail": if (Value.Any() && !NumberFormats.TryParseIntVb6(Value, out Data.RailIndex) || Data.RailIndex < 0) { Interface.AddMessage(MessageType.Error, false, $"Value is expected to be a non-negative integer number in {Key} in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); Data.RailIndex = 0; } break; } } Data.Decelerate = -Decelerate / 3.6; Data.Accelerate = Accelerate / 3.6; Data.TargetSpeed = TargetSpeed / 3.6; }
/// <summary>Parses any command-line arguments passed to the main program</summary> /// <param name="Arguments">A string array of arguments</param> /// <param name="Result">The main dialog result (Used to launch)</param> internal static void ParseArguments(string[] Arguments, ref formMain.MainDialogResult Result) { if (Arguments.Length == 0) { return; } for (int i = 0; i < Arguments.Length; i++) { int equals = Arguments[i].IndexOf('='); if (equals >= 0) { string key = Arguments[i].Substring(0, equals).Trim(new char[] { }).ToLowerInvariant(); string value = Arguments[i].Substring(equals + 1).Trim(new char[] { }); switch (key) { case "/route": Result.RouteFile = value; Result.RouteEncoding = TextEncoding.GetSystemEncodingFromFile(Result.RouteFile); break; case "/train": Result.TrainFolder = value; Result.TrainEncoding = TextEncoding.GetSystemEncodingFromFile(Result.TrainFolder, "train.txt"); break; case "/station": Result.InitialStation = value; break; case "/time": Interface.TryParseTime(value, out Result.StartTime); break; case "/ai": if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "1") { Result.AIDriver = true; } break; case "/fullscreen": if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "1") { Result.FullScreen = true; } break; case "/width": NumberFormats.TryParseIntVb6(value, out Result.Width); break; case "/height": NumberFormats.TryParseIntVb6(value, out Result.Height); break; } } } }
private int[] FindIndices(ref string Command, Expression Expression) { int[] commandIndices = { 0, 0 }; if (Command != null && Command.EndsWith(")")) { for (int k = Command.Length - 2; k >= 0; k--) { if (Command[k] == '(') { string Indices = Command.Substring(k + 1, Command.Length - k - 2).TrimStart(); Command = Command.Substring(0, k).TrimEnd(); int h = Indices.IndexOf(";", StringComparison.Ordinal); if (h >= 0) { string a = Indices.Substring(0, h).TrimEnd(); string b = Indices.Substring(h + 1).TrimStart(); if (a.Length > 0 && !NumberFormats.TryParseIntVb6(a, out commandIndices[0])) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid first index appeared at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File + "."); Command = null; } if (b.Length > 0 && !NumberFormats.TryParseIntVb6(b, out commandIndices[1])) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid second index appeared at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File + "."); Command = null; } } else { if (Indices.Length > 0 && !NumberFormats.TryParseIntVb6(Indices, out commandIndices[0])) { if (Indices.ToLowerInvariant() != "c" || Command.ToLowerInvariant() != "route.comment") { // (C) used in route comment to represent copyright symbol, so not an error Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid index appeared at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File + "."); Command = null; } } } break; } } } return(commandIndices); }
/// <summary>Parses an XML motor table node</summary> /// <param name="node">The node</param> /// <param name="Tables">The motor sound tables to assign this node's contents to</param> /// <param name="Position">The default sound position</param> /// <param name="Radius">The default sound radius</param> private void ParseMotorSoundTableNode(XmlNode node, ref BVEMotorSoundTable[] Tables, Vector3 Position, double Radius) { foreach (XmlNode c in node.ChildNodes) { int idx = -1; if (c.Name.ToLowerInvariant() != "sound") { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid array node " + c.Name + " in XML node " + node.Name); } else { for (int i = 0; i < c.ChildNodes.Count; i++) { if (c.ChildNodes[i].Name.ToLowerInvariant() == "index") { if (!NumberFormats.TryParseIntVb6(c.ChildNodes[i].InnerText.ToLowerInvariant(), out idx)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid array index " + c.Name + " in XML node " + node.Name); return; } break; } } if (idx >= 0) { for (int i = 0; i < Tables.Length; i++) { Tables[i].Buffer = null; Tables[i].Source = null; for (int j = 0; j < Tables[i].Entries.Length; j++) { if (idx == Tables[i].Entries[j].SoundIndex) { ParseNode(c, out Tables[i].Entries[j].Buffer, ref Position, Radius); } } } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid array index " + c.Name + " in XML node " + node.Name); } } } }
/// <summary>Parses an XML node containing a list of sounds into a car sound array</summary> /// <param name="node">The node to parse</param> /// <param name="Sounds">The car sound array</param> /// <param name="Position">The default position of the sound (May be overriden by any node)</param> /// <param name="Radius">The default radius of the sound (May be overriden by any node)</param> private void ParseDictionaryNode(XmlNode node, out Dictionary <int, CarSound> Sounds, Vector3 Position, double Radius) { Sounds = new Dictionary <int, CarSound>(); foreach (XmlNode c in node.ChildNodes) { int idx = -1; if (c.Name.ToLowerInvariant() != "sound") { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid array node " + c.Name + " in XML node " + node.Name); } else { for (int i = 0; i < c.ChildNodes.Count; i++) { if (c.ChildNodes[i].Name.ToLowerInvariant() == "index") { if (!NumberFormats.TryParseIntVb6(c.ChildNodes[i].InnerText.ToLowerInvariant(), out idx)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid array index " + c.Name + " in XML node " + node.Name); return; } break; } } if (idx >= 0) { CarSound sound; ParseNode(c, out sound, Position, Radius); if (Sounds.ContainsKey(idx)) { Sounds[idx] = sound; } else { Sounds.Add(idx, sound); } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid array index " + c.Name + " in XML node " + node.Name); } } } }
private static void ParseArrayNode <T>(string basePath, XElement parentNode, ICollection <SoundElement> elements) where T : SoundElement <int>, new() { foreach (XElement childNode in parentNode.Elements()) { if (childNode.Name.LocalName.ToLowerInvariant() != "sound") { Interface.AddMessage(MessageType.Error, false, $"Invalid array node {childNode.Name.LocalName} in XML node {parentNode.Name.LocalName} at line {((IXmlLineInfo)childNode).LineNumber}"); } else { int idx; XElement indexNode = childNode.Element("Index"); if (indexNode == null) { Interface.AddMessage(MessageType.Error, false, $"Invalid array index {childNode.Name.LocalName} in XML node {parentNode.Name.LocalName} at line {((IXmlLineInfo)childNode).LineNumber}"); return; } if (!NumberFormats.TryParseIntVb6(indexNode.Value, out idx)) { Interface.AddMessage(MessageType.Error, false, $"Invalid array index {childNode.Name.LocalName} in XML node {parentNode.Name.LocalName} at line {((IXmlLineInfo)childNode).LineNumber}"); return; } if (idx >= 0) { T element = new T { Key = idx }; ParseNode(basePath, childNode, element); elements.Add(element); } else { Interface.AddMessage(MessageType.Error, false, $"Invalid array index {childNode.Name.LocalName} in XML node {parentNode.Name.LocalName} at line {((IXmlLineInfo)indexNode).LineNumber}"); } } } }
/// <summary>Parses an XML node containing a list of sounds into a car sound array</summary> /// <param name="node">The node to parse</param> /// <param name="Sounds">The car sound array</param> /// <param name="Position">The default position of the sound (May be overriden by any node)</param> /// <param name="Radius">The default radius of the sound (May be overriden by any node)</param> private static void ParseArrayNode(XmlNode node, out TrainManager.CarSound[] Sounds, Vector3 Position, double Radius) { Sounds = new TrainManager.CarSound[0]; foreach (XmlNode c in node.ChildNodes) { int idx = -1; if (c.Name.ToLowerInvariant() != "sound") { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid array node " + c.Name + " in XML node " + node.Name); } else { for (int i = 0; i < c.ChildNodes.Count; i++) { if (c.ChildNodes[i].Name.ToLowerInvariant() == "index") { if (!NumberFormats.TryParseIntVb6(c.ChildNodes[i].InnerText.ToLowerInvariant(), out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid array index " + c.Name + " in XML node " + node.Name); return; } break; } } if (idx >= 0) { int l = Sounds.Length; Array.Resize(ref Sounds, idx + 1); while (l < Sounds.Length) { Sounds[l] = TrainManager.CarSound.Empty; l++; } ParseNode(c, out Sounds[idx], Position, Radius); } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid array index " + c.Name + " in XML node " + node.Name); } } } }
private static void ParseTouchSoundEntryNode(string fileName, XElement parent, ICollection <int> indices) { foreach (XElement childNode in parent.Elements()) { if (childNode.Name.LocalName.ToLowerInvariant() != "entry") { Plugin.currentHost.AddMessage(MessageType.Error, false, $"Invalid entry node {childNode.Name.LocalName} in XML node {parent.Name.LocalName} at line {((IXmlLineInfo)childNode).LineNumber}"); } else { 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 "index": if (value.Any()) { int index; if (!NumberFormats.TryParseIntVb6(value, out index)) { Plugin.currentHost.AddMessage(MessageType.Error, false, $"value is invalid in {key} in {section} at line {lineNumber.ToString(culture)} in {fileName}"); } indices.Add(index); } break; } } } } }
/// <summary>Parses an XML node containing a list of sounds into a car sound array</summary> /// <param name="node">The node to parse</param> /// <param name="Sounds">The car sound array</param> /// <param name="Position">The default position of the sound (May be overriden by any node)</param> /// <param name="Radius">The default radius of the sound (May be overriden by any node)</param> private static void ParseArrayNode(XmlNode node, out CarSound[] Sounds, Vector3 Position, double Radius) { Sounds = new CarSound[0]; foreach (XmlNode c in node.ChildNodes) { int idx = -1; if (c.Name.ToLowerInvariant() == "sound") { for (int i = 0; i < c.ChildNodes.Count; i++) { if (c.ChildNodes[i].Name.ToLowerInvariant() == "index") { if (!NumberFormats.TryParseIntVb6(c.ChildNodes[i].InnerText.ToLowerInvariant(), out idx)) { return; } break; } } if (idx >= 0) { int l = Sounds.Length; Array.Resize(ref Sounds, idx + 1); while (l < Sounds.Length) { Sounds[l] = new CarSound(); l++; } ParseNode(c, out Sounds[idx], Position, Radius); } } } }
private void ParseRouteCommand(RouteCommand Command, string[] Arguments, int Index, string FileName, double[] UnitOfLength, Expression Expression, ref RouteData Data, bool PreviewOnly) { switch (Command) { case RouteCommand.DeveloperID: //Unused by OpenBVE break; case RouteCommand.Comment: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { CurrentRoute.Comment = Arguments[0]; } break; case RouteCommand.Image: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { string f = Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[0]); if (!System.IO.File.Exists(f)) { Plugin.CurrentHost.AddMessage(MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { CurrentRoute.Image = f; } } break; case RouteCommand.TimeTable: if (!PreviewOnly) { if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "" + Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { CurrentRoute.Information.DefaultTimetableDescription = Arguments[0]; } } break; case RouteCommand.Change: if (!PreviewOnly) { int change = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out change)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); change = 0; } else if (change < -1 | change > 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Mode is expected to be -1, 0 or 1 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); change = 0; } Plugin.CurrentOptions.TrainStart = (TrainStartMode)change; } break; case RouteCommand.Gauge: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { double a; if (!NumberFormats.TryParseDoubleVb6(Arguments[0], out a)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ValueInMillimeters is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (a <= 0.0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ValueInMillimeters is expected to be positive in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { for (int tt = 0; tt < CurrentRoute.Tracks.Count; tt++) { int t = CurrentRoute.Tracks.ElementAt(tt).Key; CurrentRoute.Tracks[t].RailGauge = 0.001 * a; } } } break; case RouteCommand.Signal: if (!PreviewOnly) { if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { double a; if (!NumberFormats.TryParseDoubleVb6(Arguments[0], out a)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Speed is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { if (Index < 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "AspectIndex is expected to be non-negative in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (a < 0.0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Speed is expected to be non-negative in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { if (Index >= Data.SignalSpeeds.Length) { int n = Data.SignalSpeeds.Length; Array.Resize(ref Data.SignalSpeeds, Index + 1); for (int i = n; i < Index; i++) { Data.SignalSpeeds[i] = double.PositiveInfinity; } } Data.SignalSpeeds[Index] = a * Data.UnitOfSpeed; } } } } break; case RouteCommand.AccelerationDueToGravity: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { double a; if (!NumberFormats.TryParseDoubleVb6(Arguments[0], out a)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Value is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (a <= 0.0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Value is expected to be positive in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { CurrentRoute.Atmosphere.AccelerationDueToGravity = a; } } break; case RouteCommand.StartTime: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { double t; if (!TryParseTime(Arguments[0], out t)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Arguments[0] + " does not parse to a valid time in command " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { if (CurrentRoute.InitialStationTime == -1) { CurrentRoute.InitialStationTime = t; } } } break; case RouteCommand.LoadingScreen: if (PreviewOnly) { return; } if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { string f = Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[0]); if (!System.IO.File.Exists(f)) { Plugin.CurrentHost.AddMessage(MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { Texture t = new Texture(f, new TextureParameters(null, null), Plugin.CurrentHost); CurrentRoute.Information.LoadingScreenBackground = t; } } break; case RouteCommand.DisplaySpeed: if (PreviewOnly) { return; } if (Arguments.Length == 1 && Arguments[0].IndexOf(',') != -1) { Arguments = Arguments[0].Split(','); } if (Arguments.Length != 2) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have two arguments at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); break; } Plugin.CurrentOptions.UnitOfSpeed = Arguments[0]; if (!double.TryParse(Arguments[1], NumberStyles.Float, Culture, out Plugin.CurrentOptions.SpeedConversionFactor)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Speed conversion factor is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); Plugin.CurrentOptions.UnitOfSpeed = "km/h"; } break; case RouteCommand.Briefing: if (PreviewOnly) { return; } if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { string f = Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[0]); if (!System.IO.File.Exists(f)) { Plugin.CurrentHost.AddMessage(MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { CurrentRoute.Information.RouteBriefing = f; } } break; case RouteCommand.Elevation: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { double a; if (!NumberFormats.TryParseDoubleVb6(Arguments[0], UnitOfLength, out a)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Height is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { CurrentRoute.Atmosphere.InitialElevation = a; } } break; case RouteCommand.Temperature: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { double a; if (!NumberFormats.TryParseDoubleVb6(Arguments[0], out a)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ValueInCelsius is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (a <= -273.15) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ValueInCelsius is expected to be greater than -273.15 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (a >= 100.0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ValueInCelsius is expected to be less than 100.0 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { CurrentRoute.Atmosphere.InitialAirTemperature = a + 273.15; } } break; case RouteCommand.Pressure: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { double a; if (!NumberFormats.TryParseDoubleVb6(Arguments[0], out a)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ValueInKPa is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (a <= 0.0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ValueInKPa is expected to be positive in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (a >= 120.0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ValueInKPa is expected to be less than 120.0 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { CurrentRoute.Atmosphere.InitialAirPressure = 1000.0 * a; } } break; case RouteCommand.AmbientLight: { if (Plugin.CurrentRoute.DynamicLighting) { Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Dynamic lighting is enabled- Route.AmbientLight will be ignored"); break; } int r = 255, g = 255, b = 255; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out r)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RedValue is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (r < 0 | r > 255) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[1], out g)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "GreenValue is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (g < 0 | g > 255) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[2], out b)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BlueValue is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (b < 0 | b > 255) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); b = b < 0 ? 0 : 255; } Plugin.CurrentRoute.Atmosphere.AmbientLightColor = new Color24((byte)r, (byte)g, (byte)b); } break; case RouteCommand.DirectionalLight: { if (Plugin.CurrentRoute.DynamicLighting) { Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Dynamic lighting is enabled- Route.DirectionalLight will be ignored"); break; } int r = 255, g = 255, b = 255; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out r)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RedValue is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (r < 0 | r > 255) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[1], out g)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "GreenValue is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (g < 0 | g > 255) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[2], out b)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BlueValue is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (b < 0 | b > 255) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); b = b < 0 ? 0 : 255; } Plugin.CurrentRoute.Atmosphere.DiffuseLightColor = new Color24((byte)r, (byte)g, (byte)b); } break; case RouteCommand.LightDirection: { if (Plugin.CurrentRoute.DynamicLighting) { Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Dynamic lighting is enabled- Route.LightDirection will be ignored"); break; } double theta = 60.0, phi = -26.565051177078; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseDoubleVb6(Arguments[0], out theta)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Theta is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !NumberFormats.TryParseDoubleVb6(Arguments[1], out phi)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Phi is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } theta = theta.ToRadians(); phi = phi.ToRadians(); double dx = Math.Cos(theta) * Math.Sin(phi); double dy = -Math.Sin(theta); double dz = Math.Cos(theta) * Math.Cos(phi); Plugin.CurrentRoute.Atmosphere.LightPosition = new Vector3((float)-dx, (float)-dy, (float)-dz); } break; case RouteCommand.DynamicLight: //Read the lighting XML file string path = Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[0]); if (System.IO.File.Exists(path)) { if (DynamicLightParser.ReadLightingXML(path, out Plugin.CurrentRoute.LightDefinitions)) { Plugin.CurrentRoute.DynamicLighting = true; Data.Structure.LightDefinitions.Add(int.MaxValue, Plugin.CurrentRoute.LightDefinitions); } else { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The file " + path + " is not a valid dynamic lighting XML file, at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } } else { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Dynamic lighting XML file not found at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } break; case RouteCommand.InitialViewPoint: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { int cv; if (!NumberFormats.TryParseIntVb6(Arguments[0], out cv)) { switch (Arguments[0].ToLowerInvariant()) { case "cab": cv = 0; break; case "exterior": cv = 1; break; case "track": cv = 2; break; case "flyby": cv = 3; break; case "flybyzooming": cv = 4; break; default: cv = 0; Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is invalid at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); break; } } if (cv >= 0 && cv <= 4) { Plugin.CurrentOptions.InitialViewpoint = cv; } else { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is invalid at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } } break; case RouteCommand.TfoXML: if (!PreviewOnly) { string tfoFile = Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[0]); if (!System.IO.File.Exists(tfoFile)) { Plugin.CurrentHost.AddMessage(MessageType.Error, true, "TrackFollowingObject XML file " + tfoFile + " not found in Track.TfoXML at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); break; } if (Plugin.TrainManager.TFOs == null) { Plugin.TrainManager.TFOs = new AbstractTrain[] { }; } int n = Plugin.TrainManager.TFOs.Length; Array.Resize(ref Plugin.TrainManager.TFOs, n + 1); Plugin.TrainManager.TFOs[n] = Plugin.CurrentHost.ParseTrackFollowingObject(ObjectPath, tfoFile); } break; } }
internal void LoadCalibration(string calibrationFile) { if (!File.Exists(calibrationFile)) { return; } try { for (int i = 0; i < Calibration.Length; i++) { Calibration[i] = new AxisCalibration(); } XmlDocument currentXML = new XmlDocument(); currentXML.Load(calibrationFile); XmlNodeList documentNodes = currentXML.SelectNodes("openBVE/RailDriverCalibration"); if (documentNodes != null && documentNodes.Count != 0) { for (int i = 0; i < documentNodes.Count; i++) { int idx = -1; int lMin = 0; int lMax = 255; foreach (XmlNode node in documentNodes[i].ChildNodes) { switch (node.Name.ToLowerInvariant()) { case "axis": foreach (XmlNode n in node.ChildNodes) { switch (n.Name.ToLowerInvariant()) { case "index": if (!NumberFormats.TryParseIntVb6(n.InnerText, out idx)) { Program.AppendToLogFile(@"Invalid index in RailDriver calibration file"); } break; case "minimum": if (!NumberFormats.TryParseIntVb6(n.InnerText, out lMin)) { Program.AppendToLogFile(@"Invalid minimum in RailDriver calibration file"); } break; case "maximum": if (!NumberFormats.TryParseIntVb6(n.InnerText, out lMax)) { Program.AppendToLogFile(@"Invalid minimum in RailDriver calibration file"); } break; } } lMin = Math.Abs(lMin); lMax = Math.Abs(lMax); if (lMin > 255) { lMin = 255; } else if (lMin < 0) { lMin = 0; } if (lMax >= 255) { lMax = 255; } else if (lMax < 0) { lMax = 0; } if (lMin >= lMax) { throw new InvalidDataException(@"Maximum must be non-zero and greater than minimum."); } if (idx == -1) { throw new InvalidDataException(@"Invalid axis specified."); } Calibration[idx].Minimum = lMin; Calibration[idx].Maximum = lMax; break; } } } } } catch { for (int i = 0; i < Calibration.Length; i++) { Calibration[i] = new AxisCalibration(); } MessageBox.Show(Interface.GetInterfaceString("raildriver_config_error"), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); //Clear the calibration file File.Delete(calibrationFile); } }
/// <summary>Preprocesses the options contained within a route file</summary> /// <param name="IsRW">Whether the current route file is in RW format</param> /// <param name="Expressions">The initial list of expressions</param> /// <param name="Data">The finalized route data</param> /// <param name="UnitOfLength">The units of length conversion factor to be applied</param> /// <param name="PreviewOnly">Whether this is a preview only</param> private static void PreprocessOptions(bool IsRW, Expression[] Expressions, ref RouteData Data, ref double[] UnitOfLength, bool PreviewOnly) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string Section = ""; bool SectionAlwaysPrefix = false; // process expressions for (int j = 0; j < Expressions.Length; j++) { if (IsRW && Expressions[j].Text.StartsWith("[") && Expressions[j].Text.EndsWith("]")) { Section = Expressions[j].Text.Substring(1, Expressions[j].Text.Length - 2).Trim(new char[] { }); if (string.Compare(Section, "object", StringComparison.OrdinalIgnoreCase) == 0) { Section = "Structure"; } else if (string.Compare(Section, "railway", StringComparison.OrdinalIgnoreCase) == 0) { Section = "Track"; } SectionAlwaysPrefix = true; } else { Expressions[j].ConvertRwToCsv(Section, SectionAlwaysPrefix); // separate command and arguments string Command, ArgumentSequence; Expressions[j].SeparateCommandsAndArguments(out Command, out ArgumentSequence, Culture, true, IsRW, Section); // process command double Number; bool NumberCheck = !IsRW || string.Compare(Section, "track", StringComparison.OrdinalIgnoreCase) == 0; if (!NumberCheck || !NumberFormats.TryParseDoubleVb6(Command, UnitOfLength, out Number)) { // split arguments string[] Arguments; { int n = 0; for (int k = 0; k < ArgumentSequence.Length; k++) { if (IsRW & ArgumentSequence[k] == ',') { n++; } else if (ArgumentSequence[k] == ';') { n++; } } Arguments = new string[n + 1]; int a = 0, h = 0; for (int k = 0; k < ArgumentSequence.Length; k++) { if (IsRW & ArgumentSequence[k] == ',') { Arguments[h] = ArgumentSequence.Substring(a, k - a).Trim(new char[] { }); a = k + 1; h++; } else if (ArgumentSequence[k] == ';') { Arguments[h] = ArgumentSequence.Substring(a, k - a).Trim(new char[] { }); a = k + 1; h++; } } if (ArgumentSequence.Length - a > 0) { Arguments[h] = ArgumentSequence.Substring(a).Trim(new char[] { }); h++; } Array.Resize <string>(ref Arguments, h); } // preprocess command if (Command.ToLowerInvariant() == "with") { if (Arguments.Length >= 1) { Section = Arguments[0]; SectionAlwaysPrefix = false; } else { Section = ""; SectionAlwaysPrefix = false; } Command = null; } else { if (Command.StartsWith(".")) { Command = Section + Command; } else if (SectionAlwaysPrefix) { Command = Section + "." + Command; } Command = Command.Replace(".Void", ""); } // handle indices if (Command != null && Command.EndsWith(")")) { for (int k = Command.Length - 2; k >= 0; k--) { if (Command[k] == '(') { string Indices = Command.Substring(k + 1, Command.Length - k - 2).TrimStart(new char[] { }); Command = Command.Substring(0, k).TrimEnd(new char[] { }); int h = Indices.IndexOf(";", StringComparison.Ordinal); int CommandIndex1; if (h >= 0) { string a = Indices.Substring(0, h).TrimEnd(new char[] { }); string b = Indices.Substring(h + 1).TrimStart(new char[] { }); if (a.Length > 0 && !NumberFormats.TryParseIntVb6(a, out CommandIndex1)) { Command = null; break; } int CommandIndex2; if (b.Length > 0 && !NumberFormats.TryParseIntVb6(b, out CommandIndex2)) { Command = null; } } else { if (Indices.Length > 0 && !NumberFormats.TryParseIntVb6(Indices, out CommandIndex1)) { Command = null; } } break; } } } // process command if (Command != null) { switch (Command.ToLowerInvariant()) { // options case "options.unitoflength": { if (Arguments.Length == 0) { Interface.AddMessage(MessageType.Error, false, "At least 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { UnitOfLength = new double[Arguments.Length]; for (int i = 0; i < Arguments.Length; i++) { UnitOfLength[i] = i == Arguments.Length - 1 ? 1.0 : 0.0; if (Arguments[i].Length > 0 && !NumberFormats.TryParseDoubleVb6(Arguments[i], out UnitOfLength[i])) { Interface.AddMessage(MessageType.Error, false, "FactorInMeters" + i.ToString(Culture) + " is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); UnitOfLength[i] = i == 0 ? 1.0 : 0.0; } else if (UnitOfLength[i] <= 0.0) { Interface.AddMessage(MessageType.Error, false, "FactorInMeters" + i.ToString(Culture) + " is expected to be positive in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); UnitOfLength[i] = i == Arguments.Length - 1 ? 1.0 : 0.0; } } } } break; case "options.unitofspeed": { if (Arguments.Length < 1) { Interface.AddMessage(MessageType.Error, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length > 1) { Interface.AddMessage(MessageType.Warning, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } if (Arguments[0].Length > 0 && !NumberFormats.TryParseDoubleVb6(Arguments[0], out Data.UnitOfSpeed)) { Interface.AddMessage(MessageType.Error, false, "FactorInKmph is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); Data.UnitOfSpeed = 0.277777777777778; } else if (Data.UnitOfSpeed <= 0.0) { Interface.AddMessage(MessageType.Error, false, "FactorInKmph is expected to be positive in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); Data.UnitOfSpeed = 0.277777777777778; } else { Data.UnitOfSpeed *= 0.277777777777778; } } } break; case "options.objectvisibility": { if (Arguments.Length == 0) { Interface.AddMessage(MessageType.Error, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length > 1) { Interface.AddMessage(MessageType.Warning, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } int mode = 0; if (Arguments.Length >= 1 && Arguments[0].Length != 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out mode)) { Interface.AddMessage(MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); mode = 0; } else if (mode != 0 & mode != 1) { Interface.AddMessage(MessageType.Error, false, "The specified Mode is not supported in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); mode = 0; } Data.AccurateObjectDisposal = mode == 1; } } break; case "options.compatibletransparencymode": { //Whether to use fuzzy matching for BVE2 / BVE4 transparencies //Should be DISABLED on openBVE content if (PreviewOnly) { continue; } if (Arguments.Length == 0) { Interface.AddMessage(MessageType.Error, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length > 1) { Interface.AddMessage(MessageType.Warning, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } int mode = 0; if (Arguments.Length >= 1 && Arguments[0].Length != 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out mode)) { Interface.AddMessage(MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); mode = 0; } else if (mode != 0 & mode != 1) { Interface.AddMessage(MessageType.Error, false, "The specified Mode is not supported in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); mode = 0; } Interface.CurrentOptions.OldTransparencyMode = mode == 1; } } break; case "options.enablehacks": { //Whether to apply various hacks to fix BVE2 / BVE4 routes //Whilst this is harmless, it should be DISABLED on openBVE content //in order to ensure that all errors are correctly fixed by the developer if (PreviewOnly) { continue; } if (Arguments.Length == 0) { Interface.AddMessage(MessageType.Error, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length > 1) { Interface.AddMessage(MessageType.Warning, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } int mode = 0; if (Arguments.Length >= 1 && Arguments[0].Length != 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out mode)) { Interface.AddMessage(MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); mode = 0; } else if (mode != 0 & mode != 1) { Interface.AddMessage(MessageType.Error, false, "The specified Mode is not supported in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); mode = 0; } Interface.CurrentOptions.EnableBveTsHacks = mode == 1; } } break; } } } } } }
internal static void Process(MainForm form) { mainForm = form; if (!System.IO.File.Exists(FileName)) { MessageBox.Show("The selected folder does not contain a valid train.dat \r\n Please retry.", "CarXML Convertor", MessageBoxButtons.OK, MessageBoxIcon.Information); } string[] Lines = System.IO.File.ReadAllLines(FileName); for (int i = 0; i < Lines.Length; i++) { int n = 0; switch (Lines[i].ToLowerInvariant()) { case "#cockpit": case "#cab": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (NumberFormats.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: ConvertSoundCfg.DriverPosition.X = 0.001 * a; break; case 1: ConvertSoundCfg.DriverPosition.Y = 0.001 * a; break; case 2: ConvertSoundCfg.DriverPosition.Z = 0.001 * a; break; case 3: DriverCar = (int)Math.Round(a); break; } } i++; n++; } i--; break; case "#car": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (NumberFormats.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: if (!(a <= 0.0)) { MotorCarMass = a * 1000.0; } break; case 1: if (!(a <= 0.0)) { NumberOfMotorCars = (int)Math.Round(a); } break; case 2: if (!(a <= 0.0)) { TrailerCarMass = a * 1000.0; } break; case 3: if (!(a <= 0.0)) { NumberOfTrailerCars = (int)Math.Round(a); } break; case 4: if (!(a <= 0.0)) { CarLength = a; } break; case 5: FrontCarIsMotorCar = a == 1.0; break; case 6: if (!(a <= 0.0)) { CarWidth = a; } break; case 7: if (!(a <= 0.0)) { CarHeight = a; } break; } } i++; n++; } i--; break; case "#brake": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { int a; if (NumberFormats.TryParseIntVb6(Lines[i], out a)) { switch (n) { case 0: BrakeType = a; break; } } i++; n++; } i--; break; case "#move": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (NumberFormats.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 4: BrakeCylinderEmergencyRate = a * 1000.0; break; case 5: BrakeCylinderReleaseRate = a * 1000.0; break; } } i++; n++; } i--; break; case "#pressure": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (NumberFormats.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: if (a <= 0.0) { mainForm.updateLogBoxText += "BrakeCylinderServiceMaximumPressure is expected to be positive at line " + (i + 1).ToString(CultureInfo.InvariantCulture) + " in " + FileName; } else { BrakeCylinderServiceMaximumPressure = a * 1000.0; } break; case 1: if (a <= 0.0) { mainForm.updateLogBoxText += "BrakeCylinderEmergencyMaximumPressure is expected to be positive at line " + (i + 1).ToString(CultureInfo.InvariantCulture) + " in " + FileName; } else { BrakeCylinderEmergencyMaximumPressure = a * 1000.0; } break; case 2: if (a <= 0.0) { mainForm.updateLogBoxText += "MainReservoirMinimumPressure is expected to be positive at line " + (i + 1).ToString(CultureInfo.InvariantCulture) + " in " + FileName; } else { MainReservoirMinimumPressure = a * 1000.0; } break; case 3: if (a <= 0.0) { mainForm.updateLogBoxText += "MainReservoirMaximumPressure is expected to be positive at line " + (i + 1).ToString(CultureInfo.InvariantCulture) + " in " + FileName; } else { MainReservoirMaximumPressure = a * 1000.0; } break; case 4: if (a <= 0.0) { mainForm.updateLogBoxText += "BrakePipePressue is expected to be positive at line " + (i + 1).ToString(CultureInfo.InvariantCulture) + " in " + FileName; } else { BrakePipePressure = a * 1000.0; } break; } } i++; n++; } i--; break; default: { i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { i++; n++; } i--; } break; } } if (BrakePipePressure <= 0.0) { if (BrakeType == 2) //Automatic air brake { BrakePipePressure = BrakeCylinderEmergencyMaximumPressure + 0.75 * (MainReservoirMinimumPressure - BrakeCylinderEmergencyMaximumPressure); if (BrakePipePressure > MainReservoirMinimumPressure) { BrakePipePressure = MainReservoirMinimumPressure; } } else { if (BrakeCylinderEmergencyMaximumPressure <480000.0& MainReservoirMinimumPressure> 500000.0) { BrakePipePressure = 490000.0; } else { BrakePipePressure = BrakeCylinderEmergencyMaximumPressure + 0.75 * (MainReservoirMinimumPressure - BrakeCylinderEmergencyMaximumPressure); } } } NumberOfCars = NumberOfMotorCars + NumberOfTrailerCars; MotorCars = new bool[NumberOfCars]; if (NumberOfMotorCars == 1) { if (FrontCarIsMotorCar | NumberOfTrailerCars == 0) { MotorCars[0] = true; } else { MotorCars[NumberOfCars - 1] = true; } } else if (NumberOfMotorCars == 2) { if (FrontCarIsMotorCar | NumberOfTrailerCars == 0) { MotorCars[0] = true; MotorCars[NumberOfCars - 1] = true; } else if (NumberOfTrailerCars == 1) { MotorCars[1] = true; MotorCars[2] = true; } else { int i = (int)Math.Ceiling(0.25 * (double)(NumberOfCars - 1)); int j = (int)Math.Floor(0.75 * (double)(NumberOfCars - 1)); MotorCars[i] = true; MotorCars[j] = true; } } else if (NumberOfMotorCars > 0) { if (FrontCarIsMotorCar) { MotorCars[0] = true; double t = 1.0 + (double)NumberOfTrailerCars / (double)(NumberOfMotorCars - 1); double r = 0.0; double x = 0.0; while (true) { double y = x + t - r; x = Math.Ceiling(y); r = x - y; int i = (int)x; if (i >= NumberOfCars) { break; } MotorCars[i] = true; } } else { MotorCars[1] = true; double t = 1.0 + (double)(NumberOfTrailerCars - 1) / (double)(NumberOfMotorCars - 1); double r = 0.0; double x = 1.0; while (true) { double y = x + t - r; x = Math.Ceiling(y); r = x - y; int i = (int)x; if (i >= NumberOfCars) { break; } MotorCars[i] = true; } } } }
public static Game.Station ReadStationXML(string fileName, bool PreviewOnly, Textures.Texture[] daytimeTimetableTextures, Textures.Texture[] nighttimeTimetableTextures, int CurrentStation, ref bool passAlarm, ref CsvRwRouteParser.StopRequest stopRequest) { Game.Station station = new Game.Station { Stops = new Game.StationStop[] { } }; stopRequest.Early = new TrackManager.RequestStop(); stopRequest.OnTime = new TrackManager.RequestStop(); stopRequest.Late = new TrackManager.RequestStop(); stopRequest.OnTime.Probability = 75; //The current XML file to load XmlDocument currentXML = new XmlDocument(); //Load the object's XML file currentXML.Load(fileName); string Path = System.IO.Path.GetDirectoryName(fileName); //Check for null if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Station"); //Check this file actually contains OpenBVE light definition nodes if (DocumentNodes != null) { foreach (XmlNode n in DocumentNodes) { if (n.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode c in n.ChildNodes) { //string[] Arguments = c.InnerText.Split(','); switch (c.Name.ToLowerInvariant()) { case "name": if (!string.IsNullOrEmpty(c.InnerText)) { station.Name = c.InnerText; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Station name was empty in XML file " + fileName); } break; case "arrivaltime": if (!string.IsNullOrEmpty(c.InnerText)) { if (!Interface.TryParseTime(c.InnerText, out station.ArrivalTime)) { Interface.AddMessage(Interface.MessageType.Error, false, "Station arrival time was invalid in XML file " + fileName); } } break; case "departuretime": if (!string.IsNullOrEmpty(c.InnerText)) { if (!Interface.TryParseTime(c.InnerText, out station.DepartureTime)) { Interface.AddMessage(Interface.MessageType.Error, false, "Station arrival time was invalid in XML file " + fileName); } } break; case "type": switch (c.InnerText.ToLowerInvariant()) { case "c": case "changeends": station.Type = StationType.ChangeEnds; break; case "t": case "terminal": station.Type = StationType.Terminal; break; default: station.Type = StationType.Normal; break; } break; case "passalarm": if (!string.IsNullOrEmpty(c.InnerText)) { if (c.InnerText.ToLowerInvariant() == "1" || c.InnerText.ToLowerInvariant() == "true") { passAlarm = true; } else { passAlarm = false; } } break; case "doors": int door = 0; bool doorboth = false; if (!string.IsNullOrEmpty(c.InnerText)) { switch (c.InnerText.ToLowerInvariant()) { case "l": case "left": door = -1; break; case "r": case "right": door = 1; break; case "n": case "none": case "neither": door = 0; break; case "b": case "both": doorboth = true; break; default: if (!NumberFormats.TryParseIntVb6(c.InnerText, out door)) { Interface.AddMessage(Interface.MessageType.Error, false, "Door side was invalid in XML file " + fileName); door = 0; } break; } } station.OpenLeftDoors = door < 0.0 | doorboth; station.OpenRightDoors = door > 0.0 | doorboth; break; case "forcedredsignal": if (!string.IsNullOrEmpty(c.InnerText)) { if (c.InnerText.ToLowerInvariant() == "1" || c.InnerText.ToLowerInvariant() == "true") { station.ForceStopSignal = true; } else { station.ForceStopSignal = false; } } break; case "system": switch (c.InnerText.ToLowerInvariant()) { case "0": case "ATS": station.SafetySystem = Game.SafetySystem.Ats; break; case "1": case "ATC": station.SafetySystem = Game.SafetySystem.Atc; break; default: Interface.AddMessage(Interface.MessageType.Error, false, "An invalid station safety system was specified in XML file " + fileName); station.SafetySystem = Game.SafetySystem.Ats; break; } break; case "arrivalsound": string arrSound = string.Empty; double arrRadius = 30.0; if (!c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (c.Name.ToLowerInvariant()) { case "filename": try { arrSound = OpenBveApi.Path.CombineFile(Path, cc.InnerText); } catch { Interface.AddMessage(Interface.MessageType.Error, false, "Arrival sound filename is invalid in XML file " + fileName); } break; case "radius": if (!double.TryParse(cc.InnerText, out arrRadius)) { Interface.AddMessage(Interface.MessageType.Error, false, "Arrival sound radius was invalid in XML file " + fileName); } break; } } } else { try { arrSound = OpenBveApi.Path.CombineFile(Path, c.InnerText); } catch { Interface.AddMessage(Interface.MessageType.Error, false, "Arrival sound filename is invalid in XML file " + fileName); } } if (File.Exists(arrSound)) { station.ArrivalSoundBuffer = Sounds.RegisterBuffer(arrSound, arrRadius); } else { Interface.AddMessage(Interface.MessageType.Error, false, "Arrival sound file does not exist in XML file " + fileName); } break; case "stopduration": double stopDuration; if (!double.TryParse(c.InnerText, out stopDuration)) { Interface.AddMessage(Interface.MessageType.Error, false, "Stop duration is invalid in XML file " + fileName); } else { if (stopDuration < 5.0) { stopDuration = 5.0; } station.StopTime = stopDuration; } break; case "passengerratio": double ratio; if (!double.TryParse(c.InnerText, out ratio)) { Interface.AddMessage(Interface.MessageType.Error, false, "Passenger ratio is invalid in XML file " + fileName); } else { if (ratio < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Passenger ratio must be non-negative in XML file " + fileName); ratio = 100.0; } station.PassengerRatio = ratio * 0.01; } break; case "departuresound": string depSound = string.Empty; double depRadius = 30.0; if (!c.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cc in c.ChildNodes) { switch (c.Name.ToLowerInvariant()) { case "filename": try { depSound = OpenBveApi.Path.CombineFile(Path, cc.InnerText); } catch { Interface.AddMessage(Interface.MessageType.Error, false, "Departure sound filename is invalid in XML file " + fileName); } break; case "radius": if (!double.TryParse(cc.InnerText, out depRadius)) { Interface.AddMessage(Interface.MessageType.Error, false, "Departure sound radius was invalid in XML file " + fileName); } break; } } } else { try { depSound = OpenBveApi.Path.CombineFile(Path, c.InnerText); } catch { Interface.AddMessage(Interface.MessageType.Error, false, "Departure sound filename is invalid in XML file " + fileName); } } if (File.Exists(depSound)) { station.DepartureSoundBuffer = Sounds.RegisterBuffer(depSound, depRadius); } else { Interface.AddMessage(Interface.MessageType.Error, false, "Departure sound file does not exist in XML file " + fileName); } break; case "timetableindex": if (!PreviewOnly) { int ttidx = -1; if (!string.IsNullOrEmpty(c.InnerText)) { if (NumberFormats.TryParseIntVb6(c.InnerText, out ttidx)) { if (ttidx < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Timetable index must be non-negative in XML file " + fileName); ttidx = -1; } else if (ttidx >= daytimeTimetableTextures.Length & ttidx >= nighttimeTimetableTextures.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "Timetable index references a non-loaded texture in XML file " + fileName); ttidx = -1; } station.TimetableDaytimeTexture = ttidx >= 0 & ttidx < daytimeTimetableTextures.Length ? daytimeTimetableTextures[ttidx] : null; station.TimetableNighttimeTexture = ttidx >= 0 & ttidx < nighttimeTimetableTextures.Length ? nighttimeTimetableTextures[ttidx] : null; break; } } if (ttidx == -1) { if (CurrentStation > 0) { station.TimetableDaytimeTexture = Game.Stations[CurrentStation - 1].TimetableDaytimeTexture; station.TimetableNighttimeTexture = Game.Stations[CurrentStation - 1].TimetableNighttimeTexture; } else if (daytimeTimetableTextures.Length > 0 & nighttimeTimetableTextures.Length > 0) { station.TimetableDaytimeTexture = daytimeTimetableTextures[0]; station.TimetableNighttimeTexture = nighttimeTimetableTextures[0]; } } } break; case "requeststop": station.Type = StationType.RequestStop; station.StopMode = StationStopMode.AllRequestStop; foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "aibehaviour": switch (cc.InnerText.ToLowerInvariant()) { case "fullspeed": case "0": //With this set, the AI driver will not attempt to brake, but pass through at linespeed stopRequest.FullSpeed = true; break; case "normalbrake": case "1": //With this set, the AI driver breaks to a near stop whilst passing through the station stopRequest.FullSpeed = false; break; } break; case "playeronly": station.StopMode = StationStopMode.PlayerRequestStop; break; case "distance": if (!string.IsNullOrEmpty(cc.InnerText)) { double d; if (!NumberFormats.TryParseDoubleVb6(cc.InnerText, out d)) { Interface.AddMessage(Interface.MessageType.Error, false, "Request stop distance is invalid in XML file " + fileName); break; } stopRequest.TrackPosition -= Math.Abs(d); } break; case "earlytime": if (!string.IsNullOrEmpty(cc.InnerText)) { if (!Interface.TryParseTime(cc.InnerText, out stopRequest.Early.Time)) { Interface.AddMessage(Interface.MessageType.Error, false, "Request stop early time was invalid in XML file " + fileName); } } break; case "latetime": if (!string.IsNullOrEmpty(cc.InnerText)) { if (!Interface.TryParseTime(cc.InnerText, out stopRequest.Late.Time)) { Interface.AddMessage(Interface.MessageType.Error, false, "Request stop late time was invalid in XML file " + fileName); } } break; case "stopmessage": if (cc.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cd in cc.ChildNodes) { switch (cd.Name.ToLowerInvariant()) { case "early": if (!string.IsNullOrEmpty(cd.InnerText)) { stopRequest.Early.StopMessage = cd.InnerText; } break; case "ontime": if (!string.IsNullOrEmpty(cd.InnerText)) { stopRequest.OnTime.StopMessage = cd.InnerText; } break; case "late": if (!string.IsNullOrEmpty(cd.InnerText)) { stopRequest.Late.StopMessage = cd.InnerText; } break; case "#text": stopRequest.Early.StopMessage = cc.InnerText; stopRequest.OnTime.StopMessage = cc.InnerText; stopRequest.Late.StopMessage = cc.InnerText; break; } } } break; case "passmessage": if (cc.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode cd in cc.ChildNodes) { switch (cd.Name.ToLowerInvariant()) { case "early": if (!string.IsNullOrEmpty(cd.InnerText)) { stopRequest.Early.PassMessage = cd.InnerText; } break; case "ontime": if (!string.IsNullOrEmpty(cd.InnerText)) { stopRequest.OnTime.PassMessage = cd.InnerText; } break; case "late": if (!string.IsNullOrEmpty(cd.InnerText)) { stopRequest.Late.PassMessage = cd.InnerText; } break; case "#text": stopRequest.Early.PassMessage = cc.InnerText; stopRequest.OnTime.PassMessage = cc.InnerText; stopRequest.Late.PassMessage = cc.InnerText; break; } } } break; case "probability": foreach (XmlNode cd in cc.ChildNodes) { switch (cd.Name.ToLowerInvariant()) { case "early": if (!string.IsNullOrEmpty(cd.InnerText)) { if (!NumberFormats.TryParseIntVb6(cd.InnerText, out stopRequest.Early.Probability)) { Interface.AddMessage(Interface.MessageType.Error, false, "Request stop early probability was invalid in XML file " + fileName); } } break; case "ontime": if (!string.IsNullOrEmpty(cd.InnerText)) { if (!NumberFormats.TryParseIntVb6(cd.InnerText, out stopRequest.OnTime.Probability)) { Interface.AddMessage(Interface.MessageType.Error, false, "Request stop ontime probability was invalid in XML file " + fileName); } } break; case "late": if (!string.IsNullOrEmpty(cd.InnerText)) { if (!NumberFormats.TryParseIntVb6(cd.InnerText, out stopRequest.OnTime.Probability)) { Interface.AddMessage(Interface.MessageType.Error, false, "Request stop late probability was invalid in XML file " + fileName); } } break; case "#text": if (!NumberFormats.TryParseIntVb6(cd.InnerText, out stopRequest.OnTime.Probability)) { Interface.AddMessage(Interface.MessageType.Error, false, "Request stop probability was invalid in XML file " + fileName); } break; } } break; case "maxcars": if (!NumberFormats.TryParseIntVb6(cc.InnerText, out stopRequest.MaxNumberOfCars)) { Interface.AddMessage(Interface.MessageType.Error, false, "Request stop maximum cars was invalid in XML file " + fileName); } break; } } break; } } } } return(station); } } //We couldn't find any valid XML, so return false throw new InvalidDataException(); }
private void ParseOptionCommand(OptionsCommand Command, string[] Arguments, double[] UnitOfLength, Expression Expression, ref RouteData Data, bool PreviewOnly) { switch (Command) { case OptionsCommand.BlockLength: { double length = 25.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseDoubleVb6(Arguments[0], UnitOfLength, out length)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Length is invalid in Options.BlockLength at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); length = 25.0; } Data.BlockInterval = length; } break; case OptionsCommand.XParser: if (!PreviewOnly) { int parser = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out parser) | parser < 0 | parser > 3) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "XParser is invalid in Options.XParser at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { for (int i = 0; i < Plugin.CurrentHost.Plugins.Length; i++) { if (Plugin.CurrentHost.Plugins[i].Object != null) { Plugin.CurrentHost.Plugins[i].Object.SetObjectParser((XParsers)parser); //Remember that this will be ignored if not the X plugin! } } } } break; case OptionsCommand.ObjParser: if (!PreviewOnly) { int parser = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out parser) | parser < 0 | parser > 2) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ObjParser is invalid in Options.ObjParser at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { for (int i = 0; i < Plugin.CurrentHost.Plugins.Length; i++) { if (Plugin.CurrentHost.Plugins[i].Object != null) { Plugin.CurrentHost.Plugins[i].Object.SetObjectParser((ObjParsers)parser); //Remember that this will be ignored if not the Obj plugin! } } } } break; case OptionsCommand.UnitOfLength: case OptionsCommand.UnitOfSpeed: case OptionsCommand.ObjectVisibility: break; case OptionsCommand.SectionBehavior: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { int a; if (!NumberFormats.TryParseIntVb6(Arguments[0], out a)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (a != 0 & a != 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Mode is expected to be either 0 or 1 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { Data.ValueBasedSections = a == 1; } } break; case OptionsCommand.CantBehavior: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { int a; if (!NumberFormats.TryParseIntVb6(Arguments[0], out a)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (a != 0 & a != 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Mode is expected to be either 0 or 1 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { Data.SignedCant = a == 1; } } break; case OptionsCommand.FogBehavior: if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { int a; if (!NumberFormats.TryParseIntVb6(Arguments[0], out a)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (a != 0 & a != 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Mode is expected to be either 0 or 1 in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { Data.FogTransitionMode = a == 1; } } break; } }
internal static void ParsePatchNode(XmlNode node, ref Dictionary <string, RoutefilePatch> routeFixes) { RoutefilePatch currentPatch = new RoutefilePatch(); string currentHash = string.Empty; foreach (XmlElement childNode in node.ChildNodes.OfType <XmlElement>()) { string t; switch (childNode.Name) { case "Hash": currentHash = childNode.InnerText; break; case "FileName": currentPatch.FileName = childNode.InnerText; break; case "LineEndingFix": t = childNode.InnerText.Trim().ToLowerInvariant(); if (t == "1" || t == "true") { currentPatch.LineEndingFix = true; } else { currentPatch.LineEndingFix = false; } break; case "IgnorePitchRoll": t = childNode.InnerText.Trim().ToLowerInvariant(); if (t == "1" || t == "true") { currentPatch.IgnorePitchRoll = true; } else { currentPatch.IgnorePitchRoll = false; } break; case "LogMessage": currentPatch.LogMessage = childNode.InnerText; break; case "CylinderHack": t = childNode.InnerText.Trim().ToLowerInvariant(); if (t == "1" || t == "true") { currentPatch.CylinderHack = true; } else { currentPatch.CylinderHack = false; } break; case "Expression": t = childNode.Attributes["Number"].InnerText; int expressionNumber; if (NumberFormats.TryParseIntVb6(t, out expressionNumber)) { currentPatch.ExpressionFixes.Add(expressionNumber, childNode.InnerText); } break; case "XParser": switch (childNode.InnerText.ToLowerInvariant()) { case "original": currentPatch.XParser = XParsers.Original; break; case "new": currentPatch.XParser = XParsers.NewXParser; break; case "assimp": currentPatch.XParser = XParsers.Assimp; break; } break; case "DummyRailTypes": string[] splitString = childNode.InnerText.Split(','); for (int i = 0; i < splitString.Length; i++) { int rt; if (NumberFormats.TryParseIntVb6(splitString[i], out rt)) { currentPatch.DummyRailTypes.Add(rt); } } break; case "DummyGroundTypes": splitString = childNode.InnerText.Split(','); for (int i = 0; i < splitString.Length; i++) { int gt; if (NumberFormats.TryParseIntVb6(splitString[i], out gt)) { currentPatch.DummyGroundTypes.Add(gt); } } break; case "Derailments": t = childNode.InnerText.Trim().ToLowerInvariant(); if (t == "0" || t == "false") { currentPatch.Derailments = false; } else { currentPatch.Derailments = true; } break; case "Toppling": t = childNode.InnerText.Trim().ToLowerInvariant(); if (t == "0" || t == "false") { currentPatch.Derailments = false; } else { currentPatch.Derailments = true; } break; case "SignalSet": string signalFile = Path.CombineFile(Plugin.FileSystem.GetDataFolder("Compatibility\\Signals"), childNode.InnerText.Trim()); if (File.Exists(signalFile)) { currentPatch.CompatibilitySignalSet = signalFile; } break; case "AccurateObjectDisposal": t = childNode.InnerText.Trim().ToLowerInvariant(); if (t == "1" || t == "true") { currentPatch.AccurateObjectDisposal = true; } else { currentPatch.AccurateObjectDisposal = false; } break; case "SplitLineHack": t = childNode.InnerText.Trim().ToLowerInvariant(); if (t == "1" || t == "true") { currentPatch.SplitLineHack = true; } else { currentPatch.SplitLineHack = false; } break; case "AllowTrackPositionArguments": t = childNode.InnerText.Trim().ToLowerInvariant(); if (t == "1" || t == "true") { currentPatch.AllowTrackPositionArguments = true; } else { currentPatch.AllowTrackPositionArguments = false; } break; case "DisableSemiTransparentFaces": t = childNode.InnerText.Trim().ToLowerInvariant(); if (t == "1" || t == "true") { currentPatch.DisableSemiTransparentFaces = true; } else { currentPatch.DisableSemiTransparentFaces = false; } break; case "ReducedColorTransparency": t = childNode.InnerText.Trim().ToLowerInvariant(); if (t == "1" || t == "true") { currentPatch.ReducedColorTransparency = true; } else { currentPatch.ReducedColorTransparency = false; } break; } } if (!routeFixes.ContainsKey(currentHash)) { routeFixes.Add(currentHash, currentPatch); } else { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The RoutePatches database contains a duplicate entry with hash " + currentHash); } }
private static void ParseTouchCommandEntryNode(string fileName, XElement parent, ICollection <CommandEntry> entries) { foreach (XElement childNode in parent.Elements()) { if (childNode.Name.LocalName.ToLowerInvariant() != "entry") { Plugin.currentHost.AddMessage(MessageType.Error, false, $"Invalid entry node {childNode.Name.LocalName} in XML node {parent.Name.LocalName} at line {((IXmlLineInfo)childNode).LineNumber}"); } else { CommandEntry entry = new 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) { Plugin.currentHost.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)) { Plugin.currentHost.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 ParsePanelAnimatedNode(XElement Element, string FileName, string TrainPath, TrainManager.Train Train, int Car, TrainManager.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 TrainManager.ElementsGroup { Elements = new ObjectManager.AnimatedObject[] { }, Overlay = true }; } 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; int SoundIndex = -1; Translations.Command Command = Translations.Command.None; int CommandOption = 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 "position": { string[] s = Value.Split(','); if (s.Length == 3) { if (s[0].Length != 0 && !NumberFormats.TryParseDoubleVb6(s[0], out Position.X)) { Interface.AddMessage(MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } if (s[1].Length != 0 && !NumberFormats.TryParseDoubleVb6(s[1], out Position.Y)) { Interface.AddMessage(MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } if (s[2].Length != 0 && !NumberFormats.TryParseDoubleVb6(s[2], out Position.Z)) { Interface.AddMessage(MessageType.Error, false, "Z is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(MessageType.Error, false, "Three arguments are expected in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } } break; case "size": { string[] s = Value.Split(','); if (s.Length == 3) { if (s[0].Length != 0 && !NumberFormats.TryParseDoubleVb6(s[0], out Size.X)) { Interface.AddMessage(MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } if (s[1].Length != 0 && !NumberFormats.TryParseDoubleVb6(s[1], out Size.Y)) { Interface.AddMessage(MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } if (s[2].Length != 0 && !NumberFormats.TryParseDoubleVb6(s[2], out Size.Z)) { Interface.AddMessage(MessageType.Error, false, "Z is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(MessageType.Error, false, "Three arguments are expected 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 && !NumberFormats.TryParseIntVb6(Value, out SoundIndex)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "command": { 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 { Command = Translations.CommandInfos[i].Command; } } break; case "commandoption": if (Value.Length != 0 && !NumberFormats.TryParseIntVb6(Value, out CommandOption)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; } } CreateTouchElement(CarSection.Groups[GroupIndex], Position, Size, JumpScreen, SoundIndex, Command, CommandOption); } 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); ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(File, e); if (a != null) { for (int i = 0; i < a.Objects.Length; i++) { a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject(); } 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; } } }
/// <summary>This function processes the list of expressions for $Char, $Rnd, $If and $Sub directives, and evaluates them into the final expressions dataset</summary> private void PreprocessChrRndSub(string FileName, System.Text.Encoding Encoding, ref Expression[] Expressions) { string[] Subs = new string[16]; int openIfs = 0; for (int i = 0; i < Expressions.Length; i++) { string Epilog = " at line " + Expressions[i].Line.ToString(Culture) + ", column " + Expressions[i].Column.ToString(Culture) + " in file " + Expressions[i].File; bool continueWithNextExpression = false; for (int j = Expressions[i].Text.Length - 1; j >= 0; j--) { if (Expressions[i].Text[j] == '$') { int k; for (k = j + 1; k < Expressions[i].Text.Length; k++) { if (Expressions[i].Text[k] == '(') { break; } else if (Expressions[i].Text[k] == '/' | Expressions[i].Text[k] == '\\') { k = Expressions[i].Text.Length + 1; break; } } if (k <= Expressions[i].Text.Length) { string t = Expressions[i].Text.Substring(j, k - j).TrimEnd(new char[] { }); int l = 1, h; for (h = k + 1; h < Expressions[i].Text.Length; h++) { switch (Expressions[i].Text[h]) { case '(': l++; break; case ')': l--; if (l < 0) { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid parenthesis structure in " + t + Epilog); } break; } if (l <= 0) { break; } } if (continueWithNextExpression) { break; } if (l != 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid parenthesis structure in " + t + Epilog); break; } string s = Expressions[i].Text.Substring(k + 1, h - k - 1).Trim(new char[] { }); switch (t.ToLowerInvariant()) { case "$if": if (j != 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The $If directive must not appear within another statement" + Epilog); } else { double num; if (double.TryParse(s, NumberStyles.Float, Culture, out num)) { openIfs++; Expressions[i].Text = string.Empty; if (num == 0.0) { /* * Blank every expression until the matching $Else or $EndIf * */ i++; int level = 1; while (i < Expressions.Length) { if (Expressions[i].Text.StartsWith("$if", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level++; } else if (Expressions[i].Text.StartsWith("$else", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; if (level == 1) { level--; break; } } else if (Expressions[i].Text.StartsWith("$endif", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level--; if (level == 0) { openIfs--; break; } } else { Expressions[i].Text = string.Empty; } i++; } if (level != 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "$EndIf missing at the end of the file" + Epilog); } } continueWithNextExpression = true; break; } else { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The $If condition does not evaluate to a number" + Epilog); } } continueWithNextExpression = true; break; case "$else": /* * Blank every expression until the matching $EndIf * */ Expressions[i].Text = string.Empty; if (openIfs != 0) { i++; int level = 1; while (i < Expressions.Length) { if (Expressions[i].Text.StartsWith("$if", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level++; } else if (Expressions[i].Text.StartsWith("$else", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; if (level == 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Duplicate $Else encountered" + Epilog); } } else if (Expressions[i].Text.StartsWith("$endif", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level--; if (level == 0) { openIfs--; break; } } else { Expressions[i].Text = string.Empty; } i++; } if (level != 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "$EndIf missing at the end of the file" + Epilog); } } else { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "$Else without matching $If encountered" + Epilog); } continueWithNextExpression = true; break; case "$endif": Expressions[i].Text = string.Empty; if (openIfs != 0) { openIfs--; } else { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "$EndIf without matching $If encountered" + Epilog); } continueWithNextExpression = true; break; case "$include": if (j != 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The $Include directive must not appear within another statement" + Epilog); continueWithNextExpression = true; break; } string[] args = s.Split(new char[] { ';' }); for (int ia = 0; ia < args.Length; ia++) { args[ia] = args[ia].Trim(new char[] { }); } int count = (args.Length + 1) / 2; string[] files = new string[count]; double[] weights = new double[count]; double[] offsets = new double[count]; double weightsTotal = 0.0; for (int ia = 0; ia < count; ia++) { string file; double offset; int colon = args[2 * ia].IndexOf(':'); if (colon >= 0) { file = args[2 * ia].Substring(0, colon).TrimEnd(new char[] { }); string value = args[2 * ia].Substring(colon + 1).TrimStart(new char[] { }); if (!double.TryParse(value, NumberStyles.Float, Culture, out offset)) { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The track position offset " + value + " is invalid in " + t + Epilog); break; } } else { file = args[2 * ia]; offset = 0.0; } files[ia] = Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), file); offsets[ia] = offset; if (!System.IO.File.Exists(files[ia])) { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The file " + file + " could not be found in " + t + Epilog); for (int ta = i; ta < Expressions.Length - 1; ta++) { Expressions[ta] = Expressions[ta + 1]; } Array.Resize(ref Expressions, Expressions.Length - 1); i--; break; } if (2 * ia + 1 < args.Length) { if (!NumberFormats.TryParseDoubleVb6(args[2 * ia + 1], out weights[ia])) { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "A weight is invalid in " + t + Epilog); break; } if (weights[ia] <= 0.0) { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "A weight is not positive in " + t + Epilog); break; } weightsTotal += weights[ia]; } else { weights[ia] = 1.0; weightsTotal += 1.0; } } if (count == 0) { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "No file was specified in " + t + Epilog); break; } if (!continueWithNextExpression) { double number = Plugin.RandomNumberGenerator.NextDouble() * weightsTotal; double value = 0.0; int chosenIndex = 0; for (int ia = 0; ia < count; ia++) { value += weights[ia]; if (value > number) { chosenIndex = ia; break; } } Expression[] expr; //Get the text encoding of our $Include file System.Text.Encoding includeEncoding = TextEncoding.GetSystemEncodingFromFile(files[chosenIndex]); if (!includeEncoding.Equals(Encoding) && includeEncoding.WindowsCodePage != Encoding.WindowsCodePage) { //If the encodings do not match, add a warning //This is not critical, but it's a bad idea to mix and match character encodings within a routefile, as the auto-detection may sometimes be wrong Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "The text encoding of the $Include file " + files[chosenIndex] + " does not match that of the base routefile."); } string[] lines = System.IO.File.ReadAllLines(files[chosenIndex], includeEncoding); PreprocessSplitIntoExpressions(files[chosenIndex], lines, out expr, false, offsets[chosenIndex] + Expressions[i].TrackPositionOffset); int length = Expressions.Length; if (expr.Length == 0) { for (int ia = i; ia < Expressions.Length - 1; ia++) { Expressions[ia] = Expressions[ia + 1]; } Array.Resize(ref Expressions, length - 1); } else { Array.Resize(ref Expressions, length + expr.Length - 1); for (int ia = Expressions.Length - 1; ia >= i + expr.Length; ia--) { Expressions[ia] = Expressions[ia - expr.Length + 1]; } for (int ia = 0; ia < expr.Length; ia++) { Expressions[i + ia] = expr[ia]; } } i--; continueWithNextExpression = true; } break; case "$chr": case "$chruni": { int x; if (NumberFormats.TryParseIntVb6(s, out x)) { if (x < 0) { //Must be non-negative continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Index must be a non-negative character in " + t + Epilog); } else { Expressions[i].Text = Expressions[i].Text.Substring(0, j) + char.ConvertFromUtf32(x) + Expressions[i].Text.Substring(h + 1); } } else { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Index is invalid in " + t + Epilog); } } break; case "$chrascii": { int x; if (NumberFormats.TryParseIntVb6(s, out x)) { if (x < 0 || x > 127) { //Standard ASCII characters from 0-127 continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Index does not correspond to a valid ASCII character in " + t + Epilog); } else { Expressions[i].Text = Expressions[i].Text.Substring(0, j) + char.ConvertFromUtf32(x) + Expressions[i].Text.Substring(h + 1); } } else { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Index is invalid in " + t + Epilog); } } break; case "$rnd": { int m = s.IndexOf(";", StringComparison.Ordinal); if (m >= 0) { string s1 = s.Substring(0, m).TrimEnd(new char[] { }); string s2 = s.Substring(m + 1).TrimStart(new char[] { }); int x; if (NumberFormats.TryParseIntVb6(s1, out x)) { int y; if (NumberFormats.TryParseIntVb6(s2, out y)) { int z = x + (int)Math.Floor(Plugin.RandomNumberGenerator.NextDouble() * (y - x + 1)); Expressions[i].Text = Expressions[i].Text.Substring(0, j) + z.ToString(Culture) + Expressions[i].Text.Substring(h + 1); } else { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Index2 is invalid in " + t + Epilog); } } else { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Index1 is invalid in " + t + Epilog); } } else { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Two arguments are expected in " + t + Epilog); } } break; case "$sub": { l = 0; bool f = false; int m; for (m = h + 1; m < Expressions[i].Text.Length; m++) { switch (Expressions[i].Text[m]) { case '(': l++; break; case ')': l--; break; case '=': if (l == 0) { f = true; } break; default: if (!char.IsWhiteSpace(Expressions[i].Text[m])) { l = -1; } break; } if (f | l < 0) { break; } } if (f) { l = 0; int n; for (n = m + 1; n < Expressions[i].Text.Length; n++) { switch (Expressions[i].Text[n]) { case '(': l++; break; case ')': l--; break; } if (l < 0) { break; } } int x; if (NumberFormats.TryParseIntVb6(s, out x)) { if (x >= 0) { while (x >= Subs.Length) { Array.Resize(ref Subs, Subs.Length << 1); } Subs[x] = Expressions[i].Text.Substring(m + 1, n - m - 1).Trim(new char[] { }); Expressions[i].Text = Expressions[i].Text.Substring(0, j) + Expressions[i].Text.Substring(n); } else { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Index is expected to be non-negative in " + t + Epilog); } } else { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Index is invalid in " + t + Epilog); } } else { int x; if (NumberFormats.TryParseIntVb6(s, out x)) { if (x >= 0 & x < Subs.Length && Subs[x] != null) { Expressions[i].Text = Expressions[i].Text.Substring(0, j) + Subs[x] + Expressions[i].Text.Substring(h + 1); } else { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Index is out of range in " + t + Epilog); } } else { continueWithNextExpression = true; Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Index is invalid in " + t + Epilog); } } } break; } } } if (continueWithNextExpression) { break; } } } // handle comments introduced via chr, rnd, sub { int length = Expressions.Length; for (int i = 0; i < length; i++) { Expressions[i].Text = Expressions[i].Text.Trim(new char[] { }); if (Expressions[i].Text.Length != 0) { if (Expressions[i].Text[0] == ';') { for (int j = i; j < length - 1; j++) { Expressions[j] = Expressions[j + 1]; } length--; i--; } } else { for (int j = i; j < length - 1; j++) { Expressions[j] = Expressions[j + 1]; } length--; i--; } } if (length != Expressions.Length) { Array.Resize(ref Expressions, length); } } }
/// <summary>Parses a base track following object node</summary> /// <param name="Element">The XElement to parse</param> /// <param name="FileName">The filename of the containing XML file</param> /// <param name="Path">The path of the containing XML file</param> /// <param name="Train">The track following object to parse this node into</param> private static void ParseTrackFollowingObjectNode(XElement Element, string FileName, string Path, string ObjectPath, TrainManager.TrackFollowingObject Train) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string TrainDirectory = string.Empty; bool consistReversed = false; List <Game.TravelData> Data = new List <Game.TravelData>(); foreach (XElement SectionElement in Element.Elements()) { string Section = SectionElement.Name.LocalName; switch (SectionElement.Name.LocalName.ToLowerInvariant()) { case "stops": ParseStopNode(SectionElement, FileName, Data); break; case "train": foreach (XElement KeyNode in SectionElement.Elements()) { string Key = KeyNode.Name.LocalName; string Value = KeyNode.Value; int LineNumber = ((IXmlLineInfo)KeyNode).LineNumber; switch (Key.ToLowerInvariant()) { case "directory": { string trainDirectory = OpenBveApi.Path.CombineDirectory(Path, Value); if (!System.IO.Directory.Exists(trainDirectory)) { trainDirectory = OpenBveApi.Path.CombineFile(Program.FileSystem.InitialTrainFolder, Value); } if (!System.IO.Directory.Exists(trainDirectory)) { trainDirectory = OpenBveApi.Path.CombineFile(Program.FileSystem.TrainInstallationDirectory, Value); } if (!System.IO.Directory.Exists(trainDirectory)) { trainDirectory = OpenBveApi.Path.CombineFile(ObjectPath, Value); } if (!System.IO.Directory.Exists(trainDirectory)) { Interface.AddMessage(MessageType.Error, false, "Directory was not found in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } else { TrainDirectory = trainDirectory; } } break; case "reversed": int n; NumberFormats.TryParseIntVb6(Value, out n); if (n == 1 || Value.ToLowerInvariant() == "true") { consistReversed = true; } break; } } break; case "definition": foreach (XElement KeyNode in SectionElement.Elements()) { string Key = KeyNode.Name.LocalName; string Value = KeyNode.Value; int LineNumber = ((IXmlLineInfo)KeyNode).LineNumber; switch (Key.ToLowerInvariant()) { case "appearancetime": if (Value.Length != 0 && !Interface.TryParseTime(Value, out Train.AppearanceTime)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "appearancestartposition": if (Value.Length != 0 && !NumberFormats.TryParseDoubleVb6(Value, out Train.AppearanceStartPosition)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "appearanceendposition": if (Value.Length != 0 && !NumberFormats.TryParseDoubleVb6(Value, out Train.AppearanceEndPosition)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "leavetime": if (Value.Length != 0 && !Interface.TryParseTime(Value, out Train.LeaveTime)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; } } break; } } if (!Data.Any() || string.IsNullOrEmpty(TrainDirectory)) { return; } /* * First check for a train.ai file- Functionally identical, but allows for differently configured AI * trains not to show up as driveable */ string TrainData = OpenBveApi.Path.CombineFile(TrainDirectory, "train.ai"); if (!System.IO.File.Exists(TrainData)) { // Check for the standard driveable train.dat TrainData = OpenBveApi.Path.CombineFile(TrainDirectory, "train.dat"); } string ExteriorFile = OpenBveApi.Path.CombineFile(TrainDirectory, "extensions.cfg"); if (!System.IO.File.Exists(TrainData) || !System.IO.File.Exists(ExteriorFile)) { Interface.AddMessage(MessageType.Error, true, "The supplied train folder in TrackFollowingObject " + FileName + " did not contain a complete set of data."); return; } TrainDatParser.ParseTrainData(TrainData, TextEncoding.GetSystemEncodingFromFile(TrainData), Train); SoundCfgParser.ParseSoundConfig(TrainDirectory, Train); Train.AI = new Game.TrackFollowingObjectAI(Train, Data); UnifiedObject[] CarObjects = new UnifiedObject[Train.Cars.Length]; UnifiedObject[] BogieObjects = new UnifiedObject[Train.Cars.Length * 2]; UnifiedObject[] CouplerObjects = new UnifiedObject[Train.Cars.Length - 1]; ExtensionsCfgParser.ParseExtensionsConfig(System.IO.Path.GetDirectoryName(ExteriorFile), TextEncoding.GetSystemEncodingFromFile(ExteriorFile), ref CarObjects, ref BogieObjects, ref CouplerObjects, Train, true); int currentBogieObject = 0; for (int i = 0; i < Train.Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, false); if (so == null) { CarObjects[i] = null; } else { double sx = Train.Cars[i].Width; double sy = Train.Cars[i].Height; double sz = Train.Cars[i].Length; so.ApplyScale(sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object Train.Cars[i].LoadCarSections(CarObjects[i]); } //Load bogie objects if (BogieObjects[currentBogieObject] != null) { Train.Cars[i].FrontBogie.LoadCarSections(BogieObjects[currentBogieObject]); } currentBogieObject++; if (BogieObjects[currentBogieObject] != null) { Train.Cars[i].RearBogie.LoadCarSections(BogieObjects[currentBogieObject]); } currentBogieObject++; } // door open/close speed foreach (var Car in Train.Cars) { if (Car.Specs.DoorOpenFrequency <= 0.0) { if (Car.Doors[0].OpenSound.Buffer != null & Car.Doors[1].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer); Program.Sounds.LoadBuffer(Car.Doors[1].OpenSound.Buffer); double a = Car.Doors[0].OpenSound.Buffer.Duration; double b = Car.Doors[1].OpenSound.Buffer.Duration; Car.Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (Car.Doors[0].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer); double a = Car.Doors[0].OpenSound.Buffer.Duration; Car.Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (Car.Doors[1].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer); double b = Car.Doors[1].OpenSound.Buffer.Duration; Car.Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { Car.Specs.DoorOpenFrequency = 0.8; } } if (Car.Specs.DoorCloseFrequency <= 0.0) { if (Car.Doors[0].CloseSound.Buffer != null & Car.Doors[1].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer); Program.Sounds.LoadBuffer(Car.Doors[1].CloseSound.Buffer); double a = Car.Doors[0].CloseSound.Buffer.Duration; double b = Car.Doors[1].CloseSound.Buffer.Duration; Car.Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (Car.Doors[0].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer); double a = Car.Doors[0].CloseSound.Buffer.Duration; Car.Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (Car.Doors[1].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer); double b = Car.Doors[1].CloseSound.Buffer.Duration; Car.Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { Car.Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; Car.Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); Car.Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); Car.Specs.DoorOpenFrequency /= Car.Specs.DoorOpenPitch; Car.Specs.DoorCloseFrequency /= Car.Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ Car.Specs.DoorOpenPitch = 1.0; Car.Specs.DoorClosePitch = 1.0; } foreach (var Car in Train.Cars) { Car.FrontAxle.Follower.TrackIndex = Data[0].RailIndex; Car.RearAxle.Follower.TrackIndex = Data[0].RailIndex; Car.FrontBogie.FrontAxle.Follower.TrackIndex = Data[0].RailIndex; Car.FrontBogie.RearAxle.Follower.TrackIndex = Data[0].RailIndex; Car.RearBogie.FrontAxle.Follower.TrackIndex = Data[0].RailIndex; Car.RearBogie.RearAxle.Follower.TrackIndex = Data[0].RailIndex; } if (consistReversed) { Train.Reverse(); } Train.PlaceCars(Data[0].StopPosition); }
/// <summary>Parses a train travel stop node</summary> /// <param name="Element">The XElement to parse</param> /// <param name="FileName">The filename of the containing XML file</param> /// <param name="Data">The list of travel data to add this to</param> private static void ParseStopNode(XElement Element, string FileName, List <Game.TravelData> Data) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; foreach (XElement SectionElement in Element.Elements()) { string Section = SectionElement.Name.LocalName; switch (SectionElement.Name.LocalName.ToLowerInvariant()) { case "stop": { Game.TravelData NewData = new Game.TravelData(); double Decelerate = 0.0; double Accelerate = 0.0; double TargetSpeed = 0.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 "decelerate": if (Value.Length != 0 && !NumberFormats.TryParseDoubleVb6(Value, out Decelerate)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "stopposition": if (Value.Length != 0 && !NumberFormats.TryParseDoubleVb6(Value, out NewData.StopPosition)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "stoptime": if (Value.Length != 0 && !Interface.TryParseTime(Value, out NewData.StopTime)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "doors": { int door = 0; bool doorboth = false; switch (Value.ToLowerInvariant()) { case "l": case "left": door = -1; break; case "r": case "right": door = 1; break; case "n": case "none": case "neither": door = 0; break; case "b": case "both": doorboth = true; break; default: if (Value.Length != 0 && !NumberFormats.TryParseIntVb6(Value, out door)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); door = 0; } break; } NewData.OpenLeftDoors = door < 0.0 | doorboth; NewData.OpenRightDoors = door > 0.0 | doorboth; } break; case "accelerate": if (Value.Length != 0 && !NumberFormats.TryParseDoubleVb6(Value, out Accelerate)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "targetspeed": if (Value.Length != 0 && !NumberFormats.TryParseDoubleVb6(Value, out TargetSpeed)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "direction": { int d = 0; switch (Value.ToLowerInvariant()) { case "f": d = 1; break; case "r": d = -1; break; default: if (Value.Length != 0 && !NumberFormats.TryParseIntVb6(Value, out d)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; } if (d == 1 || d == -1) { NewData.Direction = (Game.TravelDirection)d; } } break; case "rail": if (Value.Length != 0 && !NumberFormats.TryParseIntVb6(Value, out NewData.RailIndex)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; } } NewData.Decelerate = Decelerate / 3.6; NewData.Accelerate = Accelerate / 3.6; NewData.TargetSpeed = TargetSpeed / 3.6; Data.Add(NewData); } break; } } }
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 "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)) { Interface.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)) { Interface.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)) { Interface.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)) { Interface.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)) { Interface.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)) { Interface.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 { 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); } 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)) { 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); } 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); } 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(new char[] { ',' }); 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.IsPlayerTrain) { 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 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); 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) { PanelXmlParser.ParsePanelXml(interiorFile, currentPath, Train, Car); Train.Cars[Car].CameraRestrictionMode = CameraRestrictionMode.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 = CameraRestrictionMode.On; } else if (interiorFile.ToLowerInvariant().EndsWith(".animated")) { UnifiedObject currentObject; Program.CurrentHost.LoadObject(interiorFile, Encoding.UTF8, out currentObject); var a = currentObject as AnimatedObjectCollection; if (a != null) { try { for (int i = 0; i < a.Objects.Length; i++) { Program.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 { 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 void ParsePanelAnimatedNode(XElement Element, string FileName, string TrainPath, TrainBase 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 ? 1.0 : 1.0 / (double)numberOfSectionElements; foreach (XElement SectionElement in Element.Elements()) { Plugin.CurrentProgress = Plugin.CurrentProgress + invfac * (double)currentSectionElement; if ((currentSectionElement & 4) == 0) { System.Threading.Thread.Sleep(1); if (Plugin.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)) { Plugin.currentHost.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 <CommandEntry> CommandEntries = new List <CommandEntry>(); CommandEntry CommandEntry = new 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)) { Plugin.currentHost.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)) { Plugin.currentHost.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)) { Plugin.currentHost.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)) { Plugin.currentHost.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) { Plugin.currentHost.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)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "soundentries": if (!KeyNode.HasElements) { Plugin.currentHost.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) { Plugin.currentHost.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; Plugin.currentHost.LoadObject(File, e, out currentObject); var a = currentObject as AnimatedObjectCollection; if (a != null) { for (int i = 0; i < a.Objects.Length; i++) { Plugin.currentHost.CreateDynamicObject(ref a.Objects[i].internalObject); } CarSection.Groups[GroupIndex].Elements = a.Objects; } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } } } break; } } } break; } } }
private void ParseTrainCommand(TrainCommand Command, string[] Arguments, int Index, Expression Expression, ref RouteData Data, bool PreviewOnly) { switch (Command) { case TrainCommand.Interval: { if (!PreviewOnly) { List <double> intervals = new List <double>(); for (int k = 0; k < Arguments.Length; k++) { double o; if (!NumberFormats.TryParseDoubleVb6(Arguments[k], out o)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Interval " + k.ToString(Culture) + " is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); continue; } if (o == 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Interval " + k.ToString(Culture) + " must be non-zero in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); continue; } if (o > 43200 && Plugin.CurrentOptions.EnableBveTsHacks) { //Southern Blighton- Treston park has a runinterval of well over 24 hours, and there are likely others //Discard this Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Interval " + k.ToString(Culture) + " is greater than 12 hours in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); continue; } if (o < 120 && Plugin.CurrentOptions.EnableBveTsHacks) { /* * An AI train follows the same schedule / rules as the player train * ==> * x Waiting time before departure at the first station (30s to 1min is 'normal') * x Time to accelerate to linespeed * x Time to clear (as a minimum) the protecting signal on station exit * * When the runinterval is below ~2minutes, on large numbers of routes, this * shows up as a train overlapping the player train (bad....) */ o = 120; } intervals.Add(o); } intervals.Sort(); if (intervals.Count > 0) { CurrentRoute.PrecedingTrainTimeDeltas = intervals.ToArray(); } } } break; case TrainCommand.Velocity: { if (!PreviewOnly) { double limit = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseDoubleVb6(Arguments[0], out limit)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Speed is invalid in Train.Velocity at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); limit = 0.0; } Plugin.CurrentOptions.PrecedingTrainSpeedLimit = limit <= 0.0 ? double.PositiveInfinity : Data.UnitOfSpeed * limit; } } break; case TrainCommand.Folder: case TrainCommand.File: { if (PreviewOnly) { if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { if (Path.ContainsInvalidChars(Arguments[0])) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "FolderName contains illegal characters in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { Plugin.CurrentOptions.TrainName = Arguments[0]; } } } } break; case TrainCommand.Run: case TrainCommand.Rail: { if (!PreviewOnly) { if (Index < 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RailTypeIndex is out of range in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { int val = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out val)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RunSoundIndex is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); val = 0; } if (val < 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RunSoundIndex is expected to be non-negative in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); val = 0; } if (Index >= Data.Structure.Run.Length) { Array.Resize(ref Data.Structure.Run, Index + 1); } Data.Structure.Run[Index] = val; } } } break; case TrainCommand.Flange: { if (!PreviewOnly) { if (Index < 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "RailTypeIndex is out of range in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { int val = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !NumberFormats.TryParseIntVb6(Arguments[0], out val)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "FlangeSoundIndex is invalid in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); val = 0; } if (val < 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "FlangeSoundIndex expected to be non-negative in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); val = 0; } if (Index >= Data.Structure.Flange.Length) { Array.Resize(ref Data.Structure.Flange, Index + 1); } Data.Structure.Flange[Index] = val; } } } break; case TrainCommand.TimetableDay: { if (!PreviewOnly) { if (Index < 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "TimetableIndex is expected to be non-negative in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { if (Path.ContainsInvalidChars(Arguments[0])) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "FileName " + Arguments[0] + " contains illegal characters in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { while (Index >= Data.TimetableDaytime.Length) { int n = Data.TimetableDaytime.Length; Array.Resize(ref Data.TimetableDaytime, n << 1); for (int i = n; i < Data.TimetableDaytime.Length; i++) { Data.TimetableDaytime[i] = null; } } string f = string.Empty; if (!string.IsNullOrEmpty(TrainPath)) { f = Path.CombineFile(TrainPath, Arguments[0]); } if (!System.IO.File.Exists(f)) { f = Path.CombineFile(ObjectPath, Arguments[0]); } if (System.IO.File.Exists(f)) { Plugin.CurrentHost.RegisterTexture(f, new TextureParameters(null, null), out Data.TimetableDaytime[Index]); } else { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "DaytimeTimetable " + Index + " with FileName " + Arguments[0] + " was not found in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } } } } } break; case TrainCommand.TimetableNight: { if (!PreviewOnly) { if (Index < 0) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "TimetableIndex is expected to be non-negative in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { if (Path.ContainsInvalidChars(Arguments[0])) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "FileName " + Arguments[0] + " contains illegal characters in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { while (Index >= Data.TimetableNighttime.Length) { int n = Data.TimetableNighttime.Length; Array.Resize(ref Data.TimetableNighttime, n << 1); for (int i = n; i < Data.TimetableNighttime.Length; i++) { Data.TimetableNighttime[i] = null; } } string f = string.Empty; if (!string.IsNullOrEmpty(TrainPath)) { f = Path.CombineFile(TrainPath, Arguments[0]); } if (!System.IO.File.Exists(f)) { f = Path.CombineFile(ObjectPath, Arguments[0]); } if (System.IO.File.Exists(f)) { Plugin.CurrentHost.RegisterTexture(f, new TextureParameters(null, null), out Data.TimetableNighttime[Index]); } else { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "DaytimeTimetable " + Index + " with FileName " + Arguments[0] + " was not found in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } } } } } break; case TrainCommand.Destination: { if (!PreviewOnly) { if (Arguments.Length < 1) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, Command + " is expected to have one argument at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { if (!NumberFormats.TryParseIntVb6(Arguments[0], out Plugin.CurrentOptions.InitialDestination)) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Destination is expected to be an Integer in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } } } } break; } }
/// <summary>Parses any command-line arguments passed to the main program</summary> /// <param name="Arguments">A string array of arguments</param> /// <param name="Result">The main dialog result (Used to launch)</param> internal static void ParseArguments(string[] Arguments, ref formMain.MainDialogResult Result) { if (Arguments.Length == 0) { return; } for (int i = 0; i < Arguments.Length; i++) { int equals = Arguments[i].IndexOf('='); if (@equals >= 0) { string key = Arguments[i].Substring(0, @equals).Trim().ToLowerInvariant(); string value = Arguments[i].Substring(@equals + 1).Trim(); switch (key) { case "/route": Result.RouteFile = value; switch (TextEncoding.GetEncodingFromFile(Result.RouteFile)) { case TextEncoding.Encoding.Utf7: Result.RouteEncoding = System.Text.Encoding.UTF7; break; case TextEncoding.Encoding.Utf8: Result.RouteEncoding = System.Text.Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: Result.RouteEncoding = System.Text.Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: Result.RouteEncoding = System.Text.Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: Result.RouteEncoding = System.Text.Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: Result.RouteEncoding = System.Text.Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: Result.RouteEncoding = System.Text.Encoding.GetEncoding(932); break; case TextEncoding.Encoding.Windows1252: Result.RouteEncoding = System.Text.Encoding.GetEncoding(1252); break; case TextEncoding.Encoding.Big5: Result.RouteEncoding = System.Text.Encoding.GetEncoding(950); break; default: Result.RouteEncoding = Encoding.Default; break; } break; case "/train": Result.TrainFolder = value; switch (TextEncoding.GetEncodingFromFile(Result.TrainFolder, "train.txt")) { case TextEncoding.Encoding.Utf8: Result.TrainEncoding = System.Text.Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: Result.TrainEncoding = System.Text.Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: Result.TrainEncoding = System.Text.Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: Result.TrainEncoding = System.Text.Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: Result.TrainEncoding = System.Text.Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: Result.TrainEncoding = System.Text.Encoding.GetEncoding(932); break; case TextEncoding.Encoding.Windows1252: Result.TrainEncoding = System.Text.Encoding.GetEncoding(1252); break; case TextEncoding.Encoding.Big5: Result.TrainEncoding = System.Text.Encoding.GetEncoding(950); break; default: Result.TrainEncoding = Encoding.Default; break; } break; case "/station": Result.InitialStation = value; break; case "/time": Interface.TryParseTime(value, out Result.StartTime); break; case "/ai": if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "1") { Result.AIDriver = true; } break; case "/fullscreen": if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "1") { Result.FullScreen = true; } break; case "/width": NumberFormats.TryParseIntVb6(value, out Result.Width); break; case "/height": NumberFormats.TryParseIntVb6(value, out Result.Height); break; } } } }
/// <summary> /// Function to parse train definition /// </summary> /// <param name="ObjectPath">Absolute path to the object folder of route data</param> /// <param name="FileName">The filename of the containing XML file</param> /// <param name="SectionElement">The XElement to parse</param> /// <param name="TrainDirectory">Absolute path to the train directory</param> /// <param name="ConsistReversed">Whether to reverse the train composition.</param> private static void ParseTrainNode(string ObjectPath, string FileName, XElement SectionElement, ref string TrainDirectory, ref bool ConsistReversed) { string Section = SectionElement.Name.LocalName; foreach (XElement KeyNode in SectionElement.Elements()) { string Key = KeyNode.Name.LocalName; string Value = KeyNode.Value; int LineNumber = ((IXmlLineInfo)KeyNode).LineNumber; switch (Key.ToLowerInvariant()) { case "directory": { string TmpPath = OpenBveApi.Path.CombineDirectory(System.IO.Path.GetDirectoryName(FileName), Value); if (!Directory.Exists(TmpPath)) { TmpPath = OpenBveApi.Path.CombineFile(Program.FileSystem.InitialTrainFolder, Value); } if (!Directory.Exists(TmpPath)) { TmpPath = OpenBveApi.Path.CombineFile(Program.FileSystem.TrainInstallationDirectory, Value); } if (!Directory.Exists(TmpPath)) { TmpPath = OpenBveApi.Path.CombineFile(ObjectPath, Value); } if (!Directory.Exists(TmpPath)) { Interface.AddMessage(MessageType.Error, false, $"Directory was not found in {Key} in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); } else { TrainDirectory = TmpPath; } } break; case "reversed": if (Value.Any()) { switch (Value.ToLowerInvariant()) { case "true": ConsistReversed = true; break; case "false": ConsistReversed = false; break; default: { int n; if (!NumberFormats.TryParseIntVb6(Value, out n)) { Interface.AddMessage(MessageType.Error, false, $"Value is invalid in {Key} in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); } else { ConsistReversed = Convert.ToBoolean(n); } } break; } } break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {Key} encountered in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); break; } } }
/// <summary>Loads the sound set for a BVE4 or openBVE sound.cfg based train</summary> /// <param name="train">The train</param> /// <param name="FileName">The absolute on-disk path to the sound.cfg file</param> /// <param name="trainFolder">The absolute on-disk path to the train's folder</param> internal void Parse(string FileName, string trainFolder, TrainBase train) { //Default sound positions and radii //3D center of the car Vector3 center = Vector3.Zero; //Positioned to the left of the car, but centered Y & Z Vector3 left = new Vector3(-1.3, 0.0, 0.0); //Positioned to the right of the car, but centered Y & Z Vector3 right = new Vector3(1.3, 0.0, 0.0); //Positioned at the front of the car, centered X and Y Vector3 front = new Vector3(0.0, 0.0, 0.5 * train.Cars[train.DriverCar].Length); //Positioned at the position of the panel / 3D cab (Remember that the panel is just an object in the world...) Vector3 panel = new Vector3(train.Cars[train.DriverCar].Driver.X, train.Cars[train.DriverCar].Driver.Y, train.Cars[train.DriverCar].Driver.Z + 1.0); //Radius at which the sound is audible at full volume, presumably in m //TODO: All radii are much too small in external mode, but we can't change them by default..... Encoding Encoding = TextEncoding.GetSystemEncodingFromFile(FileName); // parse configuration file System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; List <string> Lines = System.IO.File.ReadAllLines(FileName, Encoding).ToList(); for (int i = Lines.Count - 1; i >= 0; i--) { /* * Strip comments and remove empty resulting lines etc. * * This fixes an error with some NYCTA content, which has * a copyright notice instead of the file header specified.... */ int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).Trim(new char[] { }); } else { Lines[i] = Lines[i].Trim(new char[] { }); } if (string.IsNullOrEmpty(Lines[i])) { Lines.RemoveAt(i); } } if (Lines.Count == 0) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Empty sound.cfg encountered in " + FileName + "."); } else if (string.Compare(Lines[0], "version 1.0", StringComparison.OrdinalIgnoreCase) != 0) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid file format encountered in " + FileName + ". The first line is expected to be \"Version 1.0\"."); } string[] MotorFiles = new string[] { }; double invfac = Lines.Count == 0 ? 1.0 : 1.0 / Lines.Count; for (int i = 0; i < Lines.Count; i++) { Plugin.CurrentProgress = Plugin.CurrentProgress + invfac * i; if ((i & 7) == 0) { System.Threading.Thread.Sleep(1); if (Plugin.Cancel) { return; } } switch (Lines[i].ToLowerInvariant()) { case "[run]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].Sounds.Run == null) { train.Cars[c].Sounds.Run = new Dictionary <int, CarSound>(); } if (train.Cars[c].Sounds.Run.ContainsKey(k)) { train.Cars[c].Sounds.Run[k] = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center); } else { train.Cars[c].Sounds.Run.Add(k, new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center)); } } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "RunIndex must be greater than or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } i++; } i--; break; case "[flange]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].Sounds.Flange.ContainsKey(k)) { train.Cars[c].Sounds.Flange[k] = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center); } else { train.Cars[c].Sounds.Flange.Add(k, new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center)); } } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "FlangeIndex must be greater than or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } i++; } i--; break; case "[motor]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { if (k >= MotorFiles.Length) { Array.Resize(ref MotorFiles, k + 1); } MotorFiles[k] = Path.CombineFile(trainFolder, b); if (!System.IO.File.Exists(MotorFiles[k])) { Plugin.currentHost.AddMessage(MessageType.Error, true, "File " + MotorFiles[k] + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); MotorFiles[k] = null; } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "MotorIndex must be greater than or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } i++; } i--; break; case "[switch]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); int switchIndex; if (NumberFormats.TryParseIntVb6(a, out switchIndex)) { if (switchIndex < 0) { Plugin.currentHost.AddMessage(MessageType.Error, false, "SwitchIndex must be greater than or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); continue; } for (int c = 0; c < train.Cars.Length; c++) { int n = train.Cars[c].FrontAxle.PointSounds.Length; if (switchIndex >= n) { Array.Resize(ref train.Cars[c].FrontAxle.PointSounds, switchIndex + 1); Array.Resize(ref train.Cars[c].RearAxle.PointSounds, switchIndex + 1); for (int h = n; h < switchIndex; h++) { train.Cars[c].FrontAxle.PointSounds[h] = new CarSound(); train.Cars[c].RearAxle.PointSounds[h] = new CarSound(); } } Vector3 frontaxle = new Vector3(0.0, 0.0, train.Cars[c].FrontAxle.Position); Vector3 rearaxle = new Vector3(0.0, 0.0, train.Cars[c].RearAxle.Position); train.Cars[c].FrontAxle.PointSounds[switchIndex] = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, frontaxle); train.Cars[c].RearAxle.PointSounds[switchIndex] = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, rearaxle); } } else { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported index " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; break; case "[brake]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "bc release high": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].CarBrake.AirHigh = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, center); } break; case "bc release": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].CarBrake.Air = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, center); } break; case "bc release full": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].CarBrake.AirZero = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, center); } break; case "emergency": train.Handles.EmergencyBrake.ApplicationSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center); break; case "emergencyrelease": train.Handles.EmergencyBrake.ReleaseSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center); break; case "bp decomp": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].CarBrake.Release = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, center); } break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[compressor]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].CarBrake.brakeType == BrakeType.Main) { switch (a.ToLowerInvariant()) { case "attack": train.Cars[c].CarBrake.airCompressor.StartSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center); break; case "loop": train.Cars[c].CarBrake.airCompressor.LoopSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center); break; case "release": train.Cars[c].CarBrake.airCompressor.EndSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center); break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } } i++; } i--; break; case "[suspension]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "left": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.SpringL = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, left); } break; case "right": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.SpringR = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, right); } break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[horn]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { //PRIMARY HORN (Enter) case "primarystart": Plugin.currentHost.RegisterSound(Path.CombineFile(trainFolder, b), SoundCfgParser.largeRadius, out var primaryStart); train.Cars[train.DriverCar].Horns[0].StartSound = primaryStart as SoundBuffer; train.Cars[train.DriverCar].Horns[0].SoundPosition = front; train.Cars[train.DriverCar].Horns[0].StartEndSounds = true; break; case "primaryend": case "primaryrelease": Plugin.currentHost.RegisterSound(Path.CombineFile(trainFolder, b), SoundCfgParser.largeRadius, out var primaryEnd); train.Cars[train.DriverCar].Horns[0].EndSound = primaryEnd as SoundBuffer; train.Cars[train.DriverCar].Horns[0].SoundPosition = front; train.Cars[train.DriverCar].Horns[0].StartEndSounds = true; break; case "primaryloop": case "primary": Plugin.currentHost.RegisterSound(Path.CombineFile(trainFolder, b), SoundCfgParser.largeRadius, out var primaryLoop); train.Cars[train.DriverCar].Horns[0].LoopSound = primaryLoop as SoundBuffer; train.Cars[train.DriverCar].Horns[0].SoundPosition = front; train.Cars[train.DriverCar].Horns[0].Loop = false; break; //SECONDARY HORN (Numpad Enter) case "secondarystart": Plugin.currentHost.RegisterSound(Path.CombineFile(trainFolder, b), SoundCfgParser.largeRadius, out var secondaryStart); train.Cars[train.DriverCar].Horns[1].StartSound = secondaryStart as SoundBuffer; train.Cars[train.DriverCar].Horns[1].SoundPosition = front; train.Cars[train.DriverCar].Horns[1].StartEndSounds = true; break; case "secondaryend": case "secondaryrelease": Plugin.currentHost.RegisterSound(Path.CombineFile(trainFolder, b), SoundCfgParser.largeRadius, out var secondaryEnd); train.Cars[train.DriverCar].Horns[1].EndSound = secondaryEnd as SoundBuffer; train.Cars[train.DriverCar].Horns[1].SoundPosition = front; train.Cars[train.DriverCar].Horns[1].StartEndSounds = true; break; case "secondaryloop": case "secondary": Plugin.currentHost.RegisterSound(Path.CombineFile(trainFolder, b), SoundCfgParser.largeRadius, out var secondaryLoop); train.Cars[train.DriverCar].Horns[1].LoopSound = secondaryLoop as SoundBuffer; train.Cars[train.DriverCar].Horns[1].SoundPosition = front; train.Cars[train.DriverCar].Horns[1].Loop = false; break; //MUSIC HORN case "musicstart": Plugin.currentHost.RegisterSound(Path.CombineFile(trainFolder, b), SoundCfgParser.mediumRadius, out var musicStart); train.Cars[train.DriverCar].Horns[2].StartSound = musicStart as SoundBuffer; train.Cars[train.DriverCar].Horns[2].SoundPosition = front; train.Cars[train.DriverCar].Horns[2].StartEndSounds = true; break; case "musicend": case "musicrelease": Plugin.currentHost.RegisterSound(Path.CombineFile(trainFolder, b), SoundCfgParser.mediumRadius, out var musicEnd); train.Cars[train.DriverCar].Horns[2].EndSound = musicEnd as SoundBuffer; train.Cars[train.DriverCar].Horns[2].SoundPosition = front; train.Cars[train.DriverCar].Horns[2].StartEndSounds = true; break; case "musicloop": case "music": Plugin.currentHost.RegisterSound(Path.CombineFile(trainFolder, b), SoundCfgParser.mediumRadius, out var musicLoop); train.Cars[train.DriverCar].Horns[2].LoopSound = musicLoop as SoundBuffer; train.Cars[train.DriverCar].Horns[2].SoundPosition = front; train.Cars[train.DriverCar].Horns[2].Loop = true; break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[door]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "open left": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Doors[0].OpenSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, left); } break; case "open right": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Doors[1].OpenSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, right); } break; case "close left": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Doors[0].CloseSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, left); } break; case "close right": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Doors[1].CloseSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, right); } break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[ats]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { int n = train.Cars[train.DriverCar].Sounds.Plugin.Length; if (k >= n) { Array.Resize(ref train.Cars[train.DriverCar].Sounds.Plugin, k + 1); for (int h = n; h < k; h++) { train.Cars[train.DriverCar].Sounds.Plugin[h] = new CarSound(); } } train.Cars[train.DriverCar].Sounds.Plugin[k] = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); } else { Plugin.currentHost.AddMessage(MessageType.Warning, false, "Index must be greater than or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } i++; } i--; break; case "[buzzer]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "correct": train.SafetySystems.StationAdjust.AdjustAlarm = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[pilot lamp]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "on": train.SafetySystems.PilotLamp.OnSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "off": train.SafetySystems.PilotLamp.OffSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[brake handle]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "apply": train.Handles.Brake.Increase = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "applyfast": train.Handles.Brake.IncreaseFast = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "release": train.Handles.Brake.Decrease = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "releasefast": train.Handles.Brake.DecreaseFast = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "min": train.Handles.Brake.Min = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "max": train.Handles.Brake.Max = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[master controller]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "up": train.Handles.Power.Increase = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "upfast": train.Handles.Power.IncreaseFast = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "down": train.Handles.Power.Decrease = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "downfast": train.Handles.Power.DecreaseFast = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "min": train.Handles.Power.Min = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "max": train.Handles.Power.Max = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[reverser]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "on": train.Handles.Reverser.EngageSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "off": train.Handles.Reverser.ReleaseSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[breaker]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "on": train.Cars[train.DriverCar].Breaker.Resume = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, panel); break; case "off": train.Cars[train.DriverCar].Breaker.ResumeOrInterrupt = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.smallRadius, panel); break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[others]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "noise": for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].Specs.IsMotorCar | c == train.DriverCar) { train.Cars[c].Sounds.Loop = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center); } } break; case "shoe": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].CarBrake.Rub = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.mediumRadius, center); } break; case "halt": for (int c = 0; c < train.Cars.Length; c++) { train.SafetySystems.PassAlarm.Sound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); } break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; case "[windscreen]": i++; while (i < Lines.Count && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = Lines[i].Substring(j + 1).TrimStart(new char[] { }); switch (a.ToLowerInvariant()) { case "raindrop": train.Cars[train.DriverCar].Windscreen.DropSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "wetwipe": train.Cars[train.DriverCar].Windscreen.Wipers.WetWipeSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "drywipe": train.Cars[train.DriverCar].Windscreen.Wipers.DryWipeSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; case "switch": train.Cars[train.DriverCar].Windscreen.Wipers.SwitchSound = new CarSound(Plugin.currentHost, trainFolder, FileName, i, b, SoundCfgParser.tinyRadius, panel); break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } i++; } i--; break; } } // motor sound for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].Specs.IsMotorCar) { train.Cars[c].Sounds.Motor.Position = center; for (int i = 0; i < train.Cars[c].Sounds.Motor.Tables.Length; i++) { train.Cars[c].Sounds.Motor.Tables[i].Buffer = null; train.Cars[c].Sounds.Motor.Tables[i].Source = null; for (int j = 0; j < train.Cars[c].Sounds.Motor.Tables[i].Entries.Length; j++) { int index = train.Cars[c].Sounds.Motor.Tables[i].Entries[j].SoundIndex; if (index >= 0 && index < MotorFiles.Length && MotorFiles[index] != null) { Plugin.currentHost.RegisterSound(MotorFiles[index], SoundCfgParser.mediumRadius, out var motorSound); train.Cars[c].Sounds.Motor.Tables[i].Entries[j].Buffer = motorSound as SoundBuffer; } } } } } }
/// <summary> /// Function to parse the contents of TravelStopData class /// </summary> /// <param name="FileName">The filename of the containing XML file</param> /// <param name="SectionElement">The XElement to parse</param> /// <returns>An instance of the new TravelStopData class with the parse result applied</returns> private static TravelStopData ParseTravelStopNode(string FileName, XElement SectionElement) { string Section = SectionElement.Name.LocalName; TravelStopData Data = new TravelStopData(); ParseTravelDataNode(FileName, SectionElement, Data); foreach (XElement KeyNode in SectionElement.Elements()) { string Key = KeyNode.Name.LocalName; string Value = KeyNode.Value; int LineNumber = ((IXmlLineInfo)KeyNode).LineNumber; switch (Key.ToLowerInvariant()) { case "stoptime": if (Value.Any() && !Interface.TryParseTime(Value, out Data.StopTime)) { Interface.AddMessage(MessageType.Error, false, $"Value is invalid in {Key} in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); } break; case "doors": { int Door = 0; bool DoorBoth = false; switch (Value.ToLowerInvariant()) { case "l": case "left": Door = -1; break; case "r": case "right": Door = 1; break; case "n": case "none": case "neither": Door = 0; break; case "b": case "both": DoorBoth = true; break; default: if (Value.Any() && !NumberFormats.TryParseIntVb6(Value, out Door)) { Interface.AddMessage(MessageType.Error, false, $"Value is invalid in {Key} in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); } break; } Data.OpenLeftDoors = Door < 0.0 | DoorBoth; Data.OpenRightDoors = Door > 0.0 | DoorBoth; } break; case "direction": { int d = 0; switch (Value.ToLowerInvariant()) { case "f": d = 1; break; case "r": d = -1; break; default: if (Value.Any() && (!NumberFormats.TryParseIntVb6(Value, out d) || !Enum.IsDefined(typeof(TravelDirection), d))) { Interface.AddMessage(MessageType.Error, false, $"Value is invalid in {Key} in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); d = 1; } break; } Data.Direction = (TravelDirection)d; } break; case "decelerate": case "position": case "stopposition": case "accelerate": case "targetspeed": case "rail": // Already parsed break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {Key} encountered in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); break; } } return(Data); }
/// <summary>Loads a list of compatibility signal objects</summary> /// <param name="currentHost">The host application interface</param> /// <param name="fileName">The file name of the object list</param> /// <param name="objects">The returned array of speed limits</param> /// <param name="signalPost">Sets the default signal post</param> /// <param name="speedLimits">The array of signal speed limits</param> /// <returns>An array of compatability signal objects</returns> public static void ReadCompatibilitySignalXML(HostInterface currentHost, string fileName, out CompatibilitySignalObject[] objects, out UnifiedObject signalPost, out double[] speedLimits) { signalPost = new StaticObject(currentHost); objects = new CompatibilitySignalObject[9]; //Default Japanese speed limits converted to m/s speedLimits = new[] { 0.0, 6.94444444444444, 15.2777777777778, 20.8333333333333, double.PositiveInfinity, double.PositiveInfinity }; XmlDocument currentXML = new XmlDocument(); currentXML.Load(fileName); string currentPath = System.IO.Path.GetDirectoryName(fileName); if (currentXML.DocumentElement != null) { XmlNode node = currentXML.SelectSingleNode("/openBVE/CompatibilitySignals/SignalSetName"); if (node != null) { currentHost.AddMessage(MessageType.Information, false, "INFO: Using the " + node.InnerText + " compatibility signal set."); } XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/CompatibilitySignals/Signal"); if (DocumentNodes != null) { int index = 0; foreach (XmlNode nn in DocumentNodes) { List <StaticObject> objectList = new List <StaticObject>(); List <int> aspectList = new List <int>(); try { if (nn.HasChildNodes) { foreach (XmlNode n in nn.ChildNodes) { if (n.Name != "Aspect") { continue; } int aspect = 0; if (!NumberFormats.TryParseIntVb6(n.Attributes["Number"].Value, out aspect)) { currentHost.AddMessage(MessageType.Error, true, "Invalid aspect number " + aspect + " in the signal object list in the compatability signal file " + fileName); continue; } aspectList.Add(aspect); StaticObject staticObject = new StaticObject(currentHost); if (n.InnerText.ToLowerInvariant() != "null") { string objectFile = Path.CombineFile(currentPath, n.InnerText); if (File.Exists(objectFile)) { currentHost.LoadStaticObject(objectFile, Encoding.UTF8, false, out staticObject); } else { currentHost.AddMessage(MessageType.Error, true, "Compatibility signal file " + objectFile + " not found in " + fileName); } } objectList.Add(staticObject); } } } catch { currentHost.AddMessage(MessageType.Error, true, "An unexpected error was encountered whilst processing the compatability signal file " + fileName); } objects[index] = new CompatibilitySignalObject(aspectList.ToArray(), objectList.ToArray()); index++; } } string signalPostFile = Path.CombineFile(currentPath, "Japanese\\signal_post.csv"); //default plain post try { node = currentXML.SelectSingleNode("/openBVE/CompatibilitySignals/SignalPost"); if (node != null) { string newFile = Path.CombineFile(currentPath, node.InnerText); if (System.IO.File.Exists(newFile)) { signalPostFile = newFile; } } currentHost.LoadObject(signalPostFile, Encoding.UTF8, out signalPost); } catch { currentHost.AddMessage(MessageType.Error, true, "An unexpected error was encountered whilst processing the compatability signal file " + fileName); } DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/CompatibilitySignals/SpeedLimits"); if (DocumentNodes != null) { foreach (XmlNode nn in DocumentNodes) { try { if (nn.HasChildNodes) { foreach (XmlNode n in nn.ChildNodes) { if (n.Name != "Aspect") { continue; } int aspect = 0; if (n.Attributes != null) { if (!NumberFormats.TryParseIntVb6(n.Attributes["Number"].Value, out aspect)) { currentHost.AddMessage(MessageType.Error, true, "Invalid aspect number " + aspect + " in the speed limit list in the compatability signal file " + fileName); continue; } } if (aspect <= speedLimits.Length) { int l = speedLimits.Length; Array.Resize(ref speedLimits, aspect + 1); for (int i = l; i < speedLimits.Length; i++) { speedLimits[i] = double.PositiveInfinity; } if (!NumberFormats.TryParseDoubleVb6(n.InnerText, out speedLimits[aspect])) { speedLimits[aspect] = double.MaxValue; if (n.InnerText.ToLowerInvariant() != "unlimited") { currentHost.AddMessage(MessageType.Error, true, "Invalid speed limit provided for aspect " + aspect + " in the compatability signal file " + fileName); } } else { //convert to m/s as that's what we use internally speedLimits[aspect] *= 0.277777777777778; } } } } } catch { currentHost.AddMessage(MessageType.Error, true, "An unexpected error was encountered whilst processing the compatability signal file " + fileName); } } } } }