public static CombineFile ( string absolute, string relative ) : string | ||
absolute | string | The platform-specific absolute path. |
relative | string | The platform-independent relative path. |
리턴 | string |
/// <summary>Parses a openBVE panel.animated.xml file</summary> /// <param name="PanelFile">The relative path of the panel configuration file from the train</param> /// <param name="Train">The train</param> /// <param name="Car">The car index to add the panel to</param> internal void ParsePanelAnimatedXml(string PanelFile, TrainBase Train, int Car) { // The current XML file to load string FileName = PanelFile; if (!File.Exists(FileName)) { FileName = Path.CombineFile(Train.TrainFolder, PanelFile); } XDocument CurrentXML = XDocument.Load(FileName, LoadOptions.SetLineInfo); // Check for null if (CurrentXML.Root == null) { // We couldn't find any valid XML, so return false throw new System.IO.InvalidDataException(); } IEnumerable <XElement> DocumentElements = CurrentXML.Root.Elements("PanelAnimated"); // Check this file actually contains OpenBVE panel definition elements if (DocumentElements == null || !DocumentElements.Any()) { // We couldn't find any valid XML, so return false throw new System.IO.InvalidDataException(); } foreach (XElement element in DocumentElements) { ParsePanelAnimatedNode(element, FileName, Train.TrainFolder, Train, Car, Train.Cars[Car].CarSections[0], 0); } }
public override Image GetImage(string trainPath) { try { string imageFile = Path.CombineFile(trainPath, "train.png"); if (File.Exists(imageFile)) { return(Image.FromFile(imageFile)); } imageFile = Path.CombineFile(trainPath, "train.gif"); if (File.Exists(imageFile)) { return(Image.FromFile(imageFile)); } imageFile = Path.CombineFile(trainPath, "train.bmp"); if (File.Exists(imageFile)) { return(Image.FromFile(imageFile)); } } catch (Exception ex) { currentHost.ReportProblem(ProblemType.UnexpectedException, "Unable to get the image for train " + trainPath + " due to the exeception: " + ex.Message); } return(null); }
private static void routeWorkerThread_doWork(object sender, DoWorkEventArgs e) { if (string.IsNullOrEmpty(currentFile)) { return; } RouteEncoding = TextEncoding.GetSystemEncodingFromFile(currentFile); Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\loading.png"), new TextureParameters(null, null), out routePictureBox.Texture); routeDescriptionBox.Text = Translations.GetInterfaceString("start_route_processing"); Game.Reset(false); bool loaded = false; for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) { if (Program.CurrentHost.Plugins[i].Route != null && Program.CurrentHost.Plugins[i].Route.CanLoadRoute(currentFile)) { object Route = (object)Program.CurrentRoute; //must cast to allow us to use the ref keyword. string RailwayFolder = Loading.GetRailwayFolder(currentFile); string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); if (Program.CurrentHost.Plugins[i].Route.LoadRoute(currentFile, RouteEncoding, null, ObjectFolder, SoundFolder, true, ref Route)) { Program.CurrentRoute = (CurrentRoute)Route; } else { if (Program.CurrentHost.Plugins[i].Route.LastException != null) { throw Program.CurrentHost.Plugins[i].Route.LastException; //Re-throw last exception generated by the route parser plugin so that the UI thread captures it } routeDescriptionBox.Text = "An unknown error was enountered whilst attempting to parse the routefile " + currentFile; RoutefileState = RouteState.Error; } loaded = true; break; } } if (!loaded) { throw new Exception("No plugins capable of loading routefile " + currentFile + " were found."); } }
public override string GetDescription(string trainPath, Encoding encoding = null) { try { string descriptionFile = Path.CombineFile(trainPath, "train.txt"); if (File.Exists(descriptionFile)) { if (encoding == null) { return(File.ReadAllText(descriptionFile)); } return(File.ReadAllText(descriptionFile, encoding)); } } catch (Exception ex) { currentHost.ReportProblem(ProblemType.UnexpectedException, "Unable to get the description for train " + trainPath + " due to the exeception: " + ex.Message); } return(string.Empty); }
public override bool CanLoadTrain(string path) { string vehicleTxt = Path.CombineFile(path, "vehicle.txt"); if (File.Exists(vehicleTxt)) { string[] lines = File.ReadAllLines(vehicleTxt); for (int i = 10; i < lines.Length; i++) { if (lines[i].StartsWith(@"bvets vehicle ", StringComparison.InvariantCultureIgnoreCase)) { /* * BVE5 format train * When the BVE5 plugin is implemented, this should return false, as BVE5 trains * often seem to keep the train.dat lying around and we need to use the right plugin * * For the moment however, this is ignored.... */ } } } string trainDat = Path.CombineFile(path, "train.dat"); if (File.Exists(trainDat)) { return(true); } string trainXML = Path.CombineFile(path, "train.xml"); if (File.Exists(trainXML)) { /* * XML format train * At present, XML is used only as an extension, but acceleration etc. will be implemented * When this is done, return true here */ } return(false); }
/// <summary> /// Attempts to combine an absolute path /// </summary> /// <param name="absolute"></param> /// <param name="relative"></param> /// <param name="packageDirectory"></param> /// <returns></returns> public static string CombineFile(string absolute, string relative, string packageDirectory) { try { string file = Path.CombineFile(absolute, relative); //Trim relative = relative.Trim(); //Make all path separators conform relative = relative.Replace('/', '\\'); if (File.Exists(file)) { //First try a standard path combine return(file); } try { //Check the Loksim package install directory next if (File.Exists(Path.CombineFile(packageDirectory, relative))) { return(Path.CombineFile(packageDirectory, relative)); } } catch { } if (relative[0] == '\\') { //We now need to start a fuzzy find. //A char of '\' or '/' denotes a reference to the base Loksim3D directory //Split the path, and find the first directory in it string[] splitPath = relative.Split(new char['\\'], StringSplitOptions.RemoveEmptyEntries); DirectoryInfo d = new DirectoryInfo(absolute); while (d.Parent != null) { //Recurse upwards and try to see if we can find this directory d = d.Parent; if (d.ToString().ToLowerInvariant() == splitPath[0].ToLowerInvariant()) { d = d.Parent; file = Path.CombineFile(d.FullName, relative); if (File.Exists(file)) { return(file); } //Found the directory but not the file, so break out of the loop break; } } } if (!File.Exists(file)) { //Last-ditch attempt: Check User & Public for the Loksim object directory file = Path.CombineFile(Path.CombineFile(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Loksim3D"), relative); if (File.Exists(file)) { return(file); } file = Path.CombineFile(Path.CombineFile(Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments), "Loksim3D"), relative); } if (File.Exists(file)) { return(file); } return(absolute); } catch { return(absolute); } }
/// <summary>Gets the character encoding of a file within a folder</summary> /// <param name="Folder">The absolute path to the folder containing the file</param> /// <param name="File">The filename</param> /// <returns>The character encoding, or unknown</returns> public static Encoding GetEncodingFromFile(string Folder, string File) { return(GetEncodingFromFile(Path.CombineFile(Folder, File))); }
/// <summary>Gets the character system encoding of a file within a folder</summary> /// <param name="Folder">The absolute path to the folder containing the file</param> /// <param name="File">The filename</param> /// <param name="DefaultEncoding">The encoding to use if the encoding could not be determined. If not specified, the system default encoding is used.</param> /// <returns>The character system encoding, or default encoding if unknown</returns> public static System.Text.Encoding GetSystemEncodingFromFile(string Folder, string File, System.Text.Encoding DefaultEncoding = null) { return(GetSystemEncodingFromFile(Path.CombineFile(Folder, File), DefaultEncoding)); }
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())); }
/// <summary>Attempts to load and parse the current train's panel configuration file.</summary> /// <param name="Train">The train</param> /// <param name="Encoding">The selected train encoding</param> internal void ParsePanelConfig(TrainBase Train, Encoding Encoding) { Train.Cars[Train.DriverCar].CarSections = new CarSection[1]; Train.Cars[Train.DriverCar].CarSections[0] = new CarSection(currentHost, ObjectType.Overlay, true); string File = Path.CombineFile(Train.TrainFolder, "panel.xml"); if (!System.IO.File.Exists(File)) { //Try animated variant too File = Path.CombineFile(Train.TrainFolder, "panel.animated.xml"); } if (System.IO.File.Exists(File)) { FileSystem.AppendToLogFile("Loading train panel: " + File); try { /* * First load the XML. We use this to determine * whether this is a 2D or a 3D animated panel */ XDocument CurrentXML = XDocument.Load(File, LoadOptions.SetLineInfo); // Check for null if (CurrentXML.Root != null) { IEnumerable <XElement> DocumentElements = CurrentXML.Root.Elements("PanelAnimated"); if (DocumentElements.Any()) { PanelAnimatedXmlParser.ParsePanelAnimatedXml(System.IO.Path.GetFileName(File), Train, Train.DriverCar); if (Train.Cars[Train.DriverCar].CameraRestrictionMode != CameraRestrictionMode.Restricted3D) { Train.Cars[Train.DriverCar].CameraRestrictionMode = CameraRestrictionMode.NotAvailable; } return; } DocumentElements = CurrentXML.Root.Elements("Panel"); if (DocumentElements.Any()) { PanelXmlParser.ParsePanelXml(System.IO.Path.GetFileName(File), Train, Train.DriverCar); Train.Cars[Train.DriverCar].CameraRestrictionMode = CameraRestrictionMode.On; Renderer.Camera.CurrentRestriction = CameraRestrictionMode.On; return; } } } catch { var currentError = Translations.GetInterfaceString("errors_critical_file"); currentError = currentError.Replace("[file]", "panel.xml"); currentHost.ReportProblem(ProblemType.InvalidData, currentError); Cancel = true; return; } currentHost.AddMessage(MessageType.Error, false, "The panel.xml file " + File + " failed to load. Falling back to legacy panel."); } else { File = Path.CombineFile(Train.TrainFolder, "panel.animated"); if (System.IO.File.Exists(File)) { FileSystem.AppendToLogFile("Loading train panel: " + File); if (System.IO.File.Exists(Path.CombineFile(Train.TrainFolder, "panel2.cfg")) || System.IO.File.Exists(Path.CombineFile(Train.TrainFolder, "panel.cfg"))) { FileSystem.AppendToLogFile("INFO: This train contains both a 2D and a 3D panel. The 3D panel will always take precedence"); } UnifiedObject currentObject; currentHost.LoadObject(File, Encoding, out currentObject); var a = currentObject as AnimatedObjectCollection; if (a != null) { //HACK: If a == null , loading our animated object completely failed (Missing objects?). Fallback to trying the panel2.cfg try { for (int i = 0; i < a.Objects.Length; i++) { currentHost.CreateDynamicObject(ref a.Objects[i].internalObject); } Train.Cars[Train.DriverCar].CarSections[0].Groups[0].Elements = a.Objects; if (Train.Cars[Train.DriverCar].CameraRestrictionMode != CameraRestrictionMode.Restricted3D) { Train.Cars[Train.DriverCar].CameraRestrictionMode = CameraRestrictionMode.NotAvailable; Renderer.Camera.CurrentRestriction = CameraRestrictionMode.NotAvailable; } return; } catch { var currentError = Translations.GetInterfaceString("errors_critical_file"); currentError = currentError.Replace("[file]", "panel.animated"); currentHost.ReportProblem(ProblemType.InvalidData, currentError); Cancel = true; return; } } currentHost.AddMessage(MessageType.Error, false, "The panel.animated file " + File + " failed to load. Falling back to 2D panel."); } } var Panel2 = false; try { File = Path.CombineFile(Train.TrainFolder, "panel2.cfg"); if (System.IO.File.Exists(File)) { FileSystem.AppendToLogFile("Loading train panel: " + File); Panel2 = true; Panel2CfgParser.ParsePanel2Config("panel2.cfg", Train.TrainFolder, Train.Cars[Train.DriverCar]); Train.Cars[Train.DriverCar].CameraRestrictionMode = CameraRestrictionMode.On; Renderer.Camera.CurrentRestriction = CameraRestrictionMode.On; } else { File = Path.CombineFile(Train.TrainFolder, "panel.cfg"); if (System.IO.File.Exists(File)) { FileSystem.AppendToLogFile("Loading train panel: " + File); PanelCfgParser.ParsePanelConfig(Train.TrainFolder, Encoding, Train.Cars[Train.DriverCar]); Train.Cars[Train.DriverCar].CameraRestrictionMode = CameraRestrictionMode.On; Renderer.Camera.CurrentRestriction = CameraRestrictionMode.On; } else { Renderer.Camera.CurrentRestriction = CameraRestrictionMode.NotAvailable; } } } catch { var currentError = Translations.GetInterfaceString("errors_critical_file"); currentError = currentError.Replace("[file]", Panel2 ? "panel2.cfg" : "panel.cfg"); currentHost.ReportProblem(ProblemType.InvalidData, currentError); Cancel = true; } }
public override bool LoadTrain(Encoding Encoding, string trainPath, ref AbstractTrain train, ref Control[] currentControls) { CurrentControls = currentControls; TrainBase currentTrain = train as TrainBase; if (currentTrain == null) { currentHost.ReportProblem(ProblemType.InvalidData, "Train was not valid"); return(false); } if (currentTrain.State == TrainState.Bogus) { // bogus train string TrainData = Path.CombineFile(FileSystem.GetDataFolder("Compatibility", "PreTrain"), "train.dat"); TrainDatParser.Parse(TrainData, Encoding.UTF8, currentTrain); Thread.Sleep(1); if (Cancel) { return(false); } } else { currentTrain.TrainFolder = trainPath; // real train if (currentTrain.IsPlayerTrain) { FileSystem.AppendToLogFile("Loading player train: " + currentTrain.TrainFolder); } else { FileSystem.AppendToLogFile("Loading AI train: " + currentTrain.TrainFolder); } string TrainData = Path.CombineFile(currentTrain.TrainFolder, "train.dat"); TrainDatParser.Parse(TrainData, Encoding, currentTrain); Thread.Sleep(1); if (Cancel) { return(false); } SoundCfgParser.ParseSoundConfig(currentTrain); Thread.Sleep(1); if (Cancel) { return(false); } // door open/close speed for (int i = 0; i < currentTrain.Cars.Length; i++) { currentTrain.Cars[i].DetermineDoorClosingSpeed(); } } // add panel section if (currentTrain.IsPlayerTrain) { ParsePanelConfig(currentTrain, Encoding); Thread.Sleep(1); if (Cancel) { return(false); } FileSystem.AppendToLogFile("Train panel loaded sucessfully."); } // add exterior section if (currentTrain.State != TrainState.Bogus) { bool[] VisibleFromInterior = new bool[currentTrain.Cars.Length]; UnifiedObject[] CarObjects = new UnifiedObject[currentTrain.Cars.Length]; UnifiedObject[] BogieObjects = new UnifiedObject[currentTrain.Cars.Length * 2]; UnifiedObject[] CouplerObjects = new UnifiedObject[currentTrain.Cars.Length]; string tXml = Path.CombineFile(currentTrain.TrainFolder, "train.xml"); if (File.Exists(tXml)) { TrainXmlParser.Parse(tXml, currentTrain, ref CarObjects, ref BogieObjects, ref CouplerObjects, ref VisibleFromInterior); } else { ExtensionsCfgParser.ParseExtensionsConfig(currentTrain.TrainFolder, Encoding, ref CarObjects, ref BogieObjects, ref CouplerObjects, ref VisibleFromInterior, currentTrain); } currentTrain.CameraCar = currentTrain.DriverCar; Thread.Sleep(1); if (Cancel) { return(false); } //Stores the current array index of the bogie object to add //Required as there are two bogies per car, and we're using a simple linear array.... int currentBogieObject = 0; for (int i = 0; i < currentTrain.Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = Path.CombineFile(FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); StaticObject so; currentHost.LoadStaticObject(file, Encoding.UTF8, false, out so); if (so == null) { CarObjects[i] = null; } else { StaticObject c = (StaticObject)so.Clone(); //Clone as otherwise the cached object doesn't scale right c.ApplyScale(currentTrain.Cars[i].Width, currentTrain.Cars[i].Height, currentTrain.Cars[i].Length); CarObjects[i] = c; } } if (CarObjects[i] != null) { // add object currentTrain.Cars[i].LoadCarSections(CarObjects[i], VisibleFromInterior[i]); } if (CouplerObjects[i] != null) { currentTrain.Cars[i].Coupler.LoadCarSections(CouplerObjects[i], VisibleFromInterior[i]); } //Load bogie objects if (BogieObjects[currentBogieObject] != null) { currentTrain.Cars[i].FrontBogie.LoadCarSections(BogieObjects[currentBogieObject], VisibleFromInterior[i]); } currentBogieObject++; if (BogieObjects[currentBogieObject] != null) { currentTrain.Cars[i].RearBogie.LoadCarSections(BogieObjects[currentBogieObject], VisibleFromInterior[i]); } currentBogieObject++; } } // place cars currentTrain.PlaceCars(0.0); currentControls = CurrentControls; return(true); }
private static void ParseExtensionsConfig(string filePath, Encoding encoding, out UnifiedObject[] carObjects, out UnifiedObject[] bogieObjects, out UnifiedObject[] couplerObjects, out double[] axleLocations, out double[] couplerDistances, out TrainManager.Train train, bool loadObjects) { CultureInfo culture = CultureInfo.InvariantCulture; carObjects = new UnifiedObject[0]; bogieObjects = new UnifiedObject[0]; couplerObjects = new UnifiedObject[0]; axleLocations = new double[0]; couplerDistances = new double[0]; train = new TrainManager.Train { Cars = new TrainManager.Car[0] }; if (!File.Exists(filePath)) { return; } bool[] carObjectsReversed = new bool[0]; bool[] bogieObjectsReversed = new bool[0]; bool[] carsDefined = new bool[0]; bool[] bogiesDefined = new bool[0]; bool[] couplerDefined = new bool[0]; string trainPath = System.IO.Path.GetDirectoryName(filePath); if (encoding == null) { encoding = TextEncoding.GetSystemEncodingFromFile(trainPath); } string[] lines = File.ReadAllLines(filePath, 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) { // ReSharper disable RedundantExplicitParamsArrayCreation string a = lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = lines[i].Substring(j + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation int n; if (int.TryParse(a, NumberStyles.Integer, culture, out n)) { if (n >= 0) { if (n >= train.Cars.Length) { Array.Resize(ref train.Cars, n + 1); train.Cars[n] = new TrainManager.Car(train); Array.Resize(ref carObjects, n + 1); Array.Resize(ref bogieObjects, (n + 1) * 2); Array.Resize(ref couplerObjects, n); Array.Resize(ref carObjectsReversed, n + 1); Array.Resize(ref bogieObjectsReversed, (n + 1) * 2); Array.Resize(ref carsDefined, n + 1); Array.Resize(ref bogiesDefined, (n + 1) * 2); Array.Resize(ref couplerDefined, n); Array.Resize(ref axleLocations, (n + 1) * 2); Array.Resize(ref couplerDistances, n); } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}"); } else { string file = Path.CombineFile(trainPath, b); if (File.Exists(file)) { if (loadObjects) { Program.CurrentHost.LoadObject(file, encoding, out carObjects[n]); } } else { Interface.AddMessage(MessageType.Error, true, $"The car object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}"); } } } else { Interface.AddMessage(MessageType.Error, false, $"The car index {a} does not reference an existing car at line {(i + 1).ToString(culture)} in file {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The car index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); } } 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) { if (n >= train.Cars.Length) { Array.Resize(ref train.Cars, n + 1); train.Cars[n] = new TrainManager.Car(train); Array.Resize(ref carObjects, n + 1); Array.Resize(ref bogieObjects, (n + 1) * 2); Array.Resize(ref carObjectsReversed, n + 1); Array.Resize(ref bogieObjectsReversed, (n + 1) * 2); Array.Resize(ref couplerObjects, n); Array.Resize(ref carsDefined, n + 1); Array.Resize(ref bogiesDefined, (n + 1) * 2); Array.Resize(ref couplerDefined, n); Array.Resize(ref axleLocations, (n + 1) * 2); Array.Resize(ref couplerDistances, n); } if (carsDefined[n]) { Interface.AddMessage(MessageType.Error, false, $"Car {n.ToString(culture)} has already been declared at line {(i + 1).ToString(culture)} in file {filePath}"); } carsDefined[n] = true; 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) { // ReSharper disable RedundantExplicitParamsArrayCreation string a = lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = lines[i].Substring(j + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation 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 {filePath}"); break; } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}"); } else { string file = Path.CombineFile(trainPath, b); if (File.Exists(file)) { if (loadObjects) { Program.CurrentHost.LoadObject(file, encoding, out carObjects[n]); } } else { Interface.AddMessage(MessageType.Error, true, $"The car object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}"); } } 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 {filePath}"); } } 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 {filePath}"); } } break; case "reversed": carObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "axles": int k = b.IndexOf(','); if (k >= 0) { // ReSharper disable RedundantExplicitParamsArrayCreation string c = b.Substring(0, k).TrimEnd(new char[] { }); string d = b.Substring(k + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation 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 {filePath}"); } 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 {filePath}"); } 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 {filePath}"); } else { if (n == 0) { axleLocations[n] = rear; axleLocations[n + 1] = front; } else { axleLocations[n * 2] = rear; axleLocations[n * 2 + 1] = front; } } } else { Interface.AddMessage(MessageType.Error, false, $"An argument-separating comma is expected in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); break; } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); } } 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 {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The car index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}"); } } 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)) { if (n >= train.Cars.Length * 2) { Array.Resize(ref train.Cars, n / 2 + 1); if (n == 0) { train.Cars[0] = new TrainManager.Car(train); Array.Resize(ref axleLocations, 2); } else { train.Cars[n / 2] = new TrainManager.Car(train); Array.Resize(ref axleLocations, (n / 2 + 1) * 2); } Array.Resize(ref carObjects, n / 2 + 1); Array.Resize(ref bogieObjects, n + 2); Array.Resize(ref couplerObjects, n / 2); Array.Resize(ref carObjectsReversed, n / 2 + 1); Array.Resize(ref bogieObjectsReversed, n + 2); Array.Resize(ref carsDefined, n / 2 + 1); Array.Resize(ref bogiesDefined, n + 2); Array.Resize(ref couplerDefined, n / 2); Array.Resize(ref couplerDistances, n / 2); } if (n > bogiesDefined.Length - 1) { continue; } if (bogiesDefined[n]) { Interface.AddMessage(MessageType.Error, false, $"Bogie {n.ToString(culture)} has already been declared at line {(i + 1).ToString(culture)} in file {filePath}"); } bogiesDefined[n] = true; //Assuming that there are two bogies per car if (n >= 0 & n < train.Cars.Length * 2) { 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) { // ReSharper disable RedundantExplicitParamsArrayCreation string a = lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = lines[i].Substring(j + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation switch (a.ToLowerInvariant()) { case "object": if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}"); } else { if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, $"An empty bogie object was supplied at line {(i + 1).ToString(culture)} in file {filePath}"); break; } string file = Path.CombineFile(trainPath, b); if (File.Exists(file)) { if (loadObjects) { Program.CurrentHost.LoadObject(file, encoding, out bogieObjects[n]); } } else { Interface.AddMessage(MessageType.Error, true, $"The bogie object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}"); } } break; case "length": { Interface.AddMessage(MessageType.Error, false, $"A defined length is not supported for bogies at line {(i + 1).ToString(culture)} in file {filePath}"); } break; case "reversed": bogieObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "axles": //Axles aren't used in bogie positioning, just in rotation break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); break; } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, $"The bogie index {t} does not reference an existing bogie at line {(i + 1).ToString(culture)} in file {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The bogie index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}"); } } 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) { if (n >= train.Cars.Length - 1) { Array.Resize(ref train.Cars, n + 2); train.Cars[n + 1] = new TrainManager.Car(train); Array.Resize(ref carObjects, n + 2); Array.Resize(ref bogieObjects, (n + 2) * 2); Array.Resize(ref carObjectsReversed, n + 2); Array.Resize(ref bogieObjectsReversed, (n + 2) * 2); Array.Resize(ref couplerObjects, n + 1); Array.Resize(ref carsDefined, n + 2); Array.Resize(ref bogiesDefined, (n + 2) * 2); Array.Resize(ref couplerDefined, n + 1); Array.Resize(ref axleLocations, (n + 2) * 2); Array.Resize(ref couplerDistances, n + 1); } if (couplerDefined[n]) { Interface.AddMessage(MessageType.Error, false, $"Coupler {n.ToString(culture)} has already been declared at line {(i + 1).ToString(culture)} in file {filePath}"); } couplerDefined[n] = true; 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) { // ReSharper disable RedundantExplicitParamsArrayCreation string a = lines[i].Substring(0, j).TrimEnd(new char[] { }); string b = lines[i].Substring(j + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation switch (a.ToLowerInvariant()) { case "distances": { int k = b.IndexOf(','); if (k >= 0) { // ReSharper disable RedundantExplicitParamsArrayCreation string c = b.Substring(0, k).TrimEnd(new char[] { }); string d = b.Substring(k + 1).TrimStart(new char[] { }); // ReSharper restore RedundantExplicitParamsArrayCreation 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 {filePath}"); } 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 {filePath}"); } 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 {filePath}"); } else { couplerDistances[n] = 0.5 * (min + max); } } else { Interface.AddMessage(MessageType.Error, false, $"An argument-separating comma is expected in {a} at line {(i + 1).ToString(culture)} in file {filePath}"); } } 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 {filePath}"); break; } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, $"File contains illegal characters at line {(i + 1).ToString(culture)} in file {filePath}"); } else { string file = Path.CombineFile(trainPath, b); if (File.Exists(file)) { if (loadObjects) { Program.CurrentHost.LoadObject(file, encoding, out couplerObjects[n]); } } else { Interface.AddMessage(MessageType.Error, true, $"The coupler object {file} does not exist at line {(i + 1).ToString(culture)} in file {filePath}"); } } break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key-value pair {a} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); break; } } else { Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); } } 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 {filePath}"); } } else { Interface.AddMessage(MessageType.Error, false, $"The coupler index is expected to be an integer at line {(i + 1).ToString(culture)} in file {filePath}"); } } 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(filePath, Encoding.GetEncoding(1252), out carObjects, out bogieObjects, out couplerObjects, out axleLocations, out couplerDistances, out train, loadObjects); return; } Interface.AddMessage(MessageType.Error, false, $"Invalid statement {lines[i]} encountered at line {(i + 1).ToString(culture)} in file {filePath}"); } break; } } } // check for car objects and reverse if necessary int carObjectsCount = 0; for (int i = 0; i < train.Cars.Length; i++) { if (carObjects[i] != null) { carObjectsCount++; if (carObjectsReversed[i] && loadObjects) { if (carObjects[i] is StaticObject) { StaticObject obj = (StaticObject)carObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (carObjects[i] is AnimatedObjectCollection) { AnimatedObjectCollection obj = (AnimatedObjectCollection)carObjects[i]; foreach (AnimatedObject animatedObj in obj.Objects) { foreach (ObjectState state in animatedObj.States) { state.Prototype.ApplyScale(-1.0, 1.0, -1.0); Matrix4D t = state.Translation; t.Row3.X *= -1.0f; t.Row3.Z *= -1.0f; state.Translation = t; } animatedObj.TranslateXDirection.X *= -1.0; animatedObj.TranslateXDirection.Z *= -1.0; animatedObj.TranslateYDirection.X *= -1.0; animatedObj.TranslateYDirection.Z *= -1.0; animatedObj.TranslateZDirection.X *= -1.0; animatedObj.TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int bogieObjectsCount = 0; for (int i = 0; i < train.Cars.Length * 2; i++) { if (bogieObjects[i] != null) { bogieObjectsCount++; if (bogieObjectsReversed[i] && loadObjects) { if (bogieObjects[i] is StaticObject) { StaticObject obj = (StaticObject)bogieObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (bogieObjects[i] is AnimatedObjectCollection) { AnimatedObjectCollection obj = (AnimatedObjectCollection)bogieObjects[i]; foreach (AnimatedObject animatedObj in obj.Objects) { foreach (ObjectState state in animatedObj.States) { state.Prototype.ApplyScale(-1.0, 1.0, -1.0); Matrix4D t = state.Translation; t.Row3.X *= -1.0f; t.Row3.Z *= -1.0f; state.Translation = t; } animatedObj.TranslateXDirection.X *= -1.0; animatedObj.TranslateXDirection.Z *= -1.0; animatedObj.TranslateYDirection.X *= -1.0; animatedObj.TranslateYDirection.Z *= -1.0; animatedObj.TranslateZDirection.X *= -1.0; animatedObj.TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } if (carObjectsCount > 0 & carObjectsCount < train.Cars.Length) { Interface.AddMessage(MessageType.Warning, false, $"An incomplete set of exterior objects was provided in file {filePath}"); } if (bogieObjectsCount > 0 & bogieObjectsCount < train.Cars.Length * 2) { Interface.AddMessage(MessageType.Warning, false, $"An incomplete set of bogie objects was provided in file {filePath}"); } }
private static void routeWorkerThread_completed(object sender, RunWorkerCompletedEventArgs e) { RoutefileState = RouteState.Processed; if (e.Error != null || Program.CurrentRoute == null) { Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\route_error.png"), new TextureParameters(null, null), out routePictureBox.Texture); if (e.Error != null) { routeDescriptionBox.Text = e.Error.Message; RoutefileState = RouteState.Error; } routeWorkerThread.Dispose(); return; } try { // image if (!string.IsNullOrEmpty(Program.CurrentRoute.Image)) { try { if (File.Exists(Program.CurrentRoute.Image)) { Program.CurrentHost.RegisterTexture(Program.CurrentRoute.Image, new TextureParameters(null, null), out routePictureBox.Texture); } else { Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\route_unknown.png"), new TextureParameters(null, null), out routePictureBox.Texture); } } catch { routePictureBox.Texture = null; } } else { string[] f = { ".png", ".bmp", ".gif", ".tiff", ".tif", ".jpeg", ".jpg" }; int i; for (i = 0; i < f.Length; i++) { string g = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(currentFile), System.IO.Path.GetFileNameWithoutExtension(currentFile) + f[i]); if (System.IO.File.Exists(g)) { try { using (var fs = new FileStream(g, FileMode.Open, FileAccess.Read)) { //pictureboxRouteImage.Image = new Bitmap(fs); } } catch { //pictureboxRouteImage.Image = null; } break; } } if (i == f.Length) { Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\route_unknown.png"), new TextureParameters(null, null), out routePictureBox.Texture); } } // description string Description = Program.CurrentRoute.Comment.ConvertNewlinesToCrLf(); if (Description.Length != 0) { routeDescriptionBox.Text = Description; } else { routeDescriptionBox.Text = System.IO.Path.GetFileNameWithoutExtension(currentFile); } } catch (Exception ex) { Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\route_error.png"), new TextureParameters(null, null), out routePictureBox.Texture); routeDescriptionBox.Text = ex.Message; currentFile = null; } }
private void ParsePanelAnimatedNode(XElement Element, string FileName, string TrainPath, CarSection CarSection, int GroupIndex) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; int currentSectionElement = 0; int numberOfSectionElements = Element.Elements().Count(); double invfac = numberOfSectionElements == 0 ? 0.4 : 0.4 / numberOfSectionElements; foreach (XElement SectionElement in Element.Elements()) { Plugin.CurrentProgress = Plugin.LastProgress + invfac * 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, 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) { if (!NumberFormats.TryParseIntVb6(Value, out var SoundIndex)) { Plugin.currentHost.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); break; } 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 = Path.CombineFile(TrainPath, Value); if (System.IO.File.Exists(File)) { System.Text.Encoding e = TextEncoding.GetSystemEncodingFromFile(File); Plugin.currentHost.LoadObject(File, e, out var currentObject); var a = (AnimatedObjectCollection)currentObject; 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; } } }
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; } } } }