diff --git a/source/Plugins/Formats.OpenBve/Block.cs b/source/Plugins/Formats.OpenBve/Block.cs
index 230bcfd6bf..9b6c438eab 100644
--- a/source/Plugins/Formats.OpenBve/Block.cs
+++ b/source/Plugins/Formats.OpenBve/Block.cs
@@ -775,6 +775,148 @@ public virtual bool GetNextRawValue(out string s)
return false;
}
+ /// Gets the next double from the block
+ public virtual bool GetNextDouble(T2 key, NumberRange range, out double d)
+ {
+ d = -1;
+ if (rawValues.Count > 0)
+ {
+ KeyValuePair value = rawValues.Dequeue();
+ if(NumberFormats.TryParseDoubleVb6(value.Value, out double newValue))
+ {
+ switch (range)
+ {
+ case NumberRange.Any:
+ d = newValue;
+ return true;
+ case NumberRange.Positive:
+ if (newValue > 0)
+ {
+ d = newValue;
+ return true;
+ }
+ currentHost.AddMessage(MessageType.Warning, false, "Value " + newValue + " is not a positive double in Key " + key + " in Section " + Key + " at line " + value.Key);
+ return false;
+ case NumberRange.NonNegative:
+ if (newValue >= 0)
+ {
+ d = newValue;
+ return true;
+ }
+ currentHost.AddMessage(MessageType.Warning, false, "Value " + newValue + " is not a non-negative double in Key " + key + " in Section " + Key + " at line " + value.Key);
+ return false;
+ case NumberRange.NonZero:
+ if (newValue != 0)
+ {
+ d = newValue;
+ return true;
+ }
+ currentHost.AddMessage(MessageType.Warning, false, "Value " + newValue + " is not a non-zero double in Key " + key + " in Section " + Key + " at line " + value.Key);
+ return false;
+ }
+ }
+
+ currentHost.AddMessage("Value is invalid in " + key + " in " + Key + " at line " + value.Key + " in file " + FileName);
+ }
+ return false;
+ }
+
+ public virtual bool GetNextInt(T2 key, NumberRange range, out int d)
+ {
+ d = -1;
+ if (rawValues.Count > 0)
+ {
+ KeyValuePair value = rawValues.Dequeue();
+ if (NumberFormats.TryParseIntVb6(value.Value, out int newValue))
+ {
+ switch (range)
+ {
+ case NumberRange.Any:
+ d = newValue;
+ return true;
+ case NumberRange.Positive:
+ if (newValue > 0)
+ {
+ d = newValue;
+ return true;
+ }
+ currentHost.AddMessage(MessageType.Warning, false, "Value " + newValue + " is not a positive double in Key " + key + " in Section " + Key + " at line " + value.Key);
+ return false;
+ case NumberRange.NonNegative:
+ if (newValue >= 0)
+ {
+ d = newValue;
+ return true;
+ }
+ currentHost.AddMessage(MessageType.Warning, false, "Value " + newValue + " is not a non-negative double in Key " + key + " in Section " + Key + " at line " + value.Key);
+ return false;
+ case NumberRange.NonZero:
+ if (newValue != 0)
+ {
+ d = newValue;
+ return true;
+ }
+ currentHost.AddMessage(MessageType.Warning, false, "Value " + newValue + " is not a non-zero double in Key " + key + " in Section " + Key + " at line " + value.Key);
+ return false;
+ }
+ }
+
+ currentHost.AddMessage("Value is invalid in " + key + " in " + Key + " at line " + value.Key + " in file " + FileName);
+ }
+ return false;
+ }
+
+ public virtual bool GetNextBool(T2 key, out bool b)
+ {
+ if (rawValues.Count > 0)
+ {
+ KeyValuePair value = rawValues.Dequeue();
+ int i = int.Parse(value.Value);
+ b = i == 1;
+ return true;
+ }
+
+ b = false;
+ return false;
+ }
+
+ public virtual bool GetNextDoubleArray(char separator, out double[] array)
+ {
+ if (rawValues.Count > 0)
+ {
+ string s = rawValues.Dequeue().Value;
+ string[] splitString = s.Split(separator);
+ array = new double[splitString.Length];
+ for (int i = 0; i < array.Length; i++)
+ {
+ if (!NumberFormats.TryParseDoubleVb6(splitString[i], out array[i]))
+ {
+ Array.Resize(ref array, i);
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ array = Array.Empty();
+ return false;
+ }
+
+ public virtual bool GetNextEnumValue(T2 key, out T3 enumValue) where T3 : struct, Enum
+ {
+ if (rawValues.Count > 0)
+ {
+ string s = rawValues.Dequeue().Value;
+ if (Enum.TryParse(s, out enumValue) && Enum.IsDefined(typeof(T3), enumValue))
+ {
+ return true;
+ }
+ }
+ enumValue = default(T3);
+ return false;
+ }
+
public virtual bool GetNextPath(string absolutePath, out string finalPath)
{
if (rawValues.Count > 0)
diff --git a/source/Plugins/Formats.OpenBve/DAT/TrainDat.cs b/source/Plugins/Formats.OpenBve/DAT/TrainDat.cs
new file mode 100644
index 0000000000..9ed770a12a
--- /dev/null
+++ b/source/Plugins/Formats.OpenBve/DAT/TrainDat.cs
@@ -0,0 +1,165 @@
+using OpenBveApi.Hosts;
+using OpenBveApi.Math;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using OpenBveApi.Interface;
+
+namespace Formats.OpenBve
+{
+ /// Root block for a .CFG type file
+ public class TrainDatFile : Block where T1 : struct, Enum where T2 : struct, Enum
+ {
+ /// The format of the current file
+ public readonly TrainDatFormats Format;
+
+ public readonly int Version;
+ public TrainDatFile(string myFile, HostInterface currentHost) : base(-1, default, myFile, currentHost)
+ {
+ string[] lines = File.ReadAllLines(myFile);
+ List blockLines = new List();
+ bool addToBlock = false;
+ T1 previousSection = default(T1);
+ bool formatFound = false;
+ int startingLine = 0;
+ Version = 0;
+ for (int i = 0; i < lines.Length; i++)
+ {
+ int semiColon = lines[i].IndexOf(';');
+ if (semiColon != -1)
+ {
+ lines[i] = lines[i].Substring(0, semiColon);
+ }
+ lines[i] = lines[i].Trim();
+ if (lines[i].Length > 0 && !formatFound)
+ {
+ formatFound = true;
+ Format = ParseFormat(lines[i], out Version);
+ }
+
+ const int currentVersion = 18230;
+
+ switch (Format)
+ {
+ case TrainDatFormats.openBVE when Version == -1:
+ currentHost.AddMessage(MessageType.Error, false, "The train.dat version is invalid in " + FileName);
+ break;
+ case TrainDatFormats.openBVE:
+ {
+ if (Version > currentVersion)
+ {
+ currentHost.AddMessage(MessageType.Warning, false, "The train.dat " + FileName + " with version " + Version + " was created with a newer version of openBVE. Please check for an update.");
+ }
+ break;
+ }
+ case TrainDatFormats.Unsupported:
+ currentHost.AddMessage(MessageType.Error, false, "The train.dat format " + Version + " is not supported in " + FileName);
+ break;
+ case TrainDatFormats.UnknownBVE:
+ currentHost.AddMessage(MessageType.Error, false, "The train.dat appears to have been created by an unknown BVE version in " + FileName + " - Please report this.");
+ break;
+ case TrainDatFormats.MissingHeader:
+ currentHost.AddMessage(MessageType.Error, false, "The train.dat appears be missing the required header in " + FileName + " - Please report this.");
+ break;
+ }
+
+ if (lines[i].Length > 0 && lines[i][0] == '#')
+ {
+ if (!Enum.TryParse(lines[i].Substring(1), true, out T1 currentSection))
+ {
+ addToBlock = false;
+ }
+ else
+ {
+ addToBlock = true;
+ }
+
+ if (blockLines.Count > 0)
+ {
+ subBlocks.Add(new TrainDatSection(blockLines, previousSection, startingLine + 1, myFile, currentHost));
+ blockLines.Clear();
+ }
+
+ startingLine = i;
+ previousSection = currentSection;
+ }
+ else
+ {
+ if (addToBlock && !string.IsNullOrEmpty(lines[i]))
+ {
+ blockLines.Add(lines[i]);
+ }
+ }
+ }
+
+ if (blockLines.Count > 0)
+ {
+ subBlocks.Add(new TrainDatSection(blockLines, previousSection, startingLine, myFile, currentHost));
+ }
+ }
+
+
+
+ /// Parse the format of the specified train.dat
+ /// The version line of the train.dat
+ /// The version of the specified OpenBVE train.dat
+ public static TrainDatFormats ParseFormat(string line, out int version)
+ {
+ version = -1;
+ switch (line.ToLowerInvariant())
+ {
+ case "bve1200000":
+ return TrainDatFormats.BVE1200000;
+ case "bve1210000":
+ return TrainDatFormats.BVE1210000;
+ case "bve1220000":
+ return TrainDatFormats.BVE1220000;
+ case "bve2000000":
+ return TrainDatFormats.BVE2000000;
+ case "bve2060000":
+ return TrainDatFormats.BVE2060000;
+ case "openbve":
+ version = 0;
+ return TrainDatFormats.openBVE;
+ case "#acceleration":
+ case "#deceleration":
+ case "#delay":
+ case "#move":
+ case "#brake":
+ case "#pressure":
+ case "#handle":
+ case "#cab":
+ case "#car":
+ case "#device":
+ case "motor_p1":
+ case "motor_p2":
+ case "brake_p1":
+ case "brake_p2":
+ version = 0;
+ return TrainDatFormats.MissingHeader;
+ default:
+ if (line.ToLowerInvariant().StartsWith("openbve"))
+ {
+ string tt = line.Substring(7, line.Length - 7).Trim();
+ if (!NumberFormats.TryParseIntVb6(tt, out version))
+ {
+ version = -1;
+ }
+ return TrainDatFormats.openBVE;
+ }
+
+ return line.ToLowerInvariant().StartsWith("bve") ? TrainDatFormats.UnknownBVE : TrainDatFormats.Unsupported;
+ }
+ }
+ }
+ public class TrainDatSection : Block where T1 : struct, Enum where T2 : struct, Enum
+ {
+ public TrainDatSection(List blockLines, T1 myKey, int startingLine, string myFile, HostInterface currentHost) : base(-1, myKey, myFile, currentHost)
+ {
+ for (int i = 0; i < blockLines.Count; i++)
+ {
+ rawValues.Enqueue(new KeyValuePair(startingLine + i, blockLines[i]));
+ }
+ }
+ }
+}
diff --git a/source/Plugins/Train.OpenBve/Train/BVE/TrainDatparser.Formats.cs b/source/Plugins/Formats.OpenBve/Enums/TrainDat/TrainDatFormats.cs
similarity index 89%
rename from source/Plugins/Train.OpenBve/Train/BVE/TrainDatparser.Formats.cs
rename to source/Plugins/Formats.OpenBve/Enums/TrainDat/TrainDatFormats.cs
index 22b402e892..3bbd10b7df 100644
--- a/source/Plugins/Train.OpenBve/Train/BVE/TrainDatparser.Formats.cs
+++ b/source/Plugins/Formats.OpenBve/Enums/TrainDat/TrainDatFormats.cs
@@ -1,8 +1,7 @@
-// ReSharper disable InconsistentNaming
-namespace Train.OpenBve
+namespace Formats.OpenBve
{
/// The different train.dat format types
- internal enum TrainDatFormats
+ public enum TrainDatFormats
{
/// The train.dat format string is unsupported
Unsupported = -1,
diff --git a/source/Plugins/Formats.OpenBve/Enums/TrainDat/TrainDatKey.cs b/source/Plugins/Formats.OpenBve/Enums/TrainDat/TrainDatKey.cs
new file mode 100644
index 0000000000..8b237f5baa
--- /dev/null
+++ b/source/Plugins/Formats.OpenBve/Enums/TrainDat/TrainDatKey.cs
@@ -0,0 +1,56 @@
+namespace Formats.OpenBve
+{
+ public enum TrainDatKey
+ {
+ BrakeDeceleration,
+ CoefficientOfStaticFriction,
+ CoefficientOfRollingResistance,
+ CoefficientOfAerodynamicDrag,
+ ElectricBrakeDelayUp,
+ ElectricBrakeDelayDown,
+ JerkPowerUp,
+ JerkPowerDown,
+ JerkBrakeUp,
+ JerkBrakeDown,
+ BrakeCylinderUp,
+ BrakeCylinderDown,
+ BrakeSystemType,
+ ElectropneumaticType,
+ BrakeControlSpeed,
+ LocoBrakeType,
+ BrakeCylinderServiceMaximumPressure,
+ BrakeCylinderEmergencyMaximumPressure,
+ MainReservoirMinimumPressure,
+ MainReservoirMaximumPressure,
+ BrakePipePressure,
+ HandleType,
+ NumberOfPowerNotches,
+ NumberOfBrakeNotches,
+ PowerReduceSteps,
+ EbHandleBehaviour,
+ LocoBrakeNotches,
+ DriverPowerNotches,
+ DriverBrakeNotches,
+ DriverX,
+ DriverY,
+ DriverZ,
+ MotorCarMass,
+ NumberOfMotorCars,
+ TrailerCarMass,
+ NumberOfTrailerCars,
+ CarLength,
+ FrontCarIsMotorCar,
+ LengthOfCar,
+ WidthOfCar,
+ HeightOfCar,
+ ExposedFrontalArea,
+ UnexposedFrontalArea,
+ DefaultSafetySystems,
+ ReAdhesionDeviceType,
+ PassAlarm,
+ DoorOpenMode,
+ DoorCloseMode,
+ DoorWidth,
+ DoorMaxTolerance,
+ }
+}
diff --git a/source/Plugins/Formats.OpenBve/Enums/TrainDat/TrainDatSection.cs b/source/Plugins/Formats.OpenBve/Enums/TrainDat/TrainDatSection.cs
new file mode 100644
index 0000000000..c8c6f67801
--- /dev/null
+++ b/source/Plugins/Formats.OpenBve/Enums/TrainDat/TrainDatSection.cs
@@ -0,0 +1,37 @@
+namespace Formats.OpenBve
+{
+ /// The sections in a train.dat file
+ public enum TrainDatSection
+ {
+ /// Defines the acceleration characteristics of the train in each power notch
+ Acceleration,
+ /// Defines the brake co-efficients and other brake physics related values
+ Deceleration,
+ Performance = Deceleration,
+ /// Defines various delays associated with power and braking
+ Delay,
+ /// Defines the jerk values associated with power and braking
+ Move,
+ /// Defines the brake type and related settings
+ Brake,
+ /// Defines pressures for the train brake
+ Pressure,
+ /// Defines the handle types and number of notches
+ Handle,
+ /// Defines the location of the driver's eyes within the driver car
+ Cab,
+ Cockpit = Cab,
+ /// Defines the number of cars, and associated masses
+ Car,
+ /// Defines which built-in safety systems and other devices are present on the train
+ Device,
+ /// Defines the motor sound curves for P1
+ Motor_P1,
+ /// Defines the motor sound curves for P2
+ Motor_P2,
+ /// Defines the brake sound curves for P1
+ Motor_B1,
+ /// Defines the brake sound curves for P2
+ Motor_B2
+ }
+}
diff --git a/source/Plugins/Formats.OpenBve/Formats.OpenBve.csproj b/source/Plugins/Formats.OpenBve/Formats.OpenBve.csproj
index 710c56f37f..b6c84be2d9 100644
--- a/source/Plugins/Formats.OpenBve/Formats.OpenBve.csproj
+++ b/source/Plugins/Formats.OpenBve/Formats.OpenBve.csproj
@@ -58,6 +58,7 @@
+
@@ -82,6 +83,9 @@
+
+
+
diff --git a/source/Plugins/Train.MsTs/Train/VehicleParser.cs b/source/Plugins/Train.MsTs/Train/VehicleParser.cs
index eabbbc1e72..d37eca425c 100644
--- a/source/Plugins/Train.MsTs/Train/VehicleParser.cs
+++ b/source/Plugins/Train.MsTs/Train/VehicleParser.cs
@@ -377,8 +377,7 @@ internal void Parse(string trainSetDirectory, string wagonName, bool isEngine, r
if (r < 0.1) r = 0.1;
if (r > 1.0) r = 1.0;
airBrake.AuxiliaryReservoir = new AuxiliaryReservoir(0.975 * operatingPressure, 200000.0, 0.5, r);
- airBrake.EqualizingReservoir = new EqualizingReservoir(50000.0, 250000.0, 200000.0);
- airBrake.EqualizingReservoir.NormalPressure = 1.005 * operatingPressure;
+ airBrake.EqualizingReservoir = new EqualizingReservoir(50000.0, 250000.0, 200000.0, 1.005 * operatingPressure);
airBrake.StraightAirPipe = new StraightAirPipe(300000.0, emergencyRate * emergencyVal, releaseRate * releaseVal);
currentCar.CarBrake = airBrake;
@@ -390,8 +389,7 @@ internal void Parse(string trainSetDirectory, string wagonName, bool isEngine, r
vacuumBrake.MainReservoir = new MainReservoir(71110, 84660, 0.01, 0.075 / train.Cars.Length); // ~21in/hg - ~25in/hg
vacuumBrake.BrakeCylinder = new BrakeCylinder(brakeCylinderMaximumPressure, brakeCylinderMaximumPressure * 1.1, 90000.0, 300000.0, 200000.0);
vacuumBrake.AuxiliaryReservoir = new AuxiliaryReservoir(0.975 * brakeCylinderMaximumPressure, 200000.0, 0.5, 1.0);
- vacuumBrake.EqualizingReservoir = new EqualizingReservoir(50000.0, 250000.0, 200000.0);
- vacuumBrake.EqualizingReservoir.NormalPressure = 1.005 * (vacuumBrake.BrakeCylinder.EmergencyMaximumPressure + 0.75 * (vacuumBrake.MainReservoir.MinimumPressure - vacuumBrake.BrakeCylinder.EmergencyMaximumPressure));
+ vacuumBrake.EqualizingReservoir = new EqualizingReservoir(50000.0, 250000.0, 200000.0, 1.005 * (vacuumBrake.BrakeCylinder.EmergencyMaximumPressure + 0.75 * (vacuumBrake.MainReservoir.MinimumPressure - vacuumBrake.BrakeCylinder.EmergencyMaximumPressure)));
vacuumBrake.BrakePipe = new BrakePipe(brakeCylinderMaximumPressure, 10000000.0, 1500000.0, 5000000.0, false);
currentCar.CarBrake = vacuumBrake;
}
diff --git a/source/Plugins/Train.OpenBve/Plugin.cs b/source/Plugins/Train.OpenBve/Plugin.cs
index 346ff7ca16..1571c74a29 100644
--- a/source/Plugins/Train.OpenBve/Plugin.cs
+++ b/source/Plugins/Train.OpenBve/Plugin.cs
@@ -478,6 +478,16 @@ public override string GetDescription(string trainPath, Encoding encoding = null
// another variant on readme
descriptionFile = Path.CombineFile(trainPath, "read me.txt");
}
+
+ if (!File.Exists(descriptionFile))
+ {
+ // common pattern for UK BVE4 trains
+ string[] files = Directory.GetFiles(trainPath, "readme.bve4*.txt", SearchOption.TopDirectoryOnly);
+ if (files.Length > 0)
+ {
+ descriptionFile = files[0];
+ }
+ }
if (File.Exists(descriptionFile))
{
if (encoding == null)
diff --git a/source/Plugins/Train.OpenBve/Train.OpenBve.csproj b/source/Plugins/Train.OpenBve/Train.OpenBve.csproj
index 2e17a7051f..a84a9b84b0 100644
--- a/source/Plugins/Train.OpenBve/Train.OpenBve.csproj
+++ b/source/Plugins/Train.OpenBve/Train.OpenBve.csproj
@@ -69,7 +69,6 @@
-
diff --git a/source/Plugins/Train.OpenBve/Train/BVE/TrainDatParser.cs b/source/Plugins/Train.OpenBve/Train/BVE/TrainDatParser.cs
index d2c13cb541..3b8b129cbf 100644
--- a/source/Plugins/Train.OpenBve/Train/BVE/TrainDatParser.cs
+++ b/source/Plugins/Train.OpenBve/Train/BVE/TrainDatParser.cs
@@ -1,13 +1,12 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
+using System.IO;
using System.Text;
+using Formats.OpenBve;
using LibRender2.Trains;
using OpenBveApi;
using OpenBveApi.Interface;
using OpenBveApi.Math;
using OpenBveApi.Routes;
-using OpenBveApi.Trains;
using TrainManager.BrakeSystems;
using TrainManager.Car;
using TrainManager.Handles;
@@ -27,130 +26,6 @@ internal TrainDatParser(Plugin plugin)
Plugin = plugin;
}
- ///
- /// Read the file of the specified train.dat
- ///
- /// The file path of the specified train.dat
- /// The text encoding to use
- /// The array read from the specified train.dat
- private static string[] ReadFile(string fileName, Encoding encoding)
- {
- //Create the array using the default compatibility train.dat
- string[] lines = { "BVE2000000", "#CAR", "1", "1", "1", "0", "1", "1" };
-
- // load file
- try
- {
- lines = System.IO.File.ReadAllLines(fileName, encoding);
- }
- catch
- {
- //ignore and load default
- }
- if (lines.Length == 1 && encoding.Equals(Encoding.Unicode))
- {
- /*
- * Probably not unicode after all
- * Stuff edited with BVE2 / BVE4 tools should either be ASCII or SHIFT_JIS
- * both of which should read OK with ASCII for our purposes
- */
- encoding = Encoding.ASCII;
- lines = System.IO.File.ReadAllLines(fileName, encoding);
- }
- else if (lines.Length == 0)
- {
- //Catch zero-length train.dat files
- throw new Exception("The train.dat file " + fileName + " is of zero length.");
- }
-
- 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();
- }
- if (lines[i].EndsWith(","))
- {
- //File edited with MSExcel may have additional commas at the end of a line
- lines[i] = lines[i].TrimEnd(',');
- }
- }
-
- return lines;
- }
-
- /// Parse the format of the specified train.dat
- /// The array of the specified train.dat
- /// The version of the specified OpenBVE train.dat
- private static TrainDatFormats ParseFormat(IReadOnlyList lines, out int version)
- {
- version = -1;
- for (int i = 0; i < lines.Count; i++)
- {
- if (lines[i].Length <= 0)
- {
- continue;
- }
-
- string t = lines[i].ToLowerInvariant();
- switch (t)
- {
- case "bve1200000":
- return TrainDatFormats.BVE1200000;
- case "bve1210000":
- return TrainDatFormats.BVE1210000;
- case "bve1220000":
- return TrainDatFormats.BVE1220000;
- case "bve2000000":
- return TrainDatFormats.BVE2000000;
- case "bve2060000":
- return TrainDatFormats.BVE2060000;
- case "openbve":
- version = 0;
- return TrainDatFormats.openBVE;
- case "#acceleration":
- case "#deceleration":
- case "#delay":
- case "#move":
- case "#brake":
- case "#pressure":
- case "#handle":
- case "#cab":
- case "#car":
- case "#device":
- case "motor_p1":
- case "motor_p2":
- case "brake_p1":
- case "brake_p2":
- version = 0;
- return TrainDatFormats.MissingHeader;
- default:
- if (t.ToLowerInvariant().StartsWith("openbve"))
- {
- string tt = t.Substring(7, t.Length - 7).Trim();
- if (!NumberFormats.TryParseIntVb6(tt, out version))
- {
- version = -1;
- }
- return TrainDatFormats.openBVE;
- }
-
- if (t.ToLowerInvariant().StartsWith("bve"))
- {
- return TrainDatFormats.UnknownBVE;
- }
-
- return TrainDatFormats.Unsupported;
- }
- }
- return TrainDatFormats.Unsupported;
- }
-
///
/// Checks whether the parser can load the specified file.
///
@@ -159,25 +34,29 @@ private static TrainDatFormats ParseFormat(IReadOnlyList lines, out int
internal bool CanLoad(string fileName)
{
Encoding encoding = TextEncoding.GetSystemEncodingFromFile(fileName);
-
- string[] lines;
try
{
- lines = ReadFile(fileName, encoding);
+ string[] lines = File.ReadAllLines(fileName, encoding);
+ for (int i = 0; i < lines.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(lines[i]) && !lines[i].StartsWith(";"))
+ {
+ TrainDatFormats format = TrainDatFile.ParseFormat(lines[i], out _);
+ if (format == TrainDatFormats.MissingHeader)
+ {
+ // Some NYCTA stuff seems to be missing the version header from their train.dat files
+ // absolute mess....
+ Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "The train.dat file " + fileName + " has a missing version header.");
+ }
+ return format != TrainDatFormats.Unsupported;
+ }
+ }
}
catch
{
return false;
}
-
- TrainDatFormats format = ParseFormat(lines, out _);
- if (format == TrainDatFormats.MissingHeader)
- {
- // Some NYCTA stuff seems to be missing the version header from their train.dat files
- // absolute mess....
- Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "The train.dat file " + fileName + " has a missing version header.");
- }
- return format != TrainDatFormats.Unsupported;
+ return false;
}
/// Parses a BVE2 / BVE4 / openBVE train.dat file
@@ -185,35 +64,6 @@ internal bool CanLoad(string fileName)
/// The text encoding to use
/// The train
internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
- System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
-
- string[] Lines = ReadFile(FileName, Encoding);
-
- // Check version
- const int currentVersion = 18230;
- TrainDatFormats currentFormat = ParseFormat(Lines, out int myVersion);
- string versionString = Lines.FirstOrDefault(x => x.Length > 0) ?? Lines[0];
- switch (currentFormat)
- {
- case TrainDatFormats.openBVE when myVersion == -1:
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The train.dat version " + versionString + " is invalid in " + FileName);
- break;
- case TrainDatFormats.openBVE:
- {
- if (myVersion > currentVersion)
- {
- Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "The train.dat " + FileName + " with version " + versionString + " was created with a newer version of openBVE. Please check for an update.");
- }
- break;
- }
- case TrainDatFormats.Unsupported:
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The train.dat format " + versionString + " is not supported in " + FileName);
- break;
- case TrainDatFormats.UnknownBVE:
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The train.dat format " + versionString + " appears to have been created by an unknown BVE version. in " + FileName + " - Please report this.");
- break;
- }
-
// initialize
double BrakeCylinderServiceMaximumPressure = 440000.0;
double BrakeCylinderEmergencyMaximumPressure = 440000.0;
@@ -254,6 +104,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
Train.Specs.AveragesPressureDistribution = true;
double[] powerDelayUp = { }, powerDelayDown = { }, brakeDelayUp = { }, brakeDelayDown = { }, locoBrakeDelayUp = { }, locoBrakeDelayDown = { };
double electricBrakeDelayUp = 0, electricBrakeDelayDown = 0;
+
int powerNotches = 0, brakeNotches = 0, locoBrakeNotches = 0, powerReduceSteps = -1, locoBrakeType = 0, driverPowerNotches = 0, driverBrakeNotches = 0;
BVEMotorSoundTable[] Tables = new BVEMotorSoundTable[4];
for (int i = 0; i < 4; i++) {
@@ -265,752 +116,434 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
}
}
// parse configuration
- double invfac = Lines.Length == 0 ? 0.1 : 0.1 / Lines.Length;
- for (int i = 0; i < Lines.Length; i++) {
- Plugin.CurrentProgress = Plugin.LastProgress + invfac * i;
- if ((i & 7) == 0) {
- System.Threading.Thread.Sleep(1);
- if (Plugin.Cancel) return;
- }
- int n = 0;
- switch (Lines[i].ToLowerInvariant()) {
- case "#acceleration":
- i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- if (string.IsNullOrEmpty(Lines[i]))
+ TrainDatFile datFile = new TrainDatFile(FileName, Plugin.CurrentHost);
+ double invfac = datFile.RemainingSubBlocks == 0 ? 0.1 : 0.1 / datFile.RemainingSubBlocks;
+ int totalBlocks = datFile.RemainingSubBlocks;
+ while (datFile.RemainingSubBlocks > 0)
+ {
+ Block subBlock = datFile.ReadNextBlock();
+ switch (subBlock.Key)
+ {
+ case TrainDatSection.Acceleration:
+ while (subBlock.RemainingDataValues > 0)
+ {
+ if (subBlock.GetNextDoubleArray(',', out double[] curveValues) && curveValues.Length >= 4)
{
- i++;
- continue;
- }
- Array.Resize(ref AccelerationCurves, n + 1);
- AccelerationCurves[n] = new BveAccelerationCurve();
- string t = Lines[i] + ",";
- int m = 0;
- while (true) {
- int j = t.IndexOf(',');
- if (j == -1) break;
- string s = t.Substring(0, j).Trim();
- t = t.Substring(j + 1);
- if (NumberFormats.TryParseDoubleVb6(s, out var a)) {
- switch (m) {
- case 0:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "a0 in section #ACCELERATION is expected to be greater than zero at line " + (i + 1).ToString(Culture) + " in file " + FileName);
- } else {
- AccelerationCurves[n].StageZeroAcceleration = a * 0.277777777777778;
- } break;
- case 1:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "a1 in section #ACCELERATION is expected to be greater than zero at line " + (i + 1).ToString(Culture) + " in file " + FileName);
- } else {
- AccelerationCurves[n].StageOneAcceleration = a * 0.277777777777778;
- } break;
- case 2:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "v1 in section #ACCELERATION is expected to be greater than zero at line " + (i + 1).ToString(Culture) + " in file " + FileName);
- } else {
- AccelerationCurves[n].StageOneSpeed = a * 0.277777777777778;
- } break;
- case 3:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "v2 in section #ACCELERATION is expected to be greater than zero at line " + (i + 1).ToString(Culture) + " in file " + FileName);
- } else {
- AccelerationCurves[n].StageTwoSpeed = a * 0.277777777777778;
- if (AccelerationCurves[n].StageTwoSpeed < AccelerationCurves[n].StageOneSpeed) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "v2 in section #ACCELERATION is expected to be greater than or equal to v1 at line " + (i + 1).ToString(Culture) + " in file " + FileName);
- AccelerationCurves[n].StageTwoSpeed = AccelerationCurves[n].StageOneSpeed;
- }
- } break;
- case 4:
- {
- if (currentFormat == TrainDatFormats.BVE1200000 || currentFormat == TrainDatFormats.BVE1210000 || currentFormat == TrainDatFormats.BVE1220000) {
- if (a <= 0.0) {
- AccelerationCurves[n].StageTwoExponent = 1.0;
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "e in section #ACCELERATION is expected to be positive at line " + (i + 1).ToString(Culture) + " in file " + FileName);
- } else {
- const double c = 4.439346232277577;
- AccelerationCurves[n].StageTwoExponent = 1.0 - Math.Log(a) * AccelerationCurves[n].StageTwoSpeed * c;
- if (AccelerationCurves[n].StageTwoExponent <= 0.0) {
- AccelerationCurves[n].StageTwoExponent = 1.0;
- } else if (AccelerationCurves[n].StageTwoExponent > 4.0) {
- AccelerationCurves[n].StageTwoExponent = 4.0;
- }
- }
- } else {
- AccelerationCurves[n].StageTwoExponent = a;
- if (AccelerationCurves[n].StageTwoExponent <= 0.0) {
- AccelerationCurves[n].StageTwoExponent = 1.0;
- }
- }
- } break;
- }
- } m++;
- } i++; n++;
- } i--; break;
- case "#performance":
- case "#deceleration":
- i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- if (NumberFormats.TryParseDoubleVb6(Lines[i], out var a)) {
- switch (n) {
- case 0:
- if (a < 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BrakeDeceleration is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- BrakeDeceleration = a * 0.277777777777778;
- } break;
- case 1:
- if (a < 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "CoefficientOfStaticFriction is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- if (Plugin.CurrentOptions.EnableBveTsHacks && (a > 0.1 || a < 1.0))
- {
- break;
- }
- CoefficientOfStaticFriction = a;
- } break;
- case 3:
- if (a < 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "CoefficientOfRollingResistance is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- CoefficientOfRollingResistance = a;
- } break;
- case 4:
- if (a < 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "AerodynamicDragCoefficient is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- AerodynamicDragCoefficient = a;
- } break;
+ Array.Resize(ref AccelerationCurves, AccelerationCurves.Length + 1);
+ BveAccelerationCurve curve = new BveAccelerationCurve();
+ if (curveValues[0] > 0)
+ {
+ curve.StageZeroAcceleration = curveValues[0] * 0.277777777777778;
}
- } i++; n++;
- } i--; break;
- case "#delay":
- i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- if (NumberFormats.TryParseDoubleVb6(Lines[i], out var a)) {
- switch (n)
+ else
{
- case 0:
- if (currentFormat == TrainDatFormats.openBVE && myVersion >= 1534)
- {
- powerDelayUp = Lines[i].Split( ',').Select(x => double.Parse(x, Culture)).ToArray();
- }
- else
- {
- if (Plugin.CurrentOptions.EnableBveTsHacks && a > 60)
- {
- break;
- }
- powerDelayUp = new[] {a};
- }
- break;
- case 1:
- if (currentFormat == TrainDatFormats.openBVE && myVersion >= 1534)
- {
- powerDelayDown = Lines[i].Split(',').Select(x => double.Parse(x, Culture)).ToArray();
- }
- else
- {
- if (Plugin.CurrentOptions.EnableBveTsHacks && a > 60)
- {
- break;
- }
- powerDelayDown = new[] {a};
- }
- break;
- case 2:
- if (currentFormat == TrainDatFormats.openBVE && myVersion >= 1534)
- {
- brakeDelayUp = Lines[i].Split(',').Select(x => double.Parse(x, Culture)).ToArray();
- }
- else
- {
- if (Plugin.CurrentOptions.EnableBveTsHacks && a > 60)
- {
- break;
- }
- brakeDelayUp = new[] {a};
- }
- break;
- case 3:
- if (currentFormat == TrainDatFormats.openBVE && myVersion >= 1534)
- {
- brakeDelayDown = Lines[i].Split(',').Select(x => double.Parse(x, Culture)).ToArray();
- }
- else
- {
- if (Plugin.CurrentOptions.EnableBveTsHacks && a > 60)
- {
- break;
- }
- brakeDelayDown = new[] {a};
- }
- break;
- /*
- * https://github.com/leezer3/OpenBVE/issues/737
- * OpenBVE appears to have overlooked these originally
- * We can (hopefully) assume that any trains using the LocoBrake feature
- * will have set the OPENBVE header, hence it's reasonable to assume that
- * others will actually be genuine users
- *
- * Don't split per-notch, as this wll just cause more confusion
- */
- case 4:
- case 6:
- switch (currentFormat)
- {
- case TrainDatFormats.openBVE:
- if (myVersion >= 1830 && n == 4)
- {
- electricBrakeDelayUp = a;
- }
- else if (myVersion >= 1534)
- {
- locoBrakeDelayUp = Lines[i].Split(',').Select(x => double.Parse(x, Culture)).ToArray();
- }
- else
- {
- if (Plugin.CurrentOptions.EnableBveTsHacks && a > 60)
- {
- break;
- }
- locoBrakeDelayUp = new[] {a};
- }
- break;
- default:
- electricBrakeDelayUp = a;
- break;
- }
- break;
- case 5:
- case 7:
- switch (currentFormat)
- {
- case TrainDatFormats.openBVE:
- if (myVersion >= 1830 && n == 5)
- {
- electricBrakeDelayDown = a;
- }
- else if(myVersion >= 1534)
- {
- locoBrakeDelayDown = Lines[i].Split(',').Select(x => double.Parse(x, Culture)).ToArray();
- }
- else
- {
- if (Plugin.CurrentOptions.EnableBveTsHacks && a > 60)
- {
- break;
- }
- locoBrakeDelayDown = new[] {a};
- }
- break;
- default:
- electricBrakeDelayDown = a;
- break;
- }
- break;
+ Plugin.CurrentHost.AddMessage(MessageType.Error, false, "a0 in section #ACCELERATION is expected to be greater than zero for Curve " + AccelerationCurves.Length + " in file " + FileName);
}
- } i++; n++;
- } i--; break;
- case "#move":
- i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- if (NumberFormats.TryParseDoubleVb6(Lines[i], out var a)) {
- switch (n) {
- case 0:
- if (a != 0)
- {
- JerkPowerUp = 0.01 * a;
- }
- else
- {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "JerkPowerUp is expected to be non-zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
- break;
- case 1:
- if (a != 0)
- {
- JerkPowerDown = 0.01 * a;
- }
- else
- {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "JerkPowerDown is expected to be non-zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
- break;
- case 2:
- if (a != 0)
- {
- JerkBrakeUp = 0.01 * a;
- }
- else
- {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "JerkBrakeUp is expected to be non-zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
- break;
- case 3:
- if (a != 0)
- {
- JerkBrakeDown = 0.01 * a;
- }
- else
- {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "JerkBrakeDown is expected to be non-zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
- break;
- case 4:
- if (a >= 0)
- {
- BrakeCylinderUp = 1000.0 * a;
- }
- else
- {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BrakeCylinderUp is expected to be greater than zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
- break;
- case 5:
- if (a >= 0)
- {
- BrakeCylinderDown = 1000.0 * a;
- }
- else
- {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BrakeCylinderDown is expected to be greater than zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
- break;
+ if (curveValues[1] > 0)
+ {
+ curve.StageOneAcceleration = curveValues[1] * 0.277777777777778;
}
- } i++; n++;
- } i--; break;
- case "#brake":
- i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- if (NumberFormats.TryParseDoubleVb6(Lines[i], out var a))
- {
- int b;
- switch (n)
+ else
{
- case 0:
- b = (int) Math.Round(a);
- if (b >= 0 & b <= 2)
- {
- trainBrakeType = (BrakeSystemType) b;
- }
- else
- {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The setting for BrakeType is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName);
- trainBrakeType = BrakeSystemType.ElectromagneticStraightAirBrake;
- }
- break;
- case 1:
- b = (int) Math.Round(a);
- if (b >= 0 & b <= 2)
- {
- ElectropneumaticType = (EletropneumaticBrakeType) b;
- }
- else
- {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "The setting for ElectropneumaticType is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName);
- ElectropneumaticType = EletropneumaticBrakeType.None;
- }
- break;
- case 2:
- if (a < 0)
- {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BrakeControlSpeed must be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName);
- break;
- }
- if (a != 0 && trainBrakeType == BrakeSystemType.AutomaticAirBrake)
- {
- Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "BrakeControlSpeed will be ignored due to the current brake setup at line " + (i + 1).ToString(Culture) + " in " + FileName);
- break;
- }
- BrakeControlSpeed = a * 0.277777777777778; //Convert to m/s
- break;
- case 3:
- b = (int) Math.Round(a);
- switch (b)
- {
- case 0:
- //Not fitted
- break;
- case 1:
- //Notched air brake
- Train.Handles.HasLocoBrake = true;
- locomotiveBrakeType = BrakeSystemType.ElectromagneticStraightAirBrake;
- break;
- case 2:
- //Automatic air brake
- Train.Handles.HasLocoBrake = true;
- locomotiveBrakeType = BrakeSystemType.AutomaticAirBrake;
- break;
- }
- break;
+ Plugin.CurrentHost.AddMessage(MessageType.Error, false, "a1 in section #ACCELERATION is expected to be greater than zero for Curve " + AccelerationCurves.Length + " in file " + FileName);
}
- } i++; n++;
- } i--; break;
- case "#pressure":
- i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- if (NumberFormats.TryParseDoubleVb6(Lines[i], out var a)) {
- switch (n) {
- case 0:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BrakeCylinderServiceMaximumPressure is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- BrakeCylinderServiceMaximumPressure = a * 1000.0;
- } break;
- case 1:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BrakeCylinderEmergencyMaximumPressure is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- BrakeCylinderEmergencyMaximumPressure = a * 1000.0;
- } break;
- case 2:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MainReservoirMinimumPressure is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- MainReservoirMinimumPressure = a * 1000.0;
- } break;
- case 3:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MainReservoirMaximumPressure is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- MainReservoirMaximumPressure = a * 1000.0;
- } break;
- case 4:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BrakePipePressure is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- BrakePipePressure = a * 1000.0;
- } break;
+ if (curveValues[2] > 0)
+ {
+ curve.StageOneSpeed = curveValues[2] * 0.277777777777778;
}
- } i++; n++;
- } i--; break;
- case "#handle":
- i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- if (NumberFormats.TryParseIntVb6(Lines[i], out var a)) {
- switch (n) {
- case 0:
- switch (a)
- {
- case 0:
- Train.Handles.HandleType = HandleType.TwinHandle;
- break;
- case 1:
- Train.Handles.HandleType = HandleType.SingleHandle;
- break;
- case 2:
- Train.Handles.HandleType = HandleType.InterlockedTwinHandle;
- break;
- case 3:
- Train.Handles.HandleType = HandleType.InterlockedReverserHandle;
- break;
- default:
- Train.Handles.HandleType = HandleType.TwinHandle;
- break;
- }
- break;
- case 1:
- if (a > 0)
- {
- powerNotches = a;
- }
- else
- {
- powerNotches = 8;
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "NumberOfPowerNotches is expected to be positive and non-zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
- break;
- case 2:
- if (a > 0)
- {
- brakeNotches = a;
- }
- else
- {
- brakeNotches = 8;
- if (trainBrakeType != BrakeSystemType.AutomaticAirBrake)
- {
- /*
- * NumberOfBrakeNotches is ignored when using the auto-air brake
- * Whilst this value is invalid, it doesn't actually get used so get
- * rid of the pointless error message it generates
- */
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "NumberOfBrakeNotches is expected to be positive and non-zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
- }
- break;
- case 3:
- powerReduceSteps = a;
- break;
- case 4:
- if (a < 0 || a > 3)
- {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "EbHandleBehaviour is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName);
- break;
- }
- Train.Handles.EmergencyBrake.OtherHandlesBehaviour = (EbHandleBehaviour) a;
- break;
- case 5:
- if (a >= 0)
+ else
+ {
+ Plugin.CurrentHost.AddMessage(MessageType.Error, false, "v1 in section #ACCELERATION is expected to be greater than zero for Curve " + AccelerationCurves.Length + " in file " + FileName);
+ }
+ if (curveValues[3] > 0)
+ {
+ curve.StageTwoSpeed = curveValues[3] * 0.277777777777778;
+ if (curve.StageTwoSpeed < curve.StageOneSpeed)
+ {
+ Plugin.CurrentHost.AddMessage(MessageType.Error, false, "v2 in section #ACCELERATION is expected to be greater than or equal to v1 for Curve " + AccelerationCurves.Length + " in file " + FileName);
+ curve.StageTwoSpeed = curve.StageOneSpeed;
+ }
+ }
+ else
+ {
+ Plugin.CurrentHost.AddMessage(MessageType.Error, false, "v2 in section #ACCELERATION is expected to be greater than zero for Curve " + AccelerationCurves.Length + " in file " + FileName);
+ }
+ if (curveValues[4] > 0)
+ {
+ if (datFile.Format > TrainDatFormats.BVE2000000)
+ {
+ if (curveValues[4] <= 0.0)
{
-
- locoBrakeNotches = a;
+ curve.StageTwoExponent = 1.0;
+ Plugin.CurrentHost.AddMessage(MessageType.Error, false, "e in section #ACCELERATION is expected to be positive for Curve " + AccelerationCurves.Length + " in file " + FileName);
}
else
{
- locoBrakeNotches = 8;
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "NumberOfLocoBrakeNotches is expected to be positive and non-zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
-
- break;
- case 6:
- locoBrakeType = a;
- break;
- case 7:
- if (currentFormat == TrainDatFormats.openBVE && myVersion >= 15311)
- {
- if (a > 0)
+ const double c = 4.439346232277577;
+ curve.StageTwoExponent = 1.0 - Math.Log(curveValues[4]) * curve.StageTwoSpeed * c;
+ if (curve.StageTwoExponent <= 0.0)
{
- driverPowerNotches = a;
+ curve.StageTwoExponent = 1.0;
}
- else
+ else if (curve.StageTwoExponent > 4.0)
{
- driverPowerNotches = 8;
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "NumberOfDriverPowerNotches is expected to be positive and non-zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
+ curve.StageTwoExponent = 4.0;
}
}
- break;
- case 8:
- if (currentFormat == TrainDatFormats.openBVE && myVersion >= 15311)
+ }
+ else
+ {
+ curve.StageTwoExponent = curveValues[4];
+ if (curve.StageTwoExponent <= 0.0)
{
- if (a > 0)
- {
- driverBrakeNotches = a;
- }
- else
- {
- driverBrakeNotches = 8;
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "NumberOfDriverBrakeNotches is expected to be positive and non-zero at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
+ curve.StageTwoExponent = 1.0;
}
- break;
+ }
}
- } i++; n++;
- } i--; break;
- case "#cockpit":
- case "#cab":
- i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- if (NumberFormats.TryParseDoubleVb6(Lines[i], out var a)) {
- switch (n) {
- case 0: Driver.X = 0.001 * a; break;
- case 1: Driver.Y = 0.001 * a; break;
- case 2: Driver.Z = 0.001 * a; break;
- case 3: DriverCar = (int)Math.Round(a); break;
+ AccelerationCurves[AccelerationCurves.Length - 1] = curve;
+ }
+ else
+ {
+ Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid acceleration curve entry for Curve " + AccelerationCurves.Length + " in file " + FileName);
+ }
+ }
+ break;
+ case TrainDatSection.Performance:
+ if (subBlock.GetNextDouble(TrainDatKey.BrakeDeceleration, NumberRange.Positive, out double brakeDeceleration))
+ {
+ BrakeDeceleration = brakeDeceleration * 0.277777777777778;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.CoefficientOfStaticFriction, NumberRange.Positive, out double staticFriction))
+ {
+ if (Plugin.CurrentOptions.EnableBveTsHacks && (staticFriction > 0.1 || staticFriction < 1.0))
+ {
+ break;
+ }
+ CoefficientOfStaticFriction = staticFriction;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.CoefficientOfRollingResistance, NumberRange.Positive, out double rollingResistance))
+ {
+ CoefficientOfRollingResistance = rollingResistance;
+ }
+
+ if (subBlock.GetNextDouble(TrainDatKey.CoefficientOfAerodynamicDrag, NumberRange.Positive, out double aerodynamicDrag))
+ {
+ AerodynamicDragCoefficient = aerodynamicDrag;
+ }
+ break;
+ case TrainDatSection.Delay:
+ if (subBlock.GetNextDoubleArray(',', out powerDelayUp))
+ {
+ if (datFile.Format != TrainDatFormats.openBVE || datFile.Version < 1534)
+ {
+ Array.Resize(ref powerDelayUp, 1);
+ if (Plugin.CurrentOptions.EnableBveTsHacks && powerDelayUp[0] > 60)
+ {
+ powerDelayUp[0] = 0;
}
- } i++; n++;
- } i--; break;
- case "#car":
- i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- if (NumberFormats.TryParseDoubleVb6(Lines[i], out var a)) {
- switch (n) {
- case 0:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "MotorCarMass is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- MotorCarMass = a * 1000.0;
- } break;
- case 1:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "NumberOfMotorCars is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- MotorCars = (int)Math.Round(a);
- } break;
- case 2: TrailerCarMass = a * 1000.0; break;
- case 3:
- if (a < 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "NumberOfTrailerCars is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- TrailerCars = (int)Math.Round(a);
- } break;
- case 4:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "LengthOfACar is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- CarLength = a;
- } break;
- case 5: FrontCarIsMotorCar = a == 1.0; break;
- case 6:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "WidthOfACar is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- CarWidth = a;
- CarExposedFrontalArea = 0.65 * CarWidth * CarHeight;
- CarUnexposedFrontalArea = 0.2 * CarWidth * CarHeight;
- } break;
- case 7:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "HeightOfACar is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- CarHeight = a;
- CarExposedFrontalArea = 0.65 * CarWidth * CarHeight;
- CarUnexposedFrontalArea = 0.2 * CarWidth * CarHeight;
- } break;
- case 8: CenterOfGravityHeight = a; break;
- case 9:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ExposedFrontalArea is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- CarExposedFrontalArea = a;
- CarUnexposedFrontalArea = 0.2 * CarWidth * CarHeight;
- } break;
- case 10:
- if (a <= 0.0) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "UnexposedFrontalArea is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } else {
- CarUnexposedFrontalArea = a;
- } break;
+ }
+ }
+ if (subBlock.GetNextDoubleArray(',', out powerDelayDown))
+ {
+ if (datFile.Format != TrainDatFormats.openBVE || datFile.Version < 1534)
+ {
+ Array.Resize(ref powerDelayDown, 1);
+ if (Plugin.CurrentOptions.EnableBveTsHacks && powerDelayDown[0] > 60)
+ {
+ powerDelayDown[0] = 0;
}
- } i++; n++;
- } i--; break;
- case "#device":
- i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- if (NumberFormats.TryParseDoubleVb6(Lines[i], out var a)) {
- switch (n) {
- case 0:
- if (a == 0.0) {
- Train.Specs.DefaultSafetySystems |= DefaultSafetySystems.AtsSn;
- } else if (a == 1.0) {
- Train.Specs.DefaultSafetySystems |= DefaultSafetySystems.AtsSn;
- Train.Specs.DefaultSafetySystems |= DefaultSafetySystems.AtsP;
- }
- break;
- case 1:
- if (a == 1.0 | a == 2.0) {
- Train.Specs.DefaultSafetySystems |= DefaultSafetySystems.Atc;
- }
- break;
- case 2:
- if (a == 1.0) {
- Train.Specs.DefaultSafetySystems |= DefaultSafetySystems.Eb;
- }
- break;
- case 3:
- Train.Specs.HasConstSpeed = a == 1.0; break;
- case 4:
- Train.Handles.HasHoldBrake = a == 1.0; break;
- case 5:
- int dt = (int) Math.Round(a);
- if (dt < 4 && dt > -1)
- {
- ReAdhesionDevice = (ReadhesionDeviceType)dt;
- }
- else
- {
- ReAdhesionDevice = ReadhesionDeviceType.NotFitted;
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "ReAdhesionDeviceType is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName);
- }
- break;
- case 7:
- {
- int b = (int)Math.Round(a);
- if (b >= 0 & b <= 2) {
- passAlarm = (PassAlarmType)b;
- } else {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "PassAlarm is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } break;
- }
- case 8:
- {
- int b = (int)Math.Round(a);
- if (b >= 0 & b <= 2) {
- Train.Specs.DoorOpenMode = (DoorMode)b;
- } else {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "DoorOpenMode is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } break;
- }
- case 9:
- {
- int b = (int)Math.Round(a);
- if (b >= 0 & b <= 2) {
- Train.Specs.DoorCloseMode = (DoorMode)b;
- } else {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "DoorCloseMode is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } break;
- }
- case 10:
- {
- if (a >= 0.0) {
- DoorWidth = a;
- } else {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "DoorWidth is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } break;
- }
- case 11:
- {
- if (a >= 0.0) {
- DoorTolerance = a;
- } else {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "DoorMaxTolerance is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName);
- } break;
- }
+ }
+ }
+ if (subBlock.GetNextDoubleArray(',', out brakeDelayUp))
+ {
+ if (datFile.Format != TrainDatFormats.openBVE || datFile.Version < 1534)
+ {
+ Array.Resize(ref brakeDelayUp, 1);
+ if (Plugin.CurrentOptions.EnableBveTsHacks && brakeDelayUp[0] > 60)
+ {
+ brakeDelayUp[0] = 0;
}
- } i++; n++;
- } i--; break;
- case "#motor_p1":
- case "#motor_p2":
- case "#motor_b1":
- case "#motor_b2":
+ }
+ }
+ if (subBlock.GetNextDoubleArray(',', out brakeDelayDown))
{
- int msi = 0;
- switch (Lines[i].ToLowerInvariant()) {
- case "#motor_p1": msi = BVEMotorSound.MotorP1; break;
- case "#motor_p2": msi = BVEMotorSound.MotorP2; break;
- case "#motor_b1": msi = BVEMotorSound.MotorB1; break;
- case "#motor_b2": msi = BVEMotorSound.MotorB2; break;
- } i++;
- while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) {
- int u = Tables[msi].Entries.Length;
- if (n >= u) {
- Array.Resize(ref Tables[msi].Entries, 2 * u);
- for (int j = u; j < 2 * u; j++) {
- Tables[msi].Entries[j].SoundIndex = -1;
- Tables[msi].Entries[j].Pitch = 1.0f;
- Tables[msi].Entries[j].Gain = 1.0f;
- }
+ if (datFile.Format != TrainDatFormats.openBVE || datFile.Version < 1534)
+ {
+ Array.Resize(ref brakeDelayDown, 1);
+ if (Plugin.CurrentOptions.EnableBveTsHacks && brakeDelayDown[0] > 60)
+ {
+ brakeDelayDown[0] = 0;
+ }
+ }
+ }
+ /*
+ * https://github.com/leezer3/OpenBVE/issues/737
+ * OpenBVE appears to have overlooked these originally
+ * We can (hopefully) assume that any trains using the LocoBrake feature
+ * will have set the OPENBVE header, hence it's reasonable to assume that
+ * others will actually be genuine users
+ *
+ * Don't split per-notch, as this wll just cause more confusion
+ */
+ if (datFile.Format != TrainDatFormats.openBVE || datFile.Version >= 1830)
+ {
+ subBlock.GetNextDouble(TrainDatKey.ElectricBrakeDelayUp, NumberRange.Positive, out electricBrakeDelayUp);
+ subBlock.GetNextDouble(TrainDatKey.ElectricBrakeDelayDown, NumberRange.Positive, out electricBrakeDelayDown);
+ }
+ subBlock.GetNextDoubleArray(',', out locoBrakeDelayUp);
+ subBlock.GetNextDoubleArray(',', out locoBrakeDelayDown);
+ break;
+ case TrainDatSection.Move:
+ if (subBlock.GetNextDouble(TrainDatKey.JerkPowerUp, NumberRange.NonZero, out double jerkUp))
+ {
+ JerkPowerUp = 0.01 * jerkUp;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.JerkPowerUp, NumberRange.NonZero, out double jerkDown))
+ {
+ JerkPowerDown = 0.01 * jerkDown;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.JerkBrakeUp, NumberRange.NonZero, out jerkUp))
+ {
+ JerkBrakeUp = 0.01 * jerkUp;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.JerkBrakeUp, NumberRange.NonZero, out jerkDown))
+ {
+ JerkBrakeDown = 0.01 * jerkDown;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.BrakeCylinderUp, NumberRange.Positive, out double bcUp))
+ {
+ BrakeCylinderUp = 1000.0 * bcUp;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.BrakeCylinderDown, NumberRange.Positive, out double bcDown))
+ {
+ BrakeCylinderDown = 1000.0 * bcDown;
+ }
+ break;
+ case TrainDatSection.Brake:
+ subBlock.GetNextEnumValue(TrainDatKey.BrakeSystemType, out trainBrakeType);
+ subBlock.GetNextEnumValue(TrainDatKey.ElectropneumaticType, out ElectropneumaticType);
+ if (subBlock.GetNextDouble(TrainDatKey.BrakeControlSpeed, NumberRange.Positive, out double controlSpeed))
+ {
+ if (controlSpeed > 0 && trainBrakeType == BrakeSystemType.AutomaticAirBrake)
+ {
+ Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "BrakeControlSpeed will be ignored due to the current brake setup in " + FileName);
+ }
+ else
+ {
+ BrakeControlSpeed = controlSpeed * 0.277777777777778; //Convert to m/s
+ }
+ }
+ subBlock.GetNextInt(TrainDatKey.LocoBrakeType, NumberRange.NonNegative, out locoBrakeType);
+ switch (locoBrakeType)
+ {
+ case 0:
+ //Not fitted
+ break;
+ case 1:
+ //Notched air brake
+ Train.Handles.HasLocoBrake = true;
+ locomotiveBrakeType = BrakeSystemType.ElectromagneticStraightAirBrake;
+ break;
+ case 2:
+ //Automatic air brake
+ Train.Handles.HasLocoBrake = true;
+ locomotiveBrakeType = BrakeSystemType.AutomaticAirBrake;
+ break;
+ }
+ break;
+ case TrainDatSection.Pressure:
+ if (subBlock.GetNextDouble(TrainDatKey.BrakeCylinderServiceMaximumPressure, NumberRange.Positive, out double pressure))
+ {
+ BrakeCylinderServiceMaximumPressure = pressure * 1000.0;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.BrakeCylinderEmergencyMaximumPressure, NumberRange.Positive, out pressure))
+ {
+ BrakeCylinderEmergencyMaximumPressure = pressure * 1000.0;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.MainReservoirMinimumPressure, NumberRange.Positive, out pressure))
+ {
+ MainReservoirMinimumPressure = pressure * 1000.0;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.MainReservoirMaximumPressure, NumberRange.Positive, out pressure))
+ {
+ MainReservoirMaximumPressure = pressure * 1000.0;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.BrakePipePressure, NumberRange.Positive, out pressure))
+ {
+ BrakePipePressure = pressure * 1000.0;
+ }
+ break;
+ case TrainDatSection.Handle:
+ subBlock.GetNextEnumValue(TrainDatKey.HandleType, out Train.Handles.HandleType);
+ if (subBlock.GetNextInt(TrainDatKey.NumberOfPowerNotches, NumberRange.Positive, out int power))
+ {
+ powerNotches = power;
+ }
+ if (subBlock.GetNextInt(TrainDatKey.NumberOfBrakeNotches, NumberRange.NonNegative, out int brake))
+ {
+ if (brake == 0)
+ {
+ brake = 8;
+ if (trainBrakeType != BrakeSystemType.AutomaticAirBrake)
+ {
+ Plugin.CurrentHost.AddMessage(MessageType.Error, false, "NumberOfBrakeNotches is expected to be positive and non-zero in " + FileName);
}
- string t = Lines[i] + ","; int m = 0;
- while (true) {
- int j = t.IndexOf(',');
- if (j == -1) break;
- string s = t.Substring(0, j).Trim();
- t = t.Substring(j + 1);
- if (NumberFormats.TryParseDoubleVb6(s, out var a)) {
- switch (m) {
- case 0:
- Tables[msi].Entries[n].SoundIndex = (int)Math.Round(a);
- break;
- case 1:
- if (a < 0.0) a = 0.0;
- Tables[msi].Entries[n].Pitch = (float)(0.01 * a);
- break;
- case 2:
- if (a < 0.0) a = 0.0;
- Tables[msi].Entries[n].Gain = (float)Math.Pow((0.0078125 * a), 0.25);
- break;
- }
- } m++;
- } i++; n++;
}
- if (n != 0)
+ brakeNotches = brake;
+ }
+ if (subBlock.GetNextInt(TrainDatKey.PowerReduceSteps, NumberRange.NonNegative, out int steps))
+ {
+ powerReduceSteps = steps;
+ }
+ subBlock.GetNextEnumValue(TrainDatKey.EbHandleBehaviour, out Train.Handles.EmergencyBrake.OtherHandlesBehaviour);
+ if (subBlock.GetNextInt(TrainDatKey.LocoBrakeNotches, NumberRange.NonNegative, out int locoBrake))
+ {
+ locoBrakeNotches = locoBrake;
+ }
+ subBlock.GetNextEnumValue(TrainDatKey.LocoBrakeType, out Train.Handles.LocoBrakeType);
+ if (datFile.Format == TrainDatFormats.openBVE && datFile.Version >= 15311)
+ {
+ if (subBlock.GetNextInt(TrainDatKey.DriverPowerNotches, NumberRange.Positive, out power))
+ {
+ driverPowerNotches = power;
+ }
+ if (subBlock.GetNextInt(TrainDatKey.DriverBrakeNotches, NumberRange.Positive, out brake))
{
- /*
- * Handle duplicated section header:
- * If no entries, don't resize
- */
- Array.Resize(ref Tables[msi].Entries, n);
+ driverBrakeNotches = brake;
}
- i--;
- } break;
+ }
+ break;
+ case TrainDatSection.Cab:
+ subBlock.GetNextDouble(TrainDatKey.DriverX, NumberRange.Any, out Driver.X);
+ subBlock.GetNextDouble(TrainDatKey.DriverY, NumberRange.Any, out Driver.Y);
+ subBlock.GetNextDouble(TrainDatKey.DriverZ, NumberRange.Any, out Driver.Z);
+ Driver *= 0.001;
+ break;
+ case TrainDatSection.Car:
+ if (subBlock.GetNextDouble(TrainDatKey.MotorCarMass, NumberRange.Positive, out double mass))
+ {
+ MotorCarMass = mass * 1000.0;
+ }
+ if (subBlock.GetNextInt(TrainDatKey.NumberOfMotorCars, NumberRange.Positive, out int cars))
+ {
+ MotorCars = cars;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.TrailerCarMass, NumberRange.Positive, out mass))
+ {
+ TrailerCarMass = mass * 1000.0;
+ }
+ if (subBlock.GetNextInt(TrainDatKey.NumberOfTrailerCars, NumberRange.NonNegative, out cars))
+ {
+ TrailerCars = cars;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.CarLength, NumberRange.Positive, out double length))
+ {
+ CarLength = length;
+ }
+ if (subBlock.GetNextBool(TrainDatKey.FrontCarIsMotorCar, out bool frontMotor))
+ {
+ FrontCarIsMotorCar = frontMotor;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.WidthOfCar, NumberRange.Positive, out double width))
+ {
+ CarWidth = width;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.HeightOfCar, NumberRange.Positive, out double height))
+ {
+ CarHeight = height;
+ }
+ CarExposedFrontalArea = 0.65 * CarWidth * CarHeight;
+ CarUnexposedFrontalArea = 0.2 * CarWidth * CarHeight;
+
+ if (subBlock.GetNextDouble(TrainDatKey.ExposedFrontalArea, NumberRange.Positive, out double area))
+ {
+ CarExposedFrontalArea = area;
+ CarUnexposedFrontalArea = 0.2 * CarWidth * CarHeight;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.UnexposedFrontalArea, NumberRange.Positive, out area))
+ {
+ CarUnexposedFrontalArea = area;
+ }
+ break;
+ case TrainDatSection.Device:
+ subBlock.GetNextInt(TrainDatKey.DefaultSafetySystems, NumberRange.NonNegative, out int atsType);
+ switch (atsType)
+ {
+ case 0:
+ Train.Specs.DefaultSafetySystems |= DefaultSafetySystems.AtsSn;
+ break;
+ case 1:
+ Train.Specs.DefaultSafetySystems |= DefaultSafetySystems.AtsSn;
+ Train.Specs.DefaultSafetySystems |= DefaultSafetySystems.AtsP;
+ break;
+ }
+
+ subBlock.GetNextInt(TrainDatKey.DefaultSafetySystems, NumberRange.NonNegative, out int atcType);
+ switch (atcType)
+ {
+ case 1:
+ case 2:
+ Train.Specs.DefaultSafetySystems |= DefaultSafetySystems.Atc;
+ break;
+ }
+
+ subBlock.GetNextBool(TrainDatKey.DefaultSafetySystems, out bool hasEb);
+ if (hasEb)
+ {
+ Train.Specs.DefaultSafetySystems |= DefaultSafetySystems.Eb;
+ }
+
+ subBlock.GetNextBool(TrainDatKey.DefaultSafetySystems, out Train.Specs.HasConstSpeed);
+ subBlock.GetNextBool(TrainDatKey.DefaultSafetySystems, out Train.Handles.HasHoldBrake);
+ subBlock.GetNextEnumValue(TrainDatKey.PassAlarm, out passAlarm);
+ subBlock.GetNextEnumValue(TrainDatKey.DoorOpenMode, out Train.Specs.DoorOpenMode);
+ subBlock.GetNextEnumValue(TrainDatKey.DoorCloseMode, out Train.Specs.DoorCloseMode);
+ if (subBlock.GetNextDouble(TrainDatKey.DoorWidth, NumberRange.Positive, out width))
+ {
+ DoorWidth = width;
+ }
+ if (subBlock.GetNextDouble(TrainDatKey.DoorMaxTolerance, NumberRange.Positive, out double tolerance))
+ {
+ DoorTolerance = tolerance;
+ }
+ break;
+ case TrainDatSection.Motor_P1:
+ case TrainDatSection.Motor_P2:
+ case TrainDatSection.Motor_B1:
+ case TrainDatSection.Motor_B2:
+ if (subBlock.RemainingDataValues == 0)
+ {
+ // duplicated section header
+ break;
+ }
+ int msi = 0;
+ switch (subBlock.Key)
+ {
+ case TrainDatSection.Motor_P1: msi = BVEMotorSound.MotorP1; break;
+ case TrainDatSection.Motor_P2: msi = BVEMotorSound.MotorP2; break;
+ case TrainDatSection.Motor_B1: msi = BVEMotorSound.MotorB1; break;
+ case TrainDatSection.Motor_B2: msi = BVEMotorSound.MotorB2; break;
+ }
+ Array.Resize(ref Tables[msi].Entries, subBlock.RemainingDataValues);
+ for (int i = 0; i < Tables[msi].Entries.Length; i++)
+ {
+ subBlock.GetNextDoubleArray(',', out double[] entry);
+ Tables[msi].Entries[i].SoundIndex = (int)Math.Round(entry[0]);
+
+ if (entry[1] < 0.0) entry[1] = 0.0;
+ Tables[msi].Entries[i].Pitch = (float)(0.01 * entry[1]);
+ if (entry[2] < 0.0) entry[2] = 0.0;
+ Tables[msi].Entries[i].Gain = (float)Math.Pow((0.0078125 * entry[2]), 0.25);
+ }
+ break;
}
+ Plugin.CurrentProgress = Plugin.LastProgress + invfac * (totalBlocks - datFile.RemainingSubBlocks);
}
-
- if (TrailerCars > 0 & TrailerCarMass <= 0.0) {
- if (currentFormat < TrainDatFormats.openBVE && Plugin.CurrentOptions.EnableBveTsHacks && TrailerCars == 1 && TrailerCarMass == 0)
+
+
+ if (TrailerCars > 0 && TrailerCarMass == 0.0) {
+ if (datFile.Format < TrainDatFormats.openBVE && Plugin.CurrentOptions.EnableBveTsHacks && TrailerCars == 1 && TrailerCarMass == 0)
{
/*
* Early BVE train editor versions appear to have been unable to create a train with no trailer cars,
@@ -1020,7 +553,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
}
else
{
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "TrailerCarMass is expected to be positive in " + FileName);
+ Plugin.CurrentHost.AddMessage(MessageType.Error, false, "TrailerCarMass is expected to be non-zero in " + FileName);
TrailerCarMass = 1.0;
}
@@ -1038,7 +571,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
}
if (driverPowerNotches == 0)
{
- if (currentFormat == TrainDatFormats.openBVE && myVersion >= 15311)
+ if (datFile.Format == TrainDatFormats.openBVE && datFile.Version >= 15311)
{
Plugin.CurrentHost.AddMessage(MessageType.Error, false, "NumberOfDriverPowerNotches was not set in " + FileName);
}
@@ -1046,7 +579,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
}
if (driverBrakeNotches == 0)
{
- if (currentFormat == TrainDatFormats.openBVE && myVersion >= 15311)
+ if (datFile.Format == TrainDatFormats.openBVE && datFile.Version >= 15311)
{
Plugin.CurrentHost.AddMessage(MessageType.Error, false, "NumberOfDriverBrakeNotches was not set in " + FileName);
}
@@ -1080,8 +613,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
Train.Handles.LocoBrakeType = (LocoBrakeType)locoBrakeType;
Train.Handles.HoldBrake = new HoldBrakeHandle(Train);
// apply data
- if (MotorCars < 1) MotorCars = 1;
- if (TrailerCars < 0) TrailerCars = 0;
+ MotorCars = Math.Max(MotorCars, 1);
int Cars = MotorCars + TrailerCars;
Train.Cars = new CarBase[Cars];
for (int i = 0; i < Train.Cars.Length; i++)
@@ -1090,7 +622,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
}
double DistanceBetweenTheCars = 0.3;
- if (DriverCar < 0 | DriverCar >= Cars) {
+ if (DriverCar >= Cars) {
Plugin.CurrentHost.AddMessage(MessageType.Error, false, "DriverCar must point to an existing car in " + FileName);
DriverCar = 0;
@@ -1151,7 +683,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
errors = true;
}
if (errors) {
- Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Entry " + (i + 1).ToString(Culture) + " in the #ACCELERATION section is missing or invalid in " + FileName);
+ Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Entry " + (i + 1) + " in the #ACCELERATION section is missing or invalid in " + FileName);
}
if (AccelerationCurves[i].MaximumAcceleration > MaximumAcceleration) {
MaximumAcceleration = AccelerationCurves[i].MaximumAcceleration;
@@ -1259,8 +791,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
Train.Cars[i].CarBrake.MainReservoir = new MainReservoir(MainReservoirMinimumPressure, MainReservoirMaximumPressure, 0.01, (trainBrakeType == BrakeSystemType.AutomaticAirBrake ? 0.25 : 0.075) / Cars);
Train.Cars[i].CarBrake.MainReservoir.Volume = 0.5; // Organization for Co-Operation between Railways specifies 340L to 680L main reservoir capacity for EMU, so let's pick something in the middle (in m³)
- Train.Cars[i].CarBrake.EqualizingReservoir = new EqualizingReservoir(50000.0, 250000.0, 200000.0);
- Train.Cars[i].CarBrake.EqualizingReservoir.NormalPressure = 1.005 * OperatingPressure;
+ Train.Cars[i].CarBrake.EqualizingReservoir = new EqualizingReservoir(50000.0, 250000.0, 200000.0, 1.005 * OperatingPressure);
Train.Cars[i].CarBrake.EqualizingReservoir.Volume = 0.015; // very small reservoir for observation, so guess at 15L
Train.Cars[i].CarBrake.BrakePipe = new BrakePipe(OperatingPressure, 10000000.0, 1500000.0, 5000000.0, trainBrakeType == BrakeSystemType.ElectricCommandBrake);
@@ -1303,59 +834,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
Train.SafetySystems.PilotLamp = new PilotLamp(Train.Cars[DriverCar]);
Train.SafetySystems.StationAdjust = new StationAdjustAlarm(Train);
Train.SafetySystems.Headlights = new LightSource(Train, 1);
- switch (Plugin.CurrentOptions.TrainStart)
- {
- // starting mode
- case TrainStartMode.ServiceBrakesAts:
- if (trainBrakeType == BrakeSystemType.AutomaticAirBrake)
- {
- Train.Handles.Brake.Driver = (int)AirBrakeHandleState.Service;
- Train.Handles.Brake.Actual = (int)AirBrakeHandleState.Service;
- }
- else
- {
- int notch = (int)Math.Round(0.7 * Train.Handles.Brake.MaximumNotch);
- Train.Handles.Brake.Driver = notch;
- Train.Handles.Brake.Actual = notch;
- }
- Train.Handles.EmergencyBrake.Driver = false;
- Train.Handles.EmergencyBrake.Safety = false;
- Train.Handles.EmergencyBrake.Actual = false;
- Train.Handles.Reverser.Driver = ReverserPosition.Forwards;
- Train.Handles.Reverser.Actual = ReverserPosition.Forwards;
- break;
- case TrainStartMode.EmergencyBrakesAts:
- if (trainBrakeType == BrakeSystemType.AutomaticAirBrake)
- {
- Train.Handles.Brake.Driver = (int)AirBrakeHandleState.Service;
- Train.Handles.Brake.Actual = (int)AirBrakeHandleState.Service;
- }
- else
- {
- Train.Handles.Brake.Driver = Train.Handles.Brake.MaximumNotch;
- Train.Handles.Brake.Actual = Train.Handles.Brake.MaximumNotch;
- }
-
- Train.Handles.EmergencyBrake.Driver = true;
- Train.Handles.EmergencyBrake.Safety = true;
- Train.Handles.EmergencyBrake.Actual = true;
- break;
- default:
- if (trainBrakeType == BrakeSystemType.AutomaticAirBrake)
- {
- Train.Handles.Brake.Driver = (int)AirBrakeHandleState.Service;
- Train.Handles.Brake.Actual = (int)AirBrakeHandleState.Service;
- }
- else
- {
- Train.Handles.Brake.Driver = Train.Handles.Brake.MaximumNotch;
- Train.Handles.Brake.Actual = Train.Handles.Brake.MaximumNotch;
- }
- Train.Handles.EmergencyBrake.Driver = true;
- Train.Handles.EmergencyBrake.Safety = true;
- Train.Handles.EmergencyBrake.Actual = true;
- break;
- }
+ Train.Handles.Setup(Plugin.CurrentOptions.TrainStart);
// apply other attributes for all cars
double AxleDistance = 0.4 * CarLength;
for (int i = 0; i < Cars; i++) {
@@ -1393,14 +872,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) {
Train.Cars[i].Specs.CriticalTopplingAngle = 0.5 * Math.PI - Math.Atan(2 * Train.Cars[i].Specs.CenterOfGravityHeight / Train.Cars[i].Width);
}
- if (Cars == 1)
- {
- Train.Cars[Train.Cars.Length - 1].BeaconReceiver.TriggerType = EventTriggerType.SingleCarTrain;
- }
- else
- {
- Train.Cars[Train.Cars.Length - 1].BeaconReceiver.TriggerType = EventTriggerType.TrainRear;
- }
+ Train.Cars[Train.Cars.Length - 1].BeaconReceiver.TriggerType = Cars == 1 ? EventTriggerType.SingleCarTrain : EventTriggerType.TrainRear;
Plugin.MotorSoundTables = Tables;
diff --git a/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.BrakeNode.cs b/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.BrakeNode.cs
index 5a195a2b28..c97fb4ce45 100644
--- a/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.BrakeNode.cs
+++ b/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.BrakeNode.cs
@@ -112,8 +112,7 @@ private void ParseBrakeBlock(Block block, int carI
AirBrake airBrake = Train.Cars[carIndex].CarBrake as AirBrake;
airBrake.Compressor = new Compressor(compressorRate, Train.Cars[carIndex].CarBrake.MainReservoir, Train.Cars[carIndex]);
airBrake.StraightAirPipe = new StraightAirPipe(straightAirPipeServiceRate, straightAirPipeEmergencyRate, straightAirPipeReleaseRate);
- Train.Cars[carIndex].CarBrake.EqualizingReservoir = new EqualizingReservoir(equalizingReservoirServiceRate, equalizingReservoirEmergencyRate, equalizingReservoirChargeRate);
- Train.Cars[carIndex].CarBrake.EqualizingReservoir.NormalPressure = 1.005 * brakePipeNormalPressure;
+ Train.Cars[carIndex].CarBrake.EqualizingReservoir = new EqualizingReservoir(equalizingReservoirServiceRate, equalizingReservoirEmergencyRate, equalizingReservoirChargeRate, 1.005 * brakePipeNormalPressure);
Train.Cars[carIndex].CarBrake.EqualizingReservoir.Volume = equalizingReservoirVolume;
Train.Cars[carIndex].CarBrake.BrakePipe = new BrakePipe(brakePipeNormalPressure, brakePipeChargeRate, brakePipeServiceRate, brakePipeEmergencyRate, Train.Cars[0].CarBrake is ElectricCommandBrake);
diff --git a/source/TrainManager/Brake/AirBrake/Components/Reservoirs.cs b/source/TrainManager/Brake/AirBrake/Components/Reservoirs.cs
index bd119f63ca..c108da2a68 100644
--- a/source/TrainManager/Brake/AirBrake/Components/Reservoirs.cs
+++ b/source/TrainManager/Brake/AirBrake/Components/Reservoirs.cs
@@ -26,13 +26,14 @@ public class EqualizingReservoir : AbstractReservoir
/// The rate when EB brakes are applied in Pa/s
internal readonly double EmergencyRate;
/// The normal pressure
- public double NormalPressure;
+ public readonly double NormalPressure;
/// The internal volume of the reservoir
- public EqualizingReservoir(double serviceRate, double emergencyRate, double chargeRate) : base(chargeRate, 0.0)
+ public EqualizingReservoir(double serviceRate, double emergencyRate, double chargeRate, double normalPressure) : base(chargeRate, 0.0)
{
ServiceRate = serviceRate;
EmergencyRate = emergencyRate;
+ NormalPressure = normalPressure;
}
/// Creates a dummy equalizing reservoir
diff --git a/source/TrainManager/Handles/CabHandles.cs b/source/TrainManager/Handles/CabHandles.cs
index 64c4675c33..1da127cbf4 100644
--- a/source/TrainManager/Handles/CabHandles.cs
+++ b/source/TrainManager/Handles/CabHandles.cs
@@ -1,4 +1,7 @@
using OpenBveApi.Interface;
+using OpenBveApi.Trains;
+using System;
+using TrainManager.BrakeSystems;
namespace TrainManager.Handles
{
@@ -26,6 +29,66 @@ public struct CabHandles
/// The loco brake type
public LocoBrakeType LocoBrakeType;
+ /// Called to setup the initial handle states
+ /// The selected train start mode
+ public void Setup(TrainStartMode trainStart)
+ {
+ switch (trainStart)
+ {
+ // starting mode
+ case TrainStartMode.ServiceBrakesAts:
+ if (Brake.baseTrain.Cars[Brake.baseTrain.DriverCar].CarBrake is AutomaticAirBrake)
+ {
+ Brake.Driver = (int)AirBrakeHandleState.Service;
+ Brake.Actual = (int)AirBrakeHandleState.Service;
+ }
+ else
+ {
+ int notch = (int)Math.Round(0.7 * Brake.MaximumNotch);
+ Brake.Driver = notch;
+ Brake.Actual = notch;
+ }
+ EmergencyBrake.Driver = false;
+ EmergencyBrake.Safety = false;
+ EmergencyBrake.Actual = false;
+ Reverser.Driver = ReverserPosition.Forwards;
+ Reverser.Actual = ReverserPosition.Forwards;
+ break;
+ case TrainStartMode.EmergencyBrakesAts:
+ if (Brake.baseTrain.Cars[Brake.baseTrain.DriverCar].CarBrake is AutomaticAirBrake)
+ {
+ Brake.Driver = (int)AirBrakeHandleState.Service;
+ Brake.Actual = (int)AirBrakeHandleState.Service;
+ }
+ else
+ {
+ Brake.Driver = Brake.MaximumNotch;
+ Brake.Actual = Brake.MaximumNotch;
+ }
+
+ EmergencyBrake.Driver = true;
+ EmergencyBrake.Safety = true;
+ EmergencyBrake.Actual = true;
+ break;
+ default:
+ if (Brake.baseTrain.Cars[Brake.baseTrain.DriverCar].CarBrake is AutomaticAirBrake)
+ {
+ Brake.Driver = (int)AirBrakeHandleState.Service;
+ Brake.Actual = (int)AirBrakeHandleState.Service;
+ }
+ else
+ {
+ Brake.Driver = Brake.MaximumNotch;
+ Brake.Actual = Brake.MaximumNotch;
+ }
+ EmergencyBrake.Driver = true;
+ EmergencyBrake.Safety = true;
+ EmergencyBrake.Actual = true;
+ break;
+ }
+ }
+
+ /// Called when a Control is pressed
public void ControlDown(Control Control)
{
switch (Control.Command)
@@ -369,6 +432,7 @@ public void ControlDown(Control Control)
}
}
+ /// Called when a control is released
public void ControlUp(Control Control)
{
switch (Control.Command)
diff --git a/source/TrainManager/SafetySystems/Plugin/AI/PluginAI.UKDt.cs b/source/TrainManager/SafetySystems/Plugin/AI/PluginAI.UKDt.cs
index 0f4a2d0991..1bfca4910d 100644
--- a/source/TrainManager/SafetySystems/Plugin/AI/PluginAI.UKDt.cs
+++ b/source/TrainManager/SafetySystems/Plugin/AI/PluginAI.UKDt.cs
@@ -116,10 +116,10 @@ internal override void Perform(AIData data)
currentStep = 5;
}
- if (Plugin.Panel[51] == 1)
+ if (Plugin.Panel[51] == 1 || Plugin.Panel[55] == 1)
{
/*
- * Over current has tripped
+ * Over current has tripped, or wheelslip light is lit
* Let's back off to N and drop the max notch by 1
*
* Repeat until we move off properly
@@ -133,6 +133,7 @@ internal override void Perform(AIData data)
data.Handles.PowerNotch = 0;
data.Response = AIResponse.Long;
overCurrentTrip = true;
+ nextPluginAction = TrainManagerBase.currentHost.InGameTime + 20;
return;
}
data.Response = AIResponse.Long;
@@ -142,9 +143,10 @@ internal override void Perform(AIData data)
overCurrentTrip = false;
if (overCurrentSpeed != double.MaxValue)
{
- if (Plugin.Train.CurrentSpeed < overCurrentSpeed + 10)
+ if (Plugin.Train.CurrentSpeed > 5 && Plugin.Train.CurrentSpeed < overCurrentSpeed + 10)
{
data.Handles.PowerNotch = overCurrentNotch;
+ data.Response = AIResponse.Short;
}
else
{