public override bool LoadObject(string path, System.Text.Encoding Encoding, out UnifiedObject Object) { if (base.LoadObject(path, Encoding, out Object)) { return(true); } if (System.IO.File.Exists(path) || System.IO.Directory.Exists(path)) { Encoding = TextEncoding.GetSystemEncodingFromFile(path, Encoding); for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) { if (Program.CurrentHost.Plugins[i].Object != null) { try { if (Program.CurrentHost.Plugins[i].Object.CanLoadObject(path)) { try { UnifiedObject obj; if (Program.CurrentHost.Plugins[i].Object.LoadObject(path, Encoding, out obj)) { obj.OptimizeObject(false, Interface.CurrentOptions.ObjectOptimizationBasicThreshold, true); Object = obj; StaticObject staticObject = Object as StaticObject; if (staticObject != null) { StaticObjectCache.Add(ValueTuple.Create(path, false), staticObject); return(true); } AnimatedObjectCollection aoc = Object as AnimatedObjectCollection; if (aoc != null) { AnimatedObjectCollectionCache.Add(path, aoc); } return(true); } Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " returned unsuccessfully at LoadObject"); } catch (Exception ex) { Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at LoadObject:" + ex.Message); } } } catch (Exception ex) { Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at CanLoadObject:" + ex.Message); } } } Interface.AddMessage(MessageType.Error, false, "No plugin found that is capable of loading object " + path); } else { ReportProblem(OpenBveApi.Hosts.ProblemType.PathNotFound, path); } Object = null; return(false); }
// load route internal static bool LoadRoute(Bitmap bitmap = null) { if (string.IsNullOrEmpty(CurrentRouteFile)) { return(false); } Renderer.UpdateViewport(); bool result; try { Encoding encoding = TextEncoding.GetSystemEncodingFromFile(CurrentRouteFile); Loading.Load(CurrentRouteFile, encoding, bitmap); result = true; } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); Game.Reset(); result = false; CurrentRouteFile = null; } if (Loading.Cancel) { result = false; CurrentRouteFile = null; } Renderer.Lighting.Initialize(); Renderer.InitializeVisibility(); return(result); }
public override bool LoadStaticObject(string path, System.Text.Encoding Encoding, bool PreserveVertices, out StaticObject Object) { Encoding = TextEncoding.GetSystemEncodingFromFile(path, Encoding); if (System.IO.File.Exists(path) || System.IO.Directory.Exists(path)) { for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) { if (Program.CurrentHost.Plugins[i].Object != null) { try { if (Program.CurrentHost.Plugins[i].Object.CanLoadObject(path)) { try { UnifiedObject unifiedObject; if (Program.CurrentHost.Plugins[i].Object.LoadObject(path, Encoding, out unifiedObject)) { if (unifiedObject is StaticObject) { unifiedObject.OptimizeObject(PreserveVertices, Interface.CurrentOptions.ObjectOptimizationBasicThreshold, true); Object = (StaticObject)unifiedObject; return(true); } Object = null; Interface.AddMessage(MessageType.Error, false, "Attempted to load " + path + " which is an animated object where only static objects are allowed."); } Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " returned unsuccessfully at LoadObject"); } catch (Exception ex) { Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at LoadObject:" + ex.Message); } } } catch (Exception ex) { Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at CanLoadObject:" + ex.Message); } } } Interface.AddMessage(MessageType.Error, false, "No plugin found that is capable of loading object " + path); } else { ReportProblem(OpenBveApi.Hosts.ProblemType.PathNotFound, path); } Object = null; return(false); }
// parse extensions config internal void ParseExtensionsConfig(string TrainPath, System.Text.Encoding Encoding, ref UnifiedObject[] CarObjects, ref UnifiedObject[] BogieObjects, ref UnifiedObject[] CouplerObjects, ref bool[] VisibleFromInterior, TrainBase Train) { bool[] CarObjectsReversed = new bool[Train.Cars.Length]; bool[] BogieObjectsReversed = new bool[Train.Cars.Length * 2]; bool[] CarsDefined = new bool[Train.Cars.Length]; bool[] BogiesDefined = new bool[Train.Cars.Length * 2]; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "extensions.cfg"); if (System.IO.File.Exists(FileName)) { Encoding = TextEncoding.GetSystemEncodingFromFile(FileName, Encoding); string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { 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[] { }); } } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[exterior]": // exterior i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { 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 n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { if (Path.ContainsInvalidChars(b)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { Plugin.currentHost.LoadObject(File, Encoding, out CarObjects[n]); } else { Plugin.currentHost.AddMessage(MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "The car index " + a + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; break; default: if (Lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = Lines[i].Substring(4, Lines[i].Length - 5); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { if (CarsDefined[n]) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Car " + n.ToString(Culture) + " has already been declared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } CarsDefined[n] = true; bool DefinedLength = false; bool DefinedAxles = false; i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { 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 "object": if (string.IsNullOrEmpty(b)) { Plugin.currentHost.AddMessage(MessageType.Error, true, "An empty car object was supplied at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } if (Path.ContainsInvalidChars(b)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { Plugin.currentHost.LoadObject(File, Encoding, out CarObjects[n]); } else { Plugin.currentHost.AddMessage(MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "length": { double m; if (double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out m)) { if (m > 0.0) { Train.Cars[n].Length = m; Train.Cars[n].BeaconReceiverPosition = 0.5 * m; DefinedLength = true; } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "axles": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(new char[] { }); string d = b.Substring(k + 1).TrimStart(new char[] { }); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (rear >= front) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Cars[n].RearAxle.Position = rear; Train.Cars[n].FrontAxle.Position = front; DefinedAxles = true; } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "reversed": CarObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "loadingsway": Train.Cars[n].EnableLoadingSway = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "visiblefrominterior": VisibleFromInterior[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; if (DefinedLength & !DefinedAxles) { double AxleDistance = 0.4 * Train.Cars[n].Length; Train.Cars[n].RearAxle.Position = -AxleDistance; Train.Cars[n].FrontAxle.Position = AxleDistance; } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else if (Lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // coupler string t = Lines[i].Substring(8, Lines[i].Length - 9); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length - 1) { i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { 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 "distances": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(new char[] { }); string d = b.Substring(k + 1).TrimStart(new char[] { }); double min, max; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out min)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Minimum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out max)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Maximum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (min > max) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Minimum is expected to be less than Maximum in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Cars[n].Coupler.MinimumDistanceBetweenCars = min; Train.Cars[n].Coupler.MaximumDistanceBetweenCars = max; } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "object": if (string.IsNullOrEmpty(b)) { Plugin.currentHost.AddMessage(MessageType.Error, true, "An empty coupler object was supplied at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } if (Path.ContainsInvalidChars(b)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { Plugin.currentHost.LoadObject(File, Encoding, out CouplerObjects[n]); } else { Plugin.currentHost.AddMessage(MessageType.Error, true, "The coupler object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "The coupler index " + t + " does not reference an existing coupler at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "The coupler index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else if (Lines[i].StartsWith("[bogie", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = Lines[i].Substring(6, Lines[i].Length - 7); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n > BogiesDefined.Length - 1) { continue; } if (BogiesDefined[n]) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Bogie " + n.ToString(Culture) + " has already been declared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } BogiesDefined[n] = true; //Assuming that there are two bogies per car bool IsOdd = (n % 2 != 0); int CarIndex = n / 2; if (n >= 0 & n < Train.Cars.Length * 2) { bool DefinedAxles = false; i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { 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 "object": if (Path.ContainsInvalidChars(b)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (string.IsNullOrEmpty(b)) { Plugin.currentHost.AddMessage(MessageType.Error, true, "An empty bogie object was supplied at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { Plugin.currentHost.LoadObject(File, Encoding, out BogieObjects[n]); } else { Plugin.currentHost.AddMessage(MessageType.Error, true, "The bogie object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "length": { Plugin.currentHost.AddMessage(MessageType.Error, false, "A defined length is not supported for bogies at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "axles": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(new char[] { }); string d = b.Substring(k + 1).TrimStart(new char[] { }); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (rear >= front) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (IsOdd) { Train.Cars[CarIndex].FrontBogie.RearAxle.Position = rear; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = front; } else { Train.Cars[CarIndex].RearBogie.RearAxle.Position = rear; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = front; } DefinedAxles = true; } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "reversed": BogieObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; default: Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; if (!DefinedAxles) { if (IsOdd) { double AxleDistance = 0.4 * Train.Cars[CarIndex].FrontBogie.Length; Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -AxleDistance; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = AxleDistance; } else { double AxleDistance = 0.4 * Train.Cars[CarIndex].RearBogie.Length; Train.Cars[CarIndex].RearBogie.RearAxle.Position = -AxleDistance; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = AxleDistance; } } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Plugin.currentHost.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { // default if (Lines.Length == 1 && Encoding.Equals(Encoding.Unicode)) { /* * If only one line, there's a good possibility that our file is NOT Unicode at all * and that the misdetection has turned it into garbage * * Try again with ASCII instead */ ParseExtensionsConfig(TrainPath, Encoding.GetEncoding(1252), ref CarObjects, ref BogieObjects, ref CouplerObjects, ref VisibleFromInterior, Train); return; } Plugin.currentHost.AddMessage(MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; } } } // check for car objects and reverse if necessary int carObjects = 0; for (int i = 0; i < Train.Cars.Length; i++) { if (CarObjects[i] != null) { carObjects++; if (CarObjectsReversed[i]) { { // reverse axle positions double temp = Train.Cars[i].FrontAxle.Position; Train.Cars[i].FrontAxle.Position = -Train.Cars[i].RearAxle.Position; Train.Cars[i].RearAxle.Position = -temp; } if (CarObjects[i] is StaticObject) { StaticObject obj = (StaticObject)CarObjects[i].Clone(); obj.ApplyScale(-1.0, 1.0, -1.0); CarObjects[i] = obj; } else if (CarObjects[i] is AnimatedObjectCollection) { AnimatedObjectCollection obj = (AnimatedObjectCollection)CarObjects[i].Clone(); obj.Reverse(); CarObjects[i] = obj; } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int bogieObjects = 0; for (int i = 0; i < Train.Cars.Length * 2; i++) { bool IsOdd = (i % 2 != 0); int CarIndex = i / 2; if (BogieObjects[i] != null) { bogieObjects++; if (BogieObjectsReversed[i]) { { // reverse axle positions if (IsOdd) { double temp = Train.Cars[CarIndex].FrontBogie.FrontAxle.Position; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = -Train.Cars[CarIndex].FrontBogie.RearAxle.Position; Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -temp; } else { double temp = Train.Cars[CarIndex].RearBogie.FrontAxle.Position; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = -Train.Cars[CarIndex].RearBogie.RearAxle.Position; Train.Cars[CarIndex].RearBogie.RearAxle.Position = -temp; } } if (BogieObjects[i] is StaticObject) { StaticObject obj = (StaticObject)BogieObjects[i].Clone(); obj.ApplyScale(-1.0, 1.0, -1.0); BogieObjects[i] = obj; } else if (BogieObjects[i] is AnimatedObjectCollection) { AnimatedObjectCollection obj = (AnimatedObjectCollection)BogieObjects[i].Clone(); obj.Reverse(); BogieObjects[i] = obj; } else { throw new NotImplementedException(); } } } } if (carObjects > 0 & carObjects < Train.Cars.Length) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName); } if (bogieObjects > 0 & bogieObjects < Train.Cars.Length * 2) { Plugin.currentHost.AddMessage(MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + FileName); } } }
/// <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; } } } } } }
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 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); } } }
public static void ProcessFile(string pathIn, string pathOut, ConvertOption option, LogHandler callback) { callback(Message.FileStarted, pathIn); UnifiedObject unifiedObj = null; callback(Message.Reading); PluginManager.logHandler = callback; Encoding encoding = option.UseLocalEncoding ? Encoding.Default : TextEncoding.GetSystemEncodingFromFile(pathIn); bool isSketchupObjFile = IsSketchupObjFile(pathIn, encoding); if (PluginManager.LoadObject(pathIn, encoding, out unifiedObj)) { var staticObj = unifiedObj as StaticObject; callback(Message.OptimizingObject); staticObj.OptimizeObject(false, 0, true); callback(Message.OptimizingMaterial); OptimizeMaterial(ref staticObj.Mesh); callback(Message.PostProcessing); if (isSketchupObjFile) { callback(Message.SketchupHack); option.Mirror.Z ^= true; } if (option.Mirror != default(Bool3)) { staticObj.ApplyMirror(option.Mirror.X, option.Mirror.Y, option.Mirror.Z, option.Mirror.X, option.Mirror.Y, option.Mirror.Z); } if (option.Scale != default(Vector3) && option.Scale != new Vector3(1, 1, 1)) { staticObj.ApplyScale(option.Scale.X, option.Scale.Y, option.Scale.Z); } if (option.Rotation != default(Vector3)) { // TODO: This doesn't seem to be the correct way to apply euler angle rotation! if (option.Rotation.X != 0.0) { staticObj.ApplyRotation(new Vector3(1, 0, 0), option.Rotation.X); } if (option.Rotation.Y != 0.0) { staticObj.ApplyRotation(new Vector3(0, 1, 0), option.Rotation.X); } if (option.Rotation.Z != 0.0) { staticObj.ApplyRotation(new Vector3(0, 0, 1), option.Rotation.X); } } if (option.Translation != default(Vector3)) { staticObj.ApplyTranslation(option.Translation.X, option.Translation.Y, option.Translation.Z); } //var dimension = "[" + string.Join(", ", staticObj.Mesh.BoundingBox.Select(v => string.Format("({0}, {1}, {2})", v.X, v.Y, v.Z))) + "]"; //callback(Message.Dimension, dimension); if (pathOut.EndsWith(".csv", StringComparison.OrdinalIgnoreCase)) { callback(Message.Writing); CsvWriter.Save(staticObj, pathOut, option, callback); } else if (pathOut.EndsWith(".hmmo", StringComparison.OrdinalIgnoreCase)) { callback(Message.PostProcessing); staticObj.Mesh.Faces = Triangulate(staticObj.Mesh.Faces); callback(Message.Writing); // TODO: Hmmo Writer } } else { callback(Message.ObjectNotReadable); } callback(Message.FileDone, pathOut); }
internal static void Parse(string fileName, out Sound sound) { sound = new Sound(); CultureInfo culture = CultureInfo.InvariantCulture; List <string> lines = File.ReadAllLines(fileName, TextEncoding.GetSystemEncodingFromFile(fileName)).ToList(); string basePath = System.IO.Path.GetDirectoryName(fileName); 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(); } else { lines[i] = lines[i].Trim(); } if (string.IsNullOrEmpty(lines[i])) { lines.RemoveAt(i); } } if (!lines.Any()) { Interface.AddMessage(MessageType.Error, false, $"Empty sound.cfg encountered in {fileName}."); } if (string.Compare(lines[0], "version 1.0", StringComparison.OrdinalIgnoreCase) != 0) { Interface.AddMessage(MessageType.Error, false, $"Invalid file format encountered in {fileName}. The first line is expected to be \"Version 1.0\"."); } for (int i = 0; i < lines.Count; i++) { 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(); string b = lines[i].Substring(j + 1).TrimStart(); int k; if (!int.TryParse(a, NumberStyles.Integer, culture, out k)) { Interface.AddMessage(MessageType.Error, false, $"Invalid index appeared at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { if (k >= 0) { sound.SoundElements.Add(new RunElement { Key = k, FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Error, false, $"Index must be greater 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(); string b = lines[i].Substring(j + 1).TrimStart(); int k; if (!int.TryParse(a, NumberStyles.Integer, culture, out k)) { Interface.AddMessage(MessageType.Error, false, $"Invalid index appeared at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { if (k >= 0) { sound.SoundElements.Add(new FlangeElement { Key = k, FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Error, false, $"Index must be greater 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(); string b = lines[i].Substring(j + 1).TrimStart(); int k; if (!int.TryParse(a, NumberStyles.Integer, culture, out k)) { Interface.AddMessage(MessageType.Error, false, $"Invalid index appeared at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { if (k >= 0) { sound.SoundElements.Add(new MotorElement { Key = k, FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Error, false, $"Index is invalid 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(); string b = lines[i].Substring(j + 1).TrimStart(); int k; if (!int.TryParse(a, NumberStyles.Integer, culture, out k)) { Interface.AddMessage(MessageType.Error, false, $"Invalid index appeared at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { if (k >= 0) { sound.SoundElements.Add(new FrontSwitchElement { Key = k, FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Error, false, $"Index is invalid 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { BrakeKey[] keys = Enum.GetValues(typeof(BrakeKey)).OfType <BrakeKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new BrakeElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { CompressorKey[] keys = Enum.GetValues(typeof(CompressorKey)).OfType <CompressorKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new CompressorElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { SuspensionKey[] keys = Enum.GetValues(typeof(SuspensionKey)).OfType <SuspensionKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new SuspensionElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { HornKey[] primaryKeys = Enum.GetValues(typeof(HornKey)).OfType <HornKey>().Where(x => x.GetStringValues().Any(y => $"Primary{y}".Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); HornKey[] secondaryKeys = Enum.GetValues(typeof(HornKey)).OfType <HornKey>().Where(x => x.GetStringValues().Any(y => $"Secondary{y}".Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); HornKey[] musicKeys = Enum.GetValues(typeof(HornKey)).OfType <HornKey>().Where(x => x.GetStringValues().Any(y => $"Music{y}".Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (primaryKeys.Any()) { sound.SoundElements.Add(new PrimaryHornElement { Key = primaryKeys.First(), FilePath = Path.CombineFile(basePath, b) }); } else if (secondaryKeys.Any()) { sound.SoundElements.Add(new SecondaryHornElement { Key = secondaryKeys.First(), FilePath = Path.CombineFile(basePath, b) }); } else if (musicKeys.Any()) { sound.SoundElements.Add(new MusicHornElement { Key = musicKeys.First(), FilePath = Path.CombineFile(basePath, b) }); } else if ("Primary".Equals(a, StringComparison.InvariantCultureIgnoreCase)) { sound.SoundElements.Add(new PrimaryHornElement { Key = HornKey.Loop, FilePath = Path.CombineFile(basePath, b) }); } else if ("Secondary".Equals(a, StringComparison.InvariantCultureIgnoreCase)) { sound.SoundElements.Add(new SecondaryHornElement { Key = HornKey.Loop, FilePath = Path.CombineFile(basePath, b) }); } else if ("Music".Equals(a, StringComparison.InvariantCultureIgnoreCase)) { sound.SoundElements.Add(new MusicHornElement { Key = HornKey.Loop, FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { DoorKey[] keys = Enum.GetValues(typeof(DoorKey)).OfType <DoorKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new DoorElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { int k; if (!int.TryParse(a, NumberStyles.Integer, culture, out k)) { Interface.AddMessage(MessageType.Error, false, $"Invalid index appeared at line {(i + 1).ToString(culture)} in file {fileName}"); } else { if (k >= 0) { sound.SoundElements.Add(new AtsElement { Key = k, FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, "Index must be greater 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { BuzzerKey[] keys = Enum.GetValues(typeof(BuzzerKey)).OfType <BuzzerKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new BuzzerElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { PilotLampKey[] keys = Enum.GetValues(typeof(PilotLampKey)).OfType <PilotLampKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new PilotLampElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { BrakeHandleKey[] keys = Enum.GetValues(typeof(BrakeHandleKey)).OfType <BrakeHandleKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new BrakeHandleElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { MasterControllerKey[] keys = Enum.GetValues(typeof(MasterControllerKey)).OfType <MasterControllerKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new MasterControllerElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { ReverserKey[] keys = Enum.GetValues(typeof(ReverserKey)).OfType <ReverserKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new ReverserElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { BreakerKey[] keys = Enum.GetValues(typeof(BreakerKey)).OfType <BreakerKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new BreakerElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { OthersKey[] keys = Enum.GetValues(typeof(OthersKey)).OfType <OthersKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new OthersElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } i++; } i--; break; case "[requeststop]": 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { RequestStopKey[] keys = Enum.GetValues(typeof(RequestStopKey)).OfType <RequestStopKey>().Where(x => x.GetStringValues().Any(y => y.Equals(a, StringComparison.InvariantCultureIgnoreCase))).ToArray(); if (keys.Any()) { sound.SoundElements.Add(new RequestStopElement { Key = keys.First(), FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } } i++; } i--; break; case "[touch]": 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(); string b = lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"FileName contains illegal characters or is empty at line {(i + 1).ToString(culture)} in file {fileName}"); } else { int k; if (!int.TryParse(a, NumberStyles.Integer, culture, out k)) { Interface.AddMessage(MessageType.Error, false, $"Invalid index appeared at line {(i + 1).ToString(culture)} in file {fileName}"); } else { if (k >= 0) { sound.SoundElements.Add(new TouchElement { Key = k, FilePath = Path.CombineFile(basePath, b) }); } else { Interface.AddMessage(MessageType.Warning, false, "Index must be greater or equal to zero at line " + (i + 1).ToString(culture) + " in file " + fileName); } } } } i++; } i--; break; } } sound.SoundElements = new ObservableCollection <SoundElement>(sound.SoundElements.GroupBy(x => new { Type = x.GetType(), x.Key }).Select(x => x.First())); }
internal static void Parse(string fileName, out Train train) { train = new Train(); CultureInfo culture = CultureInfo.InvariantCulture; string[] lines = File.ReadAllLines(fileName, TextEncoding.GetSystemEncodingFromFile(fileName)); for (int i = 0; i < lines.Length; i++) { int j = lines[i].IndexOf(';'); if (j >= 0) { lines[i] = lines[i].Substring(0, j).Trim(); } else { lines[i] = lines[i].Trim(); } } bool ver1220000 = false; foreach (string line in lines) { if (line.Length != 0) { string s = line.ToLowerInvariant(); switch (s) { case "bve1200000": case "bve1210000": case "bve1220000": ver1220000 = true; break; case "bve2000000": case "openbve": //No action break; default: if (s.ToLowerInvariant().StartsWith("openbve")) { string tt = s.Substring(7, s.Length - 7); int v; if (int.TryParse(tt, NumberStyles.Float, culture, out v)) { if (v > currentVersion) { Interface.AddMessage(MessageType.Warning, false, $"The train.dat {fileName} was created with a newer version of openBVE. Please check for an update."); } } else { Interface.AddMessage(MessageType.Error, false, $"The train.dat version {lines[0].ToLowerInvariant()} is invalid in {fileName}"); } } break; } break; } } Acceleration acceleration = new Acceleration(); Performance performance = new Performance(); Delay delay = new Delay(); Move move = new Move(); Brake brake = new Brake(); Pressure pressure = new Pressure(); Motor motor = new Motor(); double motorCarMass = 40.0; int numberOfMotorCars = 1; double trailerCarMass = 40.0; int numberOfTrailerCars = 1; double lengthOfACar = 20.0; bool frontCarIsAMotorCar = false; double widthOfACar = 2.6; double heightOfACar = 3.2; double centerOfGravityHeight = 1.5; double exposedFrontalArea = 5.0; double unexposedFrontalArea = 1.6; for (int i = 0; i < lines.Length; i++) { int n = 0; switch (lines[i].ToLowerInvariant()) { case "#acceleration": i++; while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { if (n == acceleration.Entries.Count) { acceleration.Entries.Add(new Acceleration.Entry()); } string u = lines[i] + ","; int m = 0; while (true) { int j = u.IndexOf(','); if (j == -1) { break; } string s = u.Substring(0, j).Trim(); u = u.Substring(j + 1); double a; if (double.TryParse(s, NumberStyles.Float, culture, out a)) { switch (m) { case 0: acceleration.Entries[n].A0 = Math.Max(a, 0.0); break; case 1: acceleration.Entries[n].A1 = Math.Max(a, 0.0); break; case 2: acceleration.Entries[n].V1 = Math.Max(a, 0.0); break; case 3: acceleration.Entries[n].V2 = Math.Max(a, 0.0); if (acceleration.Entries[n].V2 < acceleration.Entries[n].V1) { double x = acceleration.Entries[n].V1; acceleration.Entries[n].V1 = acceleration.Entries[n].V2; acceleration.Entries[n].V2 = x; } break; case 4: if (ver1220000) { if (a <= 0.0) { acceleration.Entries[n].E = 1.0; } else { const double c = 1.23315173118822; acceleration.Entries[n].E = 1.0 - Math.Log(a) * acceleration.Entries[n].V2 * c; if (acceleration.Entries[n].E > 4.0) { acceleration.Entries[n].E = 4.0; } } } else { acceleration.Entries[n].E = a; } break; } } m++; } i++; n++; } i--; break; case "#performance": case "#deceleration": i++; while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { double a; if (double.TryParse(lines[i], NumberStyles.Float, culture, out a)) { switch (n) { case 0: if (a >= 0.0) { performance.Deceleration = a; } break; case 1: if (a >= 0.0) { performance.CoefficientOfStaticFriction = a; } break; case 3: if (a >= 0.0) { performance.CoefficientOfRollingResistance = a; } break; case 4: if (a >= 0.0) { performance.AerodynamicDragCoefficient = a; } break; } } i++; n++; } i--; break; case "#delay": i++; double[] delayPowerUp = delay.DelayPower.Select(x => x.Up).ToArray(); double[] delayPowerDown = delay.DelayPower.Select(x => x.Down).ToArray(); double[] delayBrakeUp = delay.DelayBrake.Select(x => x.Up).ToArray(); double[] delayBrakeDown = delay.DelayBrake.Select(x => x.Down).ToArray(); double[] delayLocoBrakeUp = delay.DelayLocoBrake.Select(x => x.Up).ToArray(); double[] delayLocoBrakeDown = delay.DelayLocoBrake.Select(x => x.Down).ToArray(); delay.DelayPower.Clear(); delay.DelayBrake.Clear(); delay.DelayLocoBrake.Clear(); while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { double a; if (double.TryParse(lines[i], NumberStyles.Float, culture, out a)) { switch (n) { case 0: if (a >= 0.0) { delayPowerUp = new[] { a }; } break; case 1: if (a >= 0.0) { delayPowerDown = new[] { a }; } break; case 2: if (a >= 0.0) { delayBrakeUp = new[] { a }; } break; case 3: if (a >= 0.0) { delayBrakeDown = new[] { a }; } break; } } else if (lines[i].IndexOf(',') != -1) { switch (n) { case 0: delayPowerUp = lines[i].Split(',').Select(x => double.Parse(x, culture)).ToArray(); break; case 1: delayPowerDown = lines[i].Split(',').Select(x => double.Parse(x, culture)).ToArray(); break; case 2: delayBrakeUp = lines[i].Split(',').Select(x => double.Parse(x, culture)).ToArray(); break; case 3: delayBrakeDown = lines[i].Split(',').Select(x => double.Parse(x, culture)).ToArray(); break; case 4: delayLocoBrakeUp = lines[i].Split(',').Select(x => double.Parse(x, culture)).ToArray(); break; case 5: delayLocoBrakeDown = lines[i].Split(',').Select(x => double.Parse(x, culture)).ToArray(); break; } } i++; n++; } for (int j = 0; j < Math.Max(delayPowerUp.Length, delayPowerDown.Length); j++) { Delay.Entry entry = new Delay.Entry(); if (j < delayPowerUp.Length) { entry.Up = delayPowerUp[j]; } if (j < delayPowerDown.Length) { entry.Down = delayPowerDown[j]; } delay.DelayPower.Add(entry); } for (int j = 0; j < Math.Max(delayBrakeUp.Length, delayBrakeDown.Length); j++) { Delay.Entry entry = new Delay.Entry(); if (j < delayBrakeUp.Length) { entry.Up = delayBrakeUp[j]; } if (j < delayBrakeDown.Length) { entry.Down = delayBrakeDown[j]; } delay.DelayBrake.Add(entry); } for (int j = 0; j < Math.Max(delayLocoBrakeUp.Length, delayLocoBrakeDown.Length); j++) { Delay.Entry entry = new Delay.Entry(); if (j < delayLocoBrakeUp.Length) { entry.Up = delayLocoBrakeUp[j]; } if (j < delayLocoBrakeDown.Length) { entry.Down = delayLocoBrakeDown[j]; } delay.DelayLocoBrake.Add(entry); } i--; break; case "#move": i++; while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { double a; if (double.TryParse(lines[i], NumberStyles.Float, culture, out a)) { switch (n) { case 0: if (a >= 0.0) { move.JerkPowerUp = a; } break; case 1: if (a >= 0.0) { move.JerkPowerDown = a; } break; case 2: if (a >= 0.0) { move.JerkBrakeUp = a; } break; case 3: if (a >= 0.0) { move.JerkBrakeDown = a; } break; case 4: if (a >= 0.0) { move.BrakeCylinderUp = a; } break; case 5: if (a >= 0.0) { move.BrakeCylinderDown = a; } break; } } i++; n++; } i--; break; case "#brake": i++; while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { double a; if (double.TryParse(lines[i], NumberStyles.Float, culture, out a)) { int b = (int)Math.Round(a); switch (n) { case 0: if (b >= 0 & b <= 2) { brake.BrakeType = (BrakeSystemType)b; } break; case 1: if (b >= 0 & b <= 2) { brake.BrakeControlSystem = (EletropneumaticBrakeType)b; } break; case 2: if (a >= 0.0) { brake.BrakeControlSpeed = a; } break; case 3: if (a <= 0 && a > 3) { brake.LocoBrakeType = (Brake.LocoBrakeTypes)b; } break; } } i++; n++; } i--; break; case "#pressure": i++; while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { double a; if (double.TryParse(lines[i], NumberStyles.Float, culture, out a)) { switch (n) { case 0: if (a > 0.0) { pressure.BrakeCylinderServiceMaximumPressure = a; } break; case 1: if (a > 0.0) { pressure.BrakeCylinderEmergencyMaximumPressure = a; } break; case 2: if (a > 0.0) { pressure.MainReservoirMinimumPressure = a; } break; case 3: if (a > 0.0) { pressure.MainReservoirMaximumPressure = a; } break; case 4: if (a > 0.0) { pressure.BrakePipeNormalPressure = a; } break; } } i++; n++; } i--; break; case "#handle": i++; while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { double a; if (double.TryParse(lines[i], NumberStyles.Float, culture, out a)) { int b = (int)Math.Round(a); switch (n) { case 0: if (b == 0 | b == 1) { train.Handle.HandleType = (Handle.HandleTypes)b; } break; case 1: if (b > 0) { train.Handle.PowerNotches = b; } break; case 2: if (b > 0) { train.Handle.BrakeNotches = b; } break; case 3: if (b >= 0) { train.Handle.PowerNotchReduceSteps = b; } break; case 4: if (a >= 0 && a < 4) { train.Handle.HandleBehaviour = (EbHandleBehaviour)b; } break; case 5: if (b > 0) { train.Handle.LocoBrakeNotches = b; } break; case 6: if (a <= 0 && a > 3) { train.Handle.LocoBrake = (LocoBrakeType)b; } break; case 7: if (b > 0) { train.Handle.DriverPowerNotches = b; } break; case 8: if (b > 0) { train.Handle.DriverBrakeNotches = b; } break; } } i++; n++; } i--; break; case "#cockpit": case "#cab": i++; while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { double a; if (double.TryParse(lines[i], NumberStyles.Float, culture, out a)) { switch (n) { case 0: train.Cab.PositionX = a; break; case 1: train.Cab.PositionY = a; break; case 2: train.Cab.PositionZ = a; break; case 3: train.Cab.DriverCar = (int)Math.Round(a); break; } } i++; n++; } i--; break; case "#car": i++; while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { double a; if (double.TryParse(lines[i], NumberStyles.Float, culture, out a)) { int b = (int)Math.Round(a); switch (n) { case 0: if (a > 0.0) { motorCarMass = a; } break; case 1: if (b >= 1) { numberOfMotorCars = b; } break; case 2: if (a > 0.0) { trailerCarMass = a; } break; case 3: if (b >= 0) { numberOfTrailerCars = b; } break; case 4: if (b > 0.0) { lengthOfACar = a; } break; case 5: frontCarIsAMotorCar = a == 1.0; break; case 6: if (a > 0.0) { widthOfACar = a; } break; case 7: if (a > 0.0) { heightOfACar = a; } break; case 8: centerOfGravityHeight = a; break; case 9: if (a > 0.0) { exposedFrontalArea = a; } break; case 10: if (a > 0.0) { unexposedFrontalArea = a; } break; } } i++; n++; } i--; break; case "#device": i++; while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { double a; if (double.TryParse(lines[i], NumberStyles.Float, culture, out a)) { int b = (int)Math.Round(a); switch (n) { case 0: if (b >= -1 & b <= 1) { train.Device.Ats = (AtsModes)b; } break; case 1: if (b >= 0 & b <= 2) { train.Device.Atc = (AtcModes)b; } break; case 2: train.Device.Eb = a == 1.0; break; case 3: train.Device.ConstSpeed = a == 1.0; break; case 4: train.Device.HoldBrake = a == 1.0; break; case 5: if (b >= -1 & b <= 3) { train.Device.ReAdhesionDevice = (ReadhesionDeviceType)b; } break; case 6: train.Device.LoadCompensatingDevice = a; break; case 7: if (b >= 0 & b <= 2) { train.Device.PassAlarm = (PassAlarmType)b; } break; case 8: if (b >= 0 & b <= 2) { train.Device.DoorOpenMode = (DoorMode)b; } break; case 9: if (b >= 0 & b <= 2) { train.Device.DoorCloseMode = (DoorMode)b; } break; case 10: if (a >= 0.0) { train.Device.DoorWidth = a; } break; case 11: if (a >= 0.0) { train.Device.DoorMaxTolerance = a; } break; } } i++; n++; } i--; break; case "#motor_p1": case "#motor_p2": case "#motor_b1": case "#motor_b2": { string section = lines[i].ToLowerInvariant(); i++; BVEMotorSoundTableEntry[] entries = new BVEMotorSoundTableEntry[800]; while (i < lines.Length && !lines[i].StartsWith("#", StringComparison.InvariantCultureIgnoreCase)) { if (n == entries.Length) { Array.Resize(ref entries, entries.Length << 1); } string u = lines[i] + ","; int k = 0; while (true) { int j = u.IndexOf(','); if (j == -1) { break; } string s = u.Substring(0, j).Trim(); u = u.Substring(j + 1); double a; if (double.TryParse(s, NumberStyles.Float, culture, out a)) { int b = (int)Math.Round(a); switch (k) { case 0: entries[n].SoundIndex = b >= 0 ? b : -1; break; case 1: entries[n].Pitch = (float)Math.Max(a, 0.0); break; case 2: entries[n].Gain = (float)Math.Max(a, 0.0); break; } } k++; } i++; n++; } Array.Resize(ref entries, n); i--; switch (section) { case "#motor_p1": motor.Tracks[(int)Motor.TrackInfo.Power1] = Motor.Track.EntriesToTrack(entries); break; case "#motor_p2": motor.Tracks[(int)Motor.TrackInfo.Power2] = Motor.Track.EntriesToTrack(entries); break; case "#motor_b1": motor.Tracks[(int)Motor.TrackInfo.Brake1] = Motor.Track.EntriesToTrack(entries); break; case "#motor_b2": motor.Tracks[(int)Motor.TrackInfo.Brake2] = Motor.Track.EntriesToTrack(entries); break; } } break; } } int numberOfCars = numberOfMotorCars + numberOfTrailerCars; bool[] isMotorCars = new bool[numberOfCars]; if (numberOfMotorCars == 1) { if (frontCarIsAMotorCar | numberOfTrailerCars == 0) { isMotorCars[0] = true; } else { isMotorCars[numberOfCars - 1] = true; } } else if (numberOfMotorCars == 2) { if (frontCarIsAMotorCar | numberOfTrailerCars == 0) { isMotorCars[0] = true; isMotorCars[numberOfCars - 1] = true; } else if (numberOfTrailerCars == 1) { isMotorCars[1] = true; isMotorCars[2] = true; } else { int i = (int)Math.Ceiling(0.25 * (numberOfCars - 1)); int j = (int)Math.Floor(0.75 * (numberOfCars - 1)); isMotorCars[i] = true; isMotorCars[j] = true; } } else if (numberOfMotorCars > 0) { if (frontCarIsAMotorCar) { isMotorCars[0] = true; double t = 1.0 + 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; } isMotorCars[i] = true; } } else { isMotorCars[1] = true; double t = 1.0 + (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; } isMotorCars[i] = true; } } } foreach (bool isMotorCar in isMotorCars) { Car car = isMotorCar ? (Car) new MotorCar() : new TrailerCar(); car.Mass = isMotorCar ? motorCarMass : trailerCarMass; car.Length = lengthOfACar; car.Width = widthOfACar; car.Height = heightOfACar; car.CenterOfGravityHeight = centerOfGravityHeight; car.ExposedFrontalArea = exposedFrontalArea; car.UnexposedFrontalArea = unexposedFrontalArea; car.Performance = (Performance)performance.Clone(); car.Delay = (Delay)delay.Clone(); car.Move = (Move)move.Clone(); car.Brake = (Brake)brake.Clone(); car.Pressure = (Pressure)pressure.Clone(); if (isMotorCar) { ((MotorCar)car).Acceleration = (Acceleration)acceleration.Clone(); ((MotorCar)car).Motor = (Motor)motor.Clone(); } train.Cars.Add(car); } for (int i = 0; i < numberOfCars - 1; i++) { train.Couplers.Add(new Models.Trains.Coupler()); } }
internal static void Parse(string fileName, Train train) { CultureInfo culture = CultureInfo.InvariantCulture; string[] lines = File.ReadAllLines(fileName, TextEncoding.GetSystemEncodingFromFile(fileName)); for (int i = 0; i < lines.Length; i++) { int j = lines[i].IndexOf(';'); if (j >= 0) { lines[i] = lines[i].Substring(0, j).Trim(); } else { lines[i] = lines[i].Trim(); } } for (int i = 0; i < lines.Length; i++) { if (lines[i].Any()) { switch (lines[i].ToLowerInvariant()) { case "[exterior]": i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Any()) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = lines[i].Substring(0, j).TrimEnd(); string b = lines[i].Substring(j + 1).TrimStart(); int n; if (int.TryParse(a, NumberStyles.Integer, culture, out n)) { if (n >= 0) { for (int k = n; k >= train.Cars.Count; k--) { train.Cars.Add(new TrailerCar()); train.Couplers.Add(new Coupler()); train.ApplyPowerNotchesToCar(); train.ApplyBrakeNotchesToCar(); train.ApplyLocoBrakeNotchesToCar(); } if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, $"An empty car object was supplied at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {fileName}"); } else { string file = Path.CombineFile(System.IO.Path.GetDirectoryName(fileName), b); if (!File.Exists(file)) { Interface.AddMessage(MessageType.Warning, true, $"The car object {file} does not exist at line {(i + 1).ToString(culture)} in file {fileName}"); } train.Cars[n].Object = file; } } else { Interface.AddMessage(MessageType.Error, false, $"The car index {n} does not reference an existing car at line {(i + 1).ToString(culture)} in file {fileName}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The car index is expected to be an integer at line {(i + 1).ToString(culture)} in file {fileName}"); } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } i++; } i--; break; default: if (lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = lines[i].Substring(4, lines[i].Length - 5); int n; if (int.TryParse(t, NumberStyles.Integer, culture, out n)) { if (n >= 0) { for (int j = n; j >= train.Cars.Count; j--) { train.Cars.Add(new TrailerCar()); train.Couplers.Add(new Coupler()); train.ApplyPowerNotchesToCar(); train.ApplyBrakeNotchesToCar(); train.ApplyLocoBrakeNotchesToCar(); } i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Any()) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = lines[i].Substring(0, j).TrimEnd(); string b = lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, $"An empty car object was supplied at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {fileName}"); } else { string file = Path.CombineFile(System.IO.Path.GetDirectoryName(fileName), b); if (!File.Exists(file)) { Interface.AddMessage(MessageType.Warning, true, $"The car object {file} does not exist at line {(i + 1).ToString(culture)} in file {fileName}"); } train.Cars[n].Object = file; } break; case "length": { double m; if (double.TryParse(b, NumberStyles.Float, culture, out m)) { if (m > 0.0) { train.Cars[n].Length = m; } else { Interface.AddMessage(MessageType.Error, false, $"Value is expected to be a positive floating-point number in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } } else { Interface.AddMessage(MessageType.Error, false, $"Value is expected to be a positive floating-point number in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } } break; case "axles": int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, NumberStyles.Float, culture, out rear)) { Interface.AddMessage(MessageType.Error, false, $"Rear is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (!double.TryParse(d, NumberStyles.Float, culture, out front)) { Interface.AddMessage(MessageType.Error, false, $"Front is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (rear >= front) { Interface.AddMessage(MessageType.Error, false, $"Rear is expected to be less than Front in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } else { train.Cars[n].RearAxle = rear; train.Cars[n].FrontAxle = front; train.Cars[n].DefinedAxles = true; } } else { Interface.AddMessage(MessageType.Error, false, $"An argument-separating comma is expected in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } break; case "reversed": train.Cars[n].Reversed = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "loadingsway": train.Cars[n].LoadingSway = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); break; } } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, $"The car index {t} does not reference an existing car at line {(i + 1).ToString(culture)} in file {fileName}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The car index is expected to be an integer at line {(i + 1).ToString(culture)} in file {fileName}"); } } else if (lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { // coupler string t = lines[i].Substring(8, lines[i].Length - 9); int n; if (int.TryParse(t, NumberStyles.Integer, culture, out n)) { if (n >= 0) { for (int j = n; j >= train.Couplers.Count; j--) { train.Cars.Add(new TrailerCar()); train.Couplers.Add(new Coupler()); train.ApplyPowerNotchesToCar(); train.ApplyBrakeNotchesToCar(); train.ApplyLocoBrakeNotchesToCar(); } i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Any()) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = lines[i].Substring(0, j).TrimEnd(); string b = lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "distances": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double min, max; if (!double.TryParse(c, NumberStyles.Float, culture, out min)) { Interface.AddMessage(MessageType.Error, false, $"Minimum is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (!double.TryParse(d, NumberStyles.Float, culture, out max)) { Interface.AddMessage(MessageType.Error, false, $"Maximum is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (min > max) { Interface.AddMessage(MessageType.Error, false, $"Minimum is expected to be less than Maximum in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } else { train.Couplers[n].Min = min; train.Couplers[n].Max = max; } } else { Interface.AddMessage(MessageType.Error, false, $"An argument-separating comma is expected in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } } break; case "object": if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, $"An empty coupler object was supplied at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {fileName}"); } else { string file = Path.CombineFile(System.IO.Path.GetDirectoryName(fileName), b); if (!File.Exists(file)) { Interface.AddMessage(MessageType.Warning, true, $"The coupler object {file} does not exist at line {(i + 1).ToString(culture)} in file {fileName}"); } train.Couplers[n].Object = file; } break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); break; } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, $"The coupler index {t} does not reference an existing coupler at line {(i + 1).ToString(culture)} in file {fileName}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The coupler index is expected to be an integer at line {(i + 1).ToString(culture)} in file {fileName}"); } } else if (lines[i].StartsWith("[bogie", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { // bogie string t = lines[i].Substring(6, lines[i].Length - 7); int n; if (int.TryParse(t, NumberStyles.Integer, culture, out n)) { //Assuming that there are two bogies per car bool IsOdd = (n % 2 != 0); int CarIndex = n / 2; if (n >= 0) { for (int j = CarIndex; j >= train.Cars.Count; j--) { train.Cars.Add(new TrailerCar()); train.Couplers.Add(new Coupler()); train.ApplyPowerNotchesToCar(); train.ApplyBrakeNotchesToCar(); train.ApplyLocoBrakeNotchesToCar(); } i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Any()) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = lines[i].Substring(0, j).TrimEnd(); string b = lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, $"An empty bogie object was supplied at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {fileName}"); } else { string file = Path.CombineFile(System.IO.Path.GetDirectoryName(fileName), b); if (!File.Exists(file)) { Interface.AddMessage(MessageType.Warning, true, $"The bogie object {file} does not exist at line {(i + 1).ToString(culture)} in file {fileName}"); } if (IsOdd) { train.Cars[CarIndex].RearBogie.Object = b; } else { train.Cars[CarIndex].FrontBogie.Object = b; } } break; case "axles": int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, NumberStyles.Float, culture, out rear)) { Interface.AddMessage(MessageType.Error, false, $"Rear is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (!double.TryParse(d, NumberStyles.Float, culture, out front)) { Interface.AddMessage(MessageType.Error, false, $"Front is expected to be a floating-point number in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } else if (rear >= front) { Interface.AddMessage(MessageType.Error, false, $"Rear is expected to be less than Front in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } else { if (IsOdd) { train.Cars[CarIndex].RearBogie.RearAxle = rear; train.Cars[CarIndex].RearBogie.FrontAxle = front; train.Cars[CarIndex].RearBogie.DefinedAxles = true; } else { train.Cars[CarIndex].FrontBogie.RearAxle = rear; train.Cars[CarIndex].FrontBogie.FrontAxle = front; train.Cars[CarIndex].FrontBogie.DefinedAxles = true; } } } else { Interface.AddMessage(MessageType.Error, false, $"An argument-separating comma is expected in {a} at line {(i + 1).ToString(culture)} in file {fileName}"); } break; case "reversed": if (IsOdd) { train.Cars[CarIndex].FrontBogie.Reversed = b.Equals("true", StringComparison.OrdinalIgnoreCase); } else { train.Cars[CarIndex].RearBogie.Reversed = b.Equals("true", StringComparison.OrdinalIgnoreCase); } break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); break; } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, $"The bogie index {t} does not reference an existing car at line {(i + 1).ToString(culture)} in file {fileName}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The bogie index is expected to be an integer at line {(i + 1).ToString(culture)} in file {fileName}"); } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {fileName}"); } break; } } } }