/// <summary>Parses any command-line arguments passed to the main program</summary> /// <param name="Arguments">A string array of arguments</param> /// <param name="Result">The main dialog result (Used to launch)</param> internal static void ParseArguments(string[] Arguments, ref formMain.MainDialogResult Result) { if (Arguments.Length == 0) { return; } for (int i = 0; i < Arguments.Length; i++) { int equals = Arguments[i].IndexOf('='); if (@equals >= 0) { string key = Arguments[i].Substring(0, @equals).Trim().ToLowerInvariant(); string value = Arguments[i].Substring(@equals + 1).Trim(); switch (key) { case "/route": Result.RouteFile = value; switch (TextEncoding.GetEncodingFromFile(Result.RouteFile)) { case TextEncoding.Encoding.Utf7: Result.RouteEncoding = System.Text.Encoding.UTF7; break; case TextEncoding.Encoding.Utf8: Result.RouteEncoding = System.Text.Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: Result.RouteEncoding = System.Text.Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: Result.RouteEncoding = System.Text.Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: Result.RouteEncoding = System.Text.Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: Result.RouteEncoding = System.Text.Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: Result.RouteEncoding = System.Text.Encoding.GetEncoding(932); break; case TextEncoding.Encoding.Windows1252: Result.RouteEncoding = System.Text.Encoding.GetEncoding(1252); break; case TextEncoding.Encoding.Big5: Result.RouteEncoding = System.Text.Encoding.GetEncoding(950); break; default: Result.RouteEncoding = Encoding.Default; break; } break; case "/train": Result.TrainFolder = value; switch (TextEncoding.GetEncodingFromFile(Result.TrainFolder, "train.txt")) { case TextEncoding.Encoding.Utf8: Result.TrainEncoding = System.Text.Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: Result.TrainEncoding = System.Text.Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: Result.TrainEncoding = System.Text.Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: Result.TrainEncoding = System.Text.Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: Result.TrainEncoding = System.Text.Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: Result.TrainEncoding = System.Text.Encoding.GetEncoding(932); break; case TextEncoding.Encoding.Windows1252: Result.TrainEncoding = System.Text.Encoding.GetEncoding(1252); break; case TextEncoding.Encoding.Big5: Result.TrainEncoding = System.Text.Encoding.GetEncoding(950); break; default: Result.TrainEncoding = Encoding.Default; break; } break; case "/station": Result.InitialStation = value; break; case "/time": Interface.TryParseTime(value, out Result.StartTime); break; case "/ai": if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "1") { Result.AIDriver = true; } break; case "/fullscreen": if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "1") { Result.FullScreen = true; } break; case "/width": NumberFormats.TryParseIntVb6(value, out Result.Width); break; case "/height": NumberFormats.TryParseIntVb6(value, out Result.Height); break; } } } }
/// <summary>Loads an object</summary> /// <param name="FileName">The file to load</param> /// <param name="Encoding">The text endcoding of the base file (Used if the encoding cannot be auto-detected)</param> /// <param name="PreserveVertices">Whether object should be optimized to remove duplicate vertices</param> /// <returns>The new object, or a null reference if loading fails</returns> /* * Notes for refactoring: * * Unused vertices must only be preserved in deformable objects (e.g. Crack and Form) * * TODO / BUG: No detection of actual file contents, which will make all parsers barf */ internal static UnifiedObject LoadObject(string FileName, Encoding Encoding, bool PreserveVertices) { if (String.IsNullOrEmpty(FileName)) { return(null); } #if !DEBUG try { #endif if (!System.IO.Path.HasExtension(FileName)) { while (true) { var f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".x"); if (System.IO.File.Exists(f)) { FileName = f; break; } f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".csv"); if (System.IO.File.Exists(f)) { FileName = f; break; } f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".b3d"); if (System.IO.File.Exists(f)) { FileName = f; break; } f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".xml"); if (System.IO.File.Exists(f)) { FileName = f; break; } f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".l3dgrp"); if (System.IO.File.Exists(f)) { FileName = f; break; } f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".l3dobj"); if (System.IO.File.Exists(f)) { FileName = f; break; } } } UnifiedObject Result; TextEncoding.Encoding newEncoding = TextEncoding.GetEncodingFromFile(FileName); if (newEncoding != TextEncoding.Encoding.Unknown) { switch (newEncoding) { case TextEncoding.Encoding.Utf7: Encoding = System.Text.Encoding.UTF7; break; case TextEncoding.Encoding.Utf8: Encoding = System.Text.Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: Encoding = System.Text.Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: Encoding = System.Text.Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: Encoding = System.Text.Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: Encoding = System.Text.Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: Encoding = System.Text.Encoding.GetEncoding(932); break; case TextEncoding.Encoding.Windows1252: Encoding = System.Text.Encoding.GetEncoding(1252); break; case TextEncoding.Encoding.Big5: Encoding = System.Text.Encoding.GetEncoding(950); break; case TextEncoding.Encoding.EUC_KR: Encoding = System.Text.Encoding.GetEncoding(949); break; } } string e = System.IO.Path.GetExtension(FileName); if (e == null) { Interface.AddMessage(MessageType.Error, false, "The file " + FileName + " does not have a recognised extension."); return(null); } switch (e.ToLowerInvariant()) { case ".csv": case ".b3d": case ".x": case ".obj": Program.CurrentHost.LoadObject(FileName, Encoding, out Result); break; case ".animated": Result = AnimatedObjectParser.ReadObject(FileName, Encoding); break; case ".xml": Result = XMLParser.ReadObject(FileName, Encoding); break; case ".l3dgrp": Result = Ls3DGrpParser.ReadObject(FileName, Encoding, new Vector3()); break; case ".l3dobj": Result = Ls3DObjectParser.ReadObject(FileName, new Vector3()); break; case ".s": Result = MsTsShapeParser.ReadObject(FileName); break; default: Interface.AddMessage(MessageType.Error, false, "The file extension is not supported: " + FileName); return(null); } if (Result != null) { Result.OptimizeObject(PreserveVertices, Interface.CurrentOptions.ObjectOptimizationBasicThreshold, Interface.CurrentOptions.ObjectOptimizationVertexCulling); } return(Result); #if !DEBUG } catch (Exception ex) { Interface.AddMessage(MessageType.Error, true, "An unexpected error occured (" + ex.Message + ") while attempting to load the file " + FileName); return(null); } #endif }
/// <summary>Parses a base track following object node</summary> /// <param name="Element">The XElement to parse</param> /// <param name="FileName">The filename of the containing XML file</param> /// <param name="Path">The path of the containing XML file</param> /// <param name="Train">The track following object to parse this node into</param> private static void ParseTrackFollowingObjectNode(XElement Element, string FileName, string Path, string ObjectPath, TrainManager.TrackFollowingObject Train) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string TrainDirectory = string.Empty; bool consistReversed = false; List <Game.TravelData> Data = new List <Game.TravelData>(); foreach (XElement SectionElement in Element.Elements()) { string Section = SectionElement.Name.LocalName; switch (SectionElement.Name.LocalName.ToLowerInvariant()) { case "stops": ParseStopNode(SectionElement, FileName, Data); break; case "train": foreach (XElement KeyNode in SectionElement.Elements()) { string Key = KeyNode.Name.LocalName; string Value = KeyNode.Value; int LineNumber = ((IXmlLineInfo)KeyNode).LineNumber; switch (Key.ToLowerInvariant()) { case "directory": { string trainDirectory = OpenBveApi.Path.CombineDirectory(Path, Value); if (!System.IO.Directory.Exists(trainDirectory)) { trainDirectory = OpenBveApi.Path.CombineFile(Program.FileSystem.InitialTrainFolder, Value); } if (!System.IO.Directory.Exists(trainDirectory)) { trainDirectory = OpenBveApi.Path.CombineFile(Program.FileSystem.TrainInstallationDirectory, Value); } if (!System.IO.Directory.Exists(trainDirectory)) { trainDirectory = OpenBveApi.Path.CombineFile(ObjectPath, Value); } if (!System.IO.Directory.Exists(trainDirectory)) { Interface.AddMessage(MessageType.Error, false, "Directory was not found in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } else { TrainDirectory = trainDirectory; } } break; case "reversed": int n; NumberFormats.TryParseIntVb6(Value, out n); if (n == 1 || Value.ToLowerInvariant() == "true") { consistReversed = true; } break; } } break; case "definition": foreach (XElement KeyNode in SectionElement.Elements()) { string Key = KeyNode.Name.LocalName; string Value = KeyNode.Value; int LineNumber = ((IXmlLineInfo)KeyNode).LineNumber; switch (Key.ToLowerInvariant()) { case "appearancetime": if (Value.Length != 0 && !Interface.TryParseTime(Value, out Train.AppearanceTime)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "appearancestartposition": if (Value.Length != 0 && !NumberFormats.TryParseDoubleVb6(Value, out Train.AppearanceStartPosition)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "appearanceendposition": if (Value.Length != 0 && !NumberFormats.TryParseDoubleVb6(Value, out Train.AppearanceEndPosition)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; case "leavetime": if (Value.Length != 0 && !Interface.TryParseTime(Value, out Train.LeaveTime)) { Interface.AddMessage(MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + LineNumber.ToString(Culture) + " in " + FileName); } break; } } break; } } if (!Data.Any() || string.IsNullOrEmpty(TrainDirectory)) { return; } /* * First check for a train.ai file- Functionally identical, but allows for differently configured AI * trains not to show up as driveable */ string TrainData = OpenBveApi.Path.CombineFile(TrainDirectory, "train.ai"); if (!System.IO.File.Exists(TrainData)) { // Check for the standard driveable train.dat TrainData = OpenBveApi.Path.CombineFile(TrainDirectory, "train.dat"); } string ExteriorFile = OpenBveApi.Path.CombineFile(TrainDirectory, "extensions.cfg"); if (!System.IO.File.Exists(TrainData) || !System.IO.File.Exists(ExteriorFile)) { Interface.AddMessage(MessageType.Error, true, "The supplied train folder in TrackFollowingObject " + FileName + " did not contain a complete set of data."); return; } TrainDatParser.ParseTrainData(TrainData, TextEncoding.GetSystemEncodingFromFile(TrainData), Train); SoundCfgParser.ParseSoundConfig(TrainDirectory, Train); Train.AI = new Game.TrackFollowingObjectAI(Train, Data); UnifiedObject[] CarObjects = new UnifiedObject[Train.Cars.Length]; UnifiedObject[] BogieObjects = new UnifiedObject[Train.Cars.Length * 2]; UnifiedObject[] CouplerObjects = new UnifiedObject[Train.Cars.Length - 1]; ExtensionsCfgParser.ParseExtensionsConfig(System.IO.Path.GetDirectoryName(ExteriorFile), TextEncoding.GetSystemEncodingFromFile(ExteriorFile), ref CarObjects, ref BogieObjects, ref CouplerObjects, Train, true); int currentBogieObject = 0; for (int i = 0; i < Train.Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, false); if (so == null) { CarObjects[i] = null; } else { double sx = Train.Cars[i].Width; double sy = Train.Cars[i].Height; double sz = Train.Cars[i].Length; so.ApplyScale(sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object Train.Cars[i].LoadCarSections(CarObjects[i]); } //Load bogie objects if (BogieObjects[currentBogieObject] != null) { Train.Cars[i].FrontBogie.LoadCarSections(BogieObjects[currentBogieObject]); } currentBogieObject++; if (BogieObjects[currentBogieObject] != null) { Train.Cars[i].RearBogie.LoadCarSections(BogieObjects[currentBogieObject]); } currentBogieObject++; } // door open/close speed foreach (var Car in Train.Cars) { if (Car.Specs.DoorOpenFrequency <= 0.0) { if (Car.Doors[0].OpenSound.Buffer != null & Car.Doors[1].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer); Program.Sounds.LoadBuffer(Car.Doors[1].OpenSound.Buffer); double a = Car.Doors[0].OpenSound.Buffer.Duration; double b = Car.Doors[1].OpenSound.Buffer.Duration; Car.Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (Car.Doors[0].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer); double a = Car.Doors[0].OpenSound.Buffer.Duration; Car.Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (Car.Doors[1].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer); double b = Car.Doors[1].OpenSound.Buffer.Duration; Car.Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { Car.Specs.DoorOpenFrequency = 0.8; } } if (Car.Specs.DoorCloseFrequency <= 0.0) { if (Car.Doors[0].CloseSound.Buffer != null & Car.Doors[1].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer); Program.Sounds.LoadBuffer(Car.Doors[1].CloseSound.Buffer); double a = Car.Doors[0].CloseSound.Buffer.Duration; double b = Car.Doors[1].CloseSound.Buffer.Duration; Car.Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (Car.Doors[0].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer); double a = Car.Doors[0].CloseSound.Buffer.Duration; Car.Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (Car.Doors[1].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer); double b = Car.Doors[1].CloseSound.Buffer.Duration; Car.Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { Car.Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; Car.Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); Car.Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); Car.Specs.DoorOpenFrequency /= Car.Specs.DoorOpenPitch; Car.Specs.DoorCloseFrequency /= Car.Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ Car.Specs.DoorOpenPitch = 1.0; Car.Specs.DoorClosePitch = 1.0; } foreach (var Car in Train.Cars) { Car.FrontAxle.Follower.TrackIndex = Data[0].RailIndex; Car.RearAxle.Follower.TrackIndex = Data[0].RailIndex; Car.FrontBogie.FrontAxle.Follower.TrackIndex = Data[0].RailIndex; Car.FrontBogie.RearAxle.Follower.TrackIndex = Data[0].RailIndex; Car.RearBogie.FrontAxle.Follower.TrackIndex = Data[0].RailIndex; Car.RearBogie.RearAxle.Follower.TrackIndex = Data[0].RailIndex; } if (consistReversed) { Train.Reverse(); } Train.PlaceCars(Data[0].StopPosition); }
// parse extensions config internal static void ParseExtensionsConfig(string TrainPath, System.Text.Encoding Encoding, ref UnifiedObject[] CarObjects, ref UnifiedObject[] BogieObjects, TrainManager.Train Train, bool LoadObjects) { 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)) { TextEncoding.Encoding newEncoding = TextEncoding.GetEncodingFromFile(FileName); if (newEncoding != TextEncoding.Encoding.Unknown) { switch (newEncoding) { case TextEncoding.Encoding.Utf7: Encoding = System.Text.Encoding.UTF7; break; case TextEncoding.Encoding.Utf8: Encoding = System.Text.Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: Encoding = System.Text.Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: Encoding = System.Text.Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: Encoding = System.Text.Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: Encoding = System.Text.Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: Encoding = System.Text.Encoding.GetEncoding(932); break; } } 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(); } else { Lines[i] = Lines[i].Trim(); } } 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(); string b = Lines[i].Substring(j + 1).TrimStart(); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { 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 = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { if (LoadObjects) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, false, false, false); } } else { Interface.AddMessage(MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } else { Interface.AddMessage(MessageType.Error, false, "The car index " + a + " 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, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { if (CarsDefined[n]) { Interface.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(); 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); break; } 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 = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { if (LoadObjects) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, false, false, false); } } else { Interface.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 { 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, System.Globalization.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, System.Globalization.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.Position = rear; Train.Cars[n].FrontAxle.Position = front; 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": CarObjectsReversed[n] = 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--; if (DefinedLength & !DefinedAxles) { double AxleDistance = 0.4 * Train.Cars[n].Length; Train.Cars[n].RearAxle.Position = -AxleDistance; Train.Cars[n].FrontAxle.Position = AxleDistance; } } 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, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Couplers.Length) { 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(); 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, System.Globalization.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, System.Globalization.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].MinimumDistanceBetweenCars = min; Train.Couplers[n].MaximumDistanceBetweenCars = 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; 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)) { // 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]) { Interface.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(); string b = Lines[i].Substring(j + 1).TrimStart(); 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 " + FileName); } else { if (string.IsNullOrEmpty(b)) { Interface.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)) { if (LoadObjects) { BogieObjects[n] = ObjectManager.LoadObject(File, Encoding, false, false, false); } } else { Interface.AddMessage(MessageType.Error, true, "The bogie object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "length": { Interface.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(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, System.Globalization.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, System.Globalization.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].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 { 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": BogieObjectsReversed[n] = 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--; 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 { 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 { // 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, Train, LoadObjects); return; } Interface.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] && LoadObjects) { { // reverse axle positions double temp = Train.Cars[i].FrontAxle.Position; Train.Cars[i].FrontAxle.Position = -Train.Cars[i].RearAxle.Position; Train.Cars[i].RearAxle.Position = -temp; } if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)CarObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int bogieObjects = 0; for (int i = 0; i < Train.Cars.Length * 2; i++) { bool IsOdd = (i % 2 != 0); int CarIndex = i / 2; if (BogieObjects[i] != null) { bogieObjects++; if (BogieObjectsReversed[i] && LoadObjects) { { // reverse axle positions if (IsOdd) { double temp = Train.Cars[CarIndex].FrontBogie.FrontAxle.Position; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = -Train.Cars[CarIndex].FrontBogie.RearAxle.Position; Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -temp; } else { double temp = Train.Cars[CarIndex].RearBogie.FrontAxle.Position; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = -Train.Cars[CarIndex].RearBogie.RearAxle.Position; Train.Cars[CarIndex].RearBogie.RearAxle.Position = -temp; } } if (BogieObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)BogieObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (BogieObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)BogieObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } if (carObjects > 0 & carObjects < Train.Cars.Length) { Interface.AddMessage(MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName); } if (bogieObjects > 0 & bogieObjects < Train.Cars.Length * 2) { Interface.AddMessage(MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + FileName); } } }
internal static void ParseExtensionsConfig(string filePath, Encoding encoding, out UnifiedObject[] carObjects, out UnifiedObject[] bogieObjects, out double[] axleLocations, out TrainManager.Train train, bool loadObjects) { CultureInfo Culture = CultureInfo.InvariantCulture; carObjects = new UnifiedObject[] { }; bogieObjects = new UnifiedObject[] { }; axleLocations = new double[] { }; train = new TrainManager.Train(); if (!System.IO.File.Exists(filePath)) { return; } train.Cars = new TrainManager.Car[] { }; 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]; axleLocations = new double[train.Cars.Length * 2]; string trainPath = System.IO.Path.GetDirectoryName(filePath); System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.InvariantCulture; TextEncoding.Encoding newEncoding = TextEncoding.GetEncodingFromFile(filePath); if (newEncoding != TextEncoding.Encoding.Unknown) { switch (newEncoding) { case TextEncoding.Encoding.Utf7: encoding = Encoding.UTF7; break; case TextEncoding.Encoding.Utf8: encoding = Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: encoding = Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: encoding = Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: encoding = Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: encoding = Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: encoding = Encoding.GetEncoding(932); break; } } string[] lines = System.IO.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) { 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) { 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 carsDefined, n + 1); Array.Resize(ref bogiesDefined, (n + 1) * 2); Array.Resize(ref axleLocations, (n + 1) * 2); } 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 = OpenBveApi.Path.CombineFile(trainPath, b); if (System.IO.File.Exists(file)) { if (loadObjects) { carObjects[n] = ObjectManager.LoadObject(file, encoding, false); } } 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, System.Globalization.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 carsDefined, n + 1); Array.Resize(ref bogiesDefined, (n + 1) * 2); Array.Resize(ref axleLocations, (n + 1) * 2); } 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) { 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)) { 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 = OpenBveApi.Path.CombineFile(trainPath, b); if (System.IO.File.Exists(file)) { if (loadObjects) { carObjects[n] = ObjectManager.LoadObject(file, encoding, false); } } 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, System.Globalization.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) { 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)) { 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, System.Globalization.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)) { // 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 >= 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 carObjectsReversed, n / 2 + 1); Array.Resize(ref bogieObjectsReversed, n + 2); Array.Resize(ref carsDefined, n / 2 + 1); Array.Resize(ref bogiesDefined, 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) { 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)) { 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 = OpenBveApi.Path.CombineFile(trainPath, b); if (System.IO.File.Exists(file)) { if (loadObjects) { bogieObjects[n] = ObjectManager.LoadObject(file, encoding, false); } } 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 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("[coupler", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { /* * Coupler statments are currently not supported in Object Viewer */ i++; } i--; } 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 axleLocations, 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]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Prototype.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Translation.Row3.X *= -1.0f; obj.Objects[j].States[h].Translation.Row3.Z *= -1.0f; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int 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]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Prototype.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Translation.Row3.X *= -1.0f; obj.Objects[j].States[h].Translation.Row3.Z *= -1.0f; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } 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 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}"); } }
/// <summary>Parses a base track following object node</summary> /// <param name="ObjectPath">Absolute path to the object folder of route data</param> /// <param name="FileName">The filename of the containing XML file</param> /// <param name="SectionElement">The XElement to parse</param> /// <param name="Train">The track following object to parse this node into</param> private static void ParseTrackFollowingObjectNode(string ObjectPath, string FileName, XElement SectionElement, TrainManager.TrackFollowingObject Train) { string Section = SectionElement.Name.LocalName; string TrainDirectory = string.Empty; bool ConsistReversed = false; List <Game.TravelData> Data = new List <Game.TravelData>(); foreach (XElement KeyNode in SectionElement.Elements()) { string Key = KeyNode.Name.LocalName; int LineNumber = ((IXmlLineInfo)KeyNode).LineNumber; switch (Key.ToLowerInvariant()) { case "definition": ParseDefinitionNode(FileName, KeyNode, Train); break; case "train": ParseTrainNode(ObjectPath, FileName, KeyNode, ref TrainDirectory, ref ConsistReversed); break; case "points": case "stops": ParseTravelDataNodes(FileName, KeyNode, Data); break; default: Interface.AddMessage(MessageType.Warning, false, $"Unsupported key {Key} encountered in {Section} at line {LineNumber.ToString(culture)} in {FileName}"); break; } } if (Data.Count < 2) { Interface.AddMessage(MessageType.Error, false, $"There must be at least two points to go through in {FileName}"); return; } if (!(Data.First() is Game.TravelStopData) || !(Data.Last() is Game.TravelStopData)) { Interface.AddMessage(MessageType.Error, false, $"The first and the last point to go through must be the \"Stop\" node in {FileName}"); return; } if (string.IsNullOrEmpty(TrainDirectory)) { Interface.AddMessage(MessageType.Error, false, $"No train has been specified in {FileName}"); return; } /* * First check for a train.ai file- Functionally identical, but allows for differently configured AI * trains not to show up as drivable */ string TrainData = OpenBveApi.Path.CombineFile(TrainDirectory, "train.ai"); if (!File.Exists(TrainData)) { // Check for the standard drivable train.dat TrainData = OpenBveApi.Path.CombineFile(TrainDirectory, "train.dat"); } string ExteriorFile = OpenBveApi.Path.CombineFile(TrainDirectory, "extensions.cfg"); if (!File.Exists(TrainData) || !File.Exists(ExteriorFile)) { Interface.AddMessage(MessageType.Error, true, $"The supplied train folder in TrackFollowingObject {FileName} did not contain a complete set of data."); return; } TrainDatParser.ParseTrainData(TrainData, TextEncoding.GetSystemEncodingFromFile(TrainData), Train); SoundCfgParser.ParseSoundConfig(TrainDirectory, Train); Train.AI = new Game.TrackFollowingObjectAI(Train, Data.ToArray()); UnifiedObject[] CarObjects = new UnifiedObject[Train.Cars.Length]; UnifiedObject[] BogieObjects = new UnifiedObject[Train.Cars.Length * 2]; UnifiedObject[] CouplerObjects = new UnifiedObject[Train.Cars.Length - 1]; bool[] VisibleFromInterior = new bool[Train.Cars.Length]; ExtensionsCfgParser.ParseExtensionsConfig(System.IO.Path.GetDirectoryName(ExteriorFile), TextEncoding.GetSystemEncodingFromFile(ExteriorFile), ref CarObjects, ref BogieObjects, ref CouplerObjects, ref VisibleFromInterior, Train, true); int currentBogieObject = 0; for (int i = 0; i < Train.Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); StaticObject so; Program.CurrentHost.LoadStaticObject(file, Encoding.UTF8, false, out so); if (so == null) { CarObjects[i] = null; } else { so.ApplyScale(Train.Cars[i].Width, Train.Cars[i].Height, Train.Cars[i].Length); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object Train.Cars[i].LoadCarSections(CarObjects[i], false); } //Load bogie objects if (BogieObjects[currentBogieObject] != null) { Train.Cars[i].FrontBogie.LoadCarSections(BogieObjects[currentBogieObject], false); } currentBogieObject++; if (BogieObjects[currentBogieObject] != null) { Train.Cars[i].RearBogie.LoadCarSections(BogieObjects[currentBogieObject], false); } currentBogieObject++; } // door open/close speed foreach (var Car in Train.Cars) { if (Car.Specs.DoorOpenFrequency <= 0.0) { if (Car.Doors[0].OpenSound.Buffer != null & Car.Doors[1].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer); Program.Sounds.LoadBuffer(Car.Doors[1].OpenSound.Buffer); double a = Car.Doors[0].OpenSound.Buffer.Duration; double b = Car.Doors[1].OpenSound.Buffer.Duration; Car.Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (Car.Doors[0].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer); double a = Car.Doors[0].OpenSound.Buffer.Duration; Car.Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (Car.Doors[1].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].OpenSound.Buffer); double b = Car.Doors[1].OpenSound.Buffer.Duration; Car.Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { Car.Specs.DoorOpenFrequency = 0.8; } } if (Car.Specs.DoorCloseFrequency <= 0.0) { if (Car.Doors[0].CloseSound.Buffer != null & Car.Doors[1].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer); Program.Sounds.LoadBuffer(Car.Doors[1].CloseSound.Buffer); double a = Car.Doors[0].CloseSound.Buffer.Duration; double b = Car.Doors[1].CloseSound.Buffer.Duration; Car.Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (Car.Doors[0].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer); double a = Car.Doors[0].CloseSound.Buffer.Duration; Car.Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (Car.Doors[1].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(Car.Doors[0].CloseSound.Buffer); double b = Car.Doors[1].CloseSound.Buffer.Duration; Car.Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { Car.Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; Car.Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); Car.Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); Car.Specs.DoorOpenFrequency /= Car.Specs.DoorOpenPitch; Car.Specs.DoorCloseFrequency /= Car.Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ Car.Specs.DoorOpenPitch = 1.0; Car.Specs.DoorClosePitch = 1.0; } foreach (var Car in Train.Cars) { Car.FrontAxle.Follower.TrackIndex = Data[0].RailIndex; Car.RearAxle.Follower.TrackIndex = Data[0].RailIndex; Car.FrontBogie.FrontAxle.Follower.TrackIndex = Data[0].RailIndex; Car.FrontBogie.RearAxle.Follower.TrackIndex = Data[0].RailIndex; Car.RearBogie.FrontAxle.Follower.TrackIndex = Data[0].RailIndex; Car.RearBogie.RearAxle.Follower.TrackIndex = Data[0].RailIndex; } if (ConsistReversed) { Train.Reverse(); } Train.PlaceCars(Data[0].Position); }
// show train private void ShowTrain(bool UserSelectedEncoding) { if (!UserSelectedEncoding) { Result.TrainEncoding = System.Text.Encoding.Default; switch (TextEncoding.GetEncodingFromFile(Result.TrainFolder, "train.txt")) { case TextEncoding.Encoding.Utf8: Result.TrainEncoding = System.Text.Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: Result.TrainEncoding = System.Text.Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: Result.TrainEncoding = System.Text.Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: Result.TrainEncoding = System.Text.Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: Result.TrainEncoding = System.Text.Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: Result.TrainEncoding = System.Text.Encoding.GetEncoding(932); break; case TextEncoding.Encoding.Windows1252: Result.TrainEncoding = System.Text.Encoding.GetEncoding(1252); break; case TextEncoding.Encoding.Big5: Result.TrainEncoding = System.Text.Encoding.GetEncoding(950); break; case TextEncoding.Encoding.EUC_KR: Result.TrainEncoding = System.Text.Encoding.GetEncoding(950); break; } int i; for (i = 0; i < Interface.CurrentOptions.TrainEncodings.Length; i++) { if (Interface.CurrentOptions.TrainEncodings[i].Value == Result.TrainFolder) { int j; for (j = 1; j < EncodingCodepages.Length; j++) { if (EncodingCodepages[j] == Interface.CurrentOptions.TrainEncodings[i].Codepage) { Result.TrainEncoding = System.Text.Encoding.GetEncoding(EncodingCodepages[j]); break; } } if (j == EncodingCodepages.Length) { Result.TrainEncoding = System.Text.Encoding.UTF8; } break; } } } { // train image string File = OpenBveApi.Path.CombineFile(Result.TrainFolder, "train.png"); if (!System.IO.File.Exists(File)) { File = OpenBveApi.Path.CombineFile(Result.TrainFolder, "train.bmp"); } if (System.IO.File.Exists(File)) { TryLoadImage(pictureboxTrainImage, File); } else { TryLoadImage(pictureboxTrainImage, "train_unknown.png"); } } { // train description string File = OpenBveApi.Path.CombineFile(Result.TrainFolder, "train.txt"); if (System.IO.File.Exists(File)) { try { string trainText = System.IO.File.ReadAllText(File, Result.TrainEncoding); trainText = trainText.ConvertNewlinesToCrLf(); textboxTrainDescription.Text = trainText; } catch { textboxTrainDescription.Text = System.IO.Path.GetFileName(Result.TrainFolder); } } else { textboxTrainDescription.Text = System.IO.Path.GetFileName(Result.TrainFolder); } } // for RAGLINK Package Manager RAGLINKCommons.RPlatform.PackagesManager.SetTrainDetailData(System.IO.Path.GetFileNameWithoutExtension(Result.TrainFolder), textboxTrainDescription.Text); }
// show route private void ShowRoute(bool UserSelectedEncoding) { if (routeWorkerThread == null) { return; } if (Result.RouteFile != null && !routeWorkerThread.IsBusy) { this.Cursor = System.Windows.Forms.Cursors.WaitCursor; // determine encoding if (!UserSelectedEncoding) { Result.RouteEncoding = System.Text.Encoding.Default; switch (TextEncoding.GetEncodingFromFile(Result.RouteFile)) { case TextEncoding.Encoding.Utf7: Result.RouteEncoding = System.Text.Encoding.UTF7; break; case TextEncoding.Encoding.Utf8: Result.RouteEncoding = System.Text.Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: Result.RouteEncoding = System.Text.Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: Result.RouteEncoding = System.Text.Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: Result.RouteEncoding = System.Text.Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: Result.RouteEncoding = System.Text.Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: Result.RouteEncoding = System.Text.Encoding.GetEncoding(932); break; case TextEncoding.Encoding.Windows1252: Result.RouteEncoding = System.Text.Encoding.GetEncoding(1252); break; case TextEncoding.Encoding.Big5: Result.RouteEncoding = System.Text.Encoding.GetEncoding(950); break; case TextEncoding.Encoding.EUC_KR: Result.RouteEncoding = System.Text.Encoding.GetEncoding(949); break; } int i; for (i = 0; i < Interface.CurrentOptions.RouteEncodings.Length; i++) { if (Interface.CurrentOptions.RouteEncodings[i].Value == Result.RouteFile) { int j; for (j = 1; j < EncodingCodepages.Length; j++) { if (EncodingCodepages[j] == Interface.CurrentOptions.RouteEncodings[i].Codepage) { Result.RouteEncoding = System.Text.Encoding.GetEncoding(EncodingCodepages[j]); break; } } if (j == EncodingCodepages.Length) { Result.RouteEncoding = System.Text.Encoding.UTF8; } break; } } } if (!routeWorkerThread.IsBusy) { //HACK: If clicking very rapidly or holding down an arrow // we can sometimes try to spawn two worker threads routeWorkerThread.RunWorkerAsync(); } } }
// show train private void ShowTrain(bool UserSelectedEncoding) { if (!UserSelectedEncoding) { Result.TrainEncoding = TextEncoding.GetSystemEncodingFromFile(Result.TrainFolder, "train.txt"); comboboxTrainEncoding.Tag = new object(); comboboxTrainEncoding.SelectedIndex = 0; comboboxTrainEncoding.Items[0] = $"{Result.TrainEncoding.EncodingName} - {Result.TrainEncoding.CodePage}"; comboboxTrainEncoding.Tag = null; int i; for (i = 0; i < Interface.CurrentOptions.TrainEncodings.Length; i++) { if (Interface.CurrentOptions.TrainEncodings[i].Value == Result.TrainFolder) { int j; for (j = 1; j < EncodingCodepages.Length; j++) { if (EncodingCodepages[j] == Interface.CurrentOptions.TrainEncodings[i].Codepage) { comboboxTrainEncoding.SelectedIndex = j; Result.TrainEncoding = System.Text.Encoding.GetEncoding(EncodingCodepages[j]); break; } } if (j == EncodingCodepages.Length) { comboboxTrainEncoding.SelectedIndex = 0; Result.TrainEncoding = System.Text.Encoding.UTF8; } break; } } panelTrainEncoding.Enabled = true; comboboxTrainEncoding.Tag = null; } { // train image string File = OpenBveApi.Path.CombineFile(Result.TrainFolder, "train.png"); if (!System.IO.File.Exists(File)) { File = OpenBveApi.Path.CombineFile(Result.TrainFolder, "train.bmp"); } if (System.IO.File.Exists(File)) { TryLoadImage(pictureboxTrainImage, File); } else { TryLoadImage(pictureboxTrainImage, "train_unknown.png"); } } { // train description string File = OpenBveApi.Path.CombineFile(Result.TrainFolder, "train.txt"); if (System.IO.File.Exists(File)) { try { string trainText = System.IO.File.ReadAllText(File, Result.TrainEncoding); trainText = trainText.ConvertNewlinesToCrLf(); textboxTrainDescription.Text = trainText; textboxTrainEncodingPreview.Text = trainText; } catch { textboxTrainDescription.Text = System.IO.Path.GetFileName(Result.TrainFolder); textboxTrainEncodingPreview.Text = ""; } } else { textboxTrainDescription.Text = System.IO.Path.GetFileName(Result.TrainFolder); textboxTrainEncodingPreview.Text = ""; } } groupboxTrainDetails.Visible = true; labelTrainEncoding.Enabled = true; labelTrainEncodingPreview.Enabled = true; textboxTrainEncodingPreview.Enabled = true; buttonStart.Enabled = Result.RouteFile != null & Result.TrainFolder != null; }
/// <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 static void PreprocessChrRndSub(string FileName, bool IsRW, System.Text.Encoding Encoding, ref Expression[] Expressions) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; 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(); 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; Interface.AddMessage(MessageType.Error, false, "Invalid parenthesis structure in " + t + Epilog); } break; } if (l <= 0) { break; } } if (continueWithNextExpression) { break; } if (l != 0) { Interface.AddMessage(MessageType.Error, false, "Invalid parenthesis structure in " + t + Epilog); break; } string s = Expressions[i].Text.Substring(k + 1, h - k - 1).Trim(); switch (t.ToLowerInvariant()) { case "$if": if (j != 0) { Interface.AddMessage(MessageType.Error, false, "The $If directive must not appear within another statement" + Epilog); } else { double num; if (double.TryParse(s, System.Globalization.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) { Interface.AddMessage(MessageType.Error, false, "$EndIf missing at the end of the file" + Epilog); } } continueWithNextExpression = true; break; } else { Interface.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) { Interface.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) { Interface.AddMessage(MessageType.Error, false, "$EndIf missing at the end of the file" + Epilog); } } else { Interface.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 { Interface.AddMessage(MessageType.Error, false, "$EndIf without matching $If encountered" + Epilog); } continueWithNextExpression = true; break; case "$include": if (j != 0) { Interface.AddMessage(MessageType.Error, false, "The $Include directive must not appear within another statement" + Epilog); continueWithNextExpression = true; break; } string[] args = s.Split(';'); for (int ia = 0; ia < args.Length; ia++) { args[ia] = args[ia].Trim(); } 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(); string value = args[2 * ia].Substring(colon + 1).TrimStart(); if (!double.TryParse(value, NumberStyles.Float, Culture, out offset)) { continueWithNextExpression = true; Interface.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] = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), file); offsets[ia] = offset; if (!System.IO.File.Exists(files[ia])) { continueWithNextExpression = true; Interface.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 <Expression>(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; Interface.AddMessage(MessageType.Error, false, "A weight is invalid in " + t + Epilog); break; } if (weights[ia] <= 0.0) { continueWithNextExpression = true; Interface.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; Interface.AddMessage(MessageType.Error, false, "No file was specified in " + t + Epilog); break; } if (!continueWithNextExpression) { double number = Program.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 Interface.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], IsRW, 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 <Expression>(ref Expressions, length - 1); } else { Array.Resize <Expression>(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; Interface.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; Interface.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 > 128) { //Standard ASCII characters from 0-128 continueWithNextExpression = true; Interface.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; Interface.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(); string s2 = s.Substring(m + 1).TrimStart(); int x; if (NumberFormats.TryParseIntVb6(s1, out x)) { int y; if (NumberFormats.TryParseIntVb6(s2, out y)) { int z = x + (int)Math.Floor(Program.RandomNumberGenerator.NextDouble() * (double)(y - x + 1)); Expressions[i].Text = Expressions[i].Text.Substring(0, j) + z.ToString(Culture) + Expressions[i].Text.Substring(h + 1); } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index2 is invalid in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index1 is invalid in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.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 <string>(ref Subs, Subs.Length << 1); } Subs[x] = Expressions[i].Text.Substring(m + 1, n - m - 1).Trim(); Expressions[i].Text = Expressions[i].Text.Substring(0, j) + Expressions[i].Text.Substring(n); } else { continueWithNextExpression = true; Interface.AddMessage(MessageType.Error, false, "Index is expected to be non-negative in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.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; Interface.AddMessage(MessageType.Error, false, "Index is out of range in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.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(); 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 <Expression>(ref Expressions, length); } } }
/// <summary>Parses any command-line arguments passed to the main program</summary> /// <param name="Arguments">A string array of arguments</param> /// <param name="Result">The main dialog result (Used to launch)</param> internal static void ParseArguments(string[] Arguments, ref formMain.MainDialogResult Result) { if (Arguments.Length == 0) { return; } for (int i = 0; i < Arguments.Length; i++) { int equals = Arguments[i].IndexOf('='); if (equals >= 0) { string key = Arguments[i].Substring(0, equals).Trim(new char[] { }).ToLowerInvariant(); string value = Arguments[i].Substring(equals + 1).Trim(new char[] { }); switch (key) { case "/route": Result.RouteFile = value; Result.RouteEncoding = TextEncoding.GetSystemEncodingFromFile(Result.RouteFile); break; case "/train": Result.TrainFolder = value; Result.TrainEncoding = TextEncoding.GetSystemEncodingFromFile(Result.TrainFolder, "train.txt"); break; case "/station": Result.InitialStation = value; break; case "/time": Interface.TryParseTime(value, out Result.StartTime); break; case "/ai": if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "1") { Result.AIDriver = true; } break; case "/fullscreen": if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "1") { Result.FullScreen = true; } break; case "/width": NumberFormats.TryParseIntVb6(value, out Result.Width); break; case "/height": NumberFormats.TryParseIntVb6(value, out Result.Height); break; case "/glmenu": if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "1") { Result.ExperimentalGLMenu = true; } break; } } } }