diff --git a/makefile b/makefile
index df03f3bd27..5651e19664 100644
--- a/makefile
+++ b/makefile
@@ -268,7 +268,7 @@ create_resource_tmp = $(eval $(call resource_rule_impl, $(firstword $(subst ^, ,
# OpenBve #
###########
-OPEN_BVE_FOLDERS := . Audio Game Graphics Graphics/Renderer Interface OldCode OldCode/NewCode Parsers Properties OldParsers OldParsers/BveRouteParser Simulation/TrainManager Simulation/TrainManager/Train Simulation/World System System/Functions System/Input System/Logging System/Program System/Translations UserInterface
+OPEN_BVE_FOLDERS := . Audio Game Graphics Graphics/Renderer Interface OldCode OldCode/NewCode Parsers Properties OldParsers OldParsers/BveRouteParser Simulation/TrainManager Simulation/TrainManager/Train Simulation/TrainPlugins Simulation/World System System/Functions System/Input System/Logging System/Program System/Translations UserInterface
OPEN_BVE_FOLDERS := $(addprefix $(OPEN_BVE_ROOT)/, $(OPEN_BVE_FOLDERS))
OPEN_BVE_SRC := $(filter-out "$(OPEN_BVE_ROOT)/Properties/AssemblyInfo.cs",$(patsubst %, "%", $(foreach sdir, $(OPEN_BVE_FOLDERS), $(wildcard $(sdir)/*.cs))))
OPEN_BVE_DOC := $(addprefix /doc:, $(foreach sdir, $(OPEN_BVE_FOLDERS), $(wildcard $(sdir)/*.xml)))
diff --git a/source/OpenBVE/Game/Messages.cs b/source/OpenBVE/Game/Messages.cs
index d4ab3bc363..6f5b3a3e72 100644
--- a/source/OpenBVE/Game/Messages.cs
+++ b/source/OpenBVE/Game/Messages.cs
@@ -36,6 +36,17 @@ internal struct Message
internal Vector2 RendererPosition;
/// The level of alpha used by the renderer whilst fading out the message
internal double RendererAlpha;
+
+ internal Message(string Text, MessageDependency Depencency, MessageColor Color, double Timeout)
+ {
+ this.InternalText = Text;
+ this.Depencency = Depencency;
+ this.Color = Color;
+ this.Timeout = Timeout;
+ DisplayText = "";
+ RendererPosition = new Vector2(0.0, 0.0);
+ RendererAlpha = 0.0;
+ }
}
/// The current in-game messages
@@ -71,11 +82,22 @@ internal static void AddMessage(string Text, MessageDependency Depencency, Inter
Messages[n].RendererAlpha = 0.0;
}
}
+
+ internal static void AddMessage(Message Message)
+ {
+ int n = Messages.Length;
+ Array.Resize(ref Messages, n + 1);
+ Messages[n] = Message;
+ Messages[n].Timeout += Game.SecondsSinceMidnight;
+ }
+
internal static void AddDebugMessage(string text, double duration)
{
Game.AddMessage(text, Game.MessageDependency.None, Interface.GameMode.Expert, MessageColor.Magenta, Game.SecondsSinceMidnight + duration);
}
+
+
/// The number 1km/h must be multiplied by to produce your desired speed units, or 0.0 to disable this
internal static double SpeedConversionFactor = 0.0;
/// The unit of speed displayed in in-game messages
diff --git a/source/OpenBVE/OldCode/Loading.cs b/source/OpenBVE/OldCode/Loading.cs
index 225ca30bcd..5b4c0a9307 100644
--- a/source/OpenBVE/OldCode/Loading.cs
+++ b/source/OpenBVE/OldCode/Loading.cs
@@ -1,7 +1,9 @@
using System;
+using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;
+using OpenBveApi.Colors;
using OpenBveApi.Math;
namespace OpenBve {
@@ -29,8 +31,10 @@ internal static class Loading {
private static Encoding CurrentTrainEncoding;
internal static double TrainProgressCurrentSum;
internal static double TrainProgressCurrentWeight;
- /// Stores the plugin error message string, or a null reference if no error encountered
- internal static string PluginError;
+ /// The queue of messages to be displayed once the game has loaded
+ internal static List MessageQueue = new List();
+ /// The color to be used for the plugin message
+ internal static MessageColor PluginMessageColor = MessageColor.Red;
// load
/// Initializes loading the route and train asynchronously. Set the Loading.Cancel member to cancel loading. Check the Loading.Complete member to see when loading has finished.
diff --git a/source/OpenBVE/OldCode/NewCode/GameWindow.cs b/source/OpenBVE/OldCode/NewCode/GameWindow.cs
index 295787a211..010757cc93 100644
--- a/source/OpenBVE/OldCode/NewCode/GameWindow.cs
+++ b/source/OpenBVE/OldCode/NewCode/GameWindow.cs
@@ -5,6 +5,7 @@
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
+using OpenBveApi.Colors;
using OpenTK;
using OpenTK.Graphics;
using GL = OpenTK.Graphics.OpenGL.GL;
@@ -665,10 +666,12 @@ private void SetupSimulation()
Game.RouteInformation.ErrorsAndWarnings = Messages;
//Print the plugin error encountered (If any) for 10s
//This must be done after the simulation has init, as otherwise the timeout doesn't work
- if (Loading.PluginError != null)
+ if (Loading.MessageQueue.Count > 0)
{
- Game.AddMessage(Loading.PluginError, Game.MessageDependency.None, Interface.GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Game.SecondsSinceMidnight + 5.0);
- Game.AddMessage(Interface.GetInterfaceString("errors_plugin_failure2"), Game.MessageDependency.None, Interface.GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Game.SecondsSinceMidnight + 5.0);
+ foreach (var message in Loading.MessageQueue)
+ {
+ Game.AddMessage(message);
+ }
}
}
loadComplete = true;
diff --git a/source/OpenBVE/OldCode/PluginManager.cs b/source/OpenBVE/OldCode/PluginManager.cs
deleted file mode 100644
index 8eb8f61d47..0000000000
--- a/source/OpenBVE/OldCode/PluginManager.cs
+++ /dev/null
@@ -1,653 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using OpenBveApi.Colors;
-using OpenBveApi.Runtime;
-
-namespace OpenBve {
- internal static partial class PluginManager {
-
- /// Represents an abstract plugin.
- internal abstract class Plugin {
-
- // --- members ---
- /// The file title of the plugin, including the file extension.
- internal string PluginTitle;
- /// Whether the plugin is the default ATS/ATC plugin.
- internal bool IsDefault;
- /// Whether the plugin returned valid information in the last Elapse call.
- internal bool PluginValid;
- /// The debug message the plugin returned in the last Elapse call.
- internal string PluginMessage;
- /// The train the plugin is attached to.
- internal TrainManager.Train Train;
- /// The array of panel variables.
- internal int[] Panel;
- /// Whether the plugin supports the AI.
- internal bool SupportsAI;
- /// The last in-game time reported to the plugin.
- internal double LastTime;
- /// The last reverser reported to the plugin.
- internal int LastReverser;
- /// The last power notch reported to the plugin.
- internal int LastPowerNotch;
- /// The last brake notch reported to the plugin.
- internal int LastBrakeNotch;
- /// The last aspects per relative section reported to the plugin. Section 0 is the current section, section 1 the upcoming section, and so on.
- internal int[] LastAspects;
- /// The absolute section the train was last.
- internal int LastSection;
- /// The last exception the plugin raised.
- internal Exception LastException;
- //NEW: Whether this plugin can disable the time acceleration factor
- /// Whether this plugin can disable time acceleration.
- internal static bool DisableTimeAcceleration;
- /// The current camera view mode
- internal OpenBveApi.Runtime.CameraViewMode CurrentCameraViewMode;
-
- private List currentRouteStations;
- internal bool StationsLoaded;
- // --- functions ---
- /// Called to load and initialize the plugin.
- /// The train specifications.
- /// The initialization mode of the train.
- /// Whether loading the plugin was successful.
- internal abstract bool Load(VehicleSpecs specs, InitializationModes mode);
- /// Called to unload the plugin.
- internal abstract void Unload();
- /// Called before the train jumps to a different location.
- /// The initialization mode of the train.
- internal abstract void BeginJump(InitializationModes mode);
- /// Called when the train has finished jumping to a different location.
- internal abstract void EndJump();
- /// Called every frame to update the plugin.
- internal void UpdatePlugin() {
- /*
- * Prepare the vehicle state.
- * */
- double location = this.Train.Cars[0].FrontAxle.Follower.TrackPosition - this.Train.Cars[0].FrontAxlePosition + 0.5 * this.Train.Cars[0].Length;
- //Curve Radius, Cant and Pitch Added
- double CurrentRadius = this.Train.Cars[0].FrontAxle.Follower.CurveRadius;
- double CurrentCant = this.Train.Cars[0].FrontAxle.Follower.CurveCant;
- double CurrentPitch = this.Train.Cars[0].FrontAxle.Follower.Pitch;
- //If the list of stations has not been loaded, do so
- if (!StationsLoaded)
- {
- currentRouteStations = new List();
- foreach (Game.Station selectedStation in Game.Stations)
- {
- Station i = new Station
- {
- Name = selectedStation.Name,
- ArrivalTime = selectedStation.ArrivalTime,
- DepartureTime = selectedStation.DepartureTime,
- StopTime = selectedStation.StopTime,
- OpenLeftDoors = selectedStation.OpenLeftDoors,
- OpenRightDoors = selectedStation.OpenRightDoors,
- ForceStopSignal = selectedStation.ForceStopSignal,
- DefaultTrackPosition = selectedStation.DefaultTrackPosition
- };
- currentRouteStations.Add(i);
- }
- StationsLoaded = true;
- }
- //End of additions
- double speed = this.Train.Cars[this.Train.DriverCar].Specs.CurrentPerceivedSpeed;
- double bcPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.BrakeCylinderCurrentPressure;
- double mrPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.MainReservoirCurrentPressure;
- double erPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.EqualizingReservoirCurrentPressure;
- double bpPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.BrakePipeCurrentPressure;
- double sapPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.StraightAirPipeCurrentPressure;
- VehicleState vehicle = new VehicleState(location, new Speed(speed), bcPressure, mrPressure, erPressure, bpPressure, sapPressure, CurrentRadius, CurrentCant, CurrentPitch);
- /*
- * Prepare the preceding vehicle state.
- * */
- double bestLocation = double.MaxValue;
- double bestSpeed = 0.0;
- for (int i = 0; i < TrainManager.Trains.Length; i++) {
- if (TrainManager.Trains[i] != this.Train & TrainManager.Trains[i].State == TrainManager.TrainState.Available) {
- int c = TrainManager.Trains[i].Cars.Length - 1;
- double z = TrainManager.Trains[i].Cars[c].RearAxle.Follower.TrackPosition - TrainManager.Trains[i].Cars[c].RearAxlePosition - 0.5 * TrainManager.Trains[i].Cars[c].Length;
- if (z >= location & z < bestLocation) {
- bestLocation = z;
- bestSpeed = TrainManager.Trains[i].Specs.CurrentAverageSpeed;
- }
- }
- }
- var precedingVehicle = bestLocation != double.MaxValue ? new PrecedingVehicleState(bestLocation, bestLocation - location, new Speed(bestSpeed)) : null;
- /*
- * Get the driver handles.
- * */
- Handles handles = GetHandles();
- /*
- * Update the plugin.
- * */
- double totalTime = Game.SecondsSinceMidnight;
- double elapsedTime = Game.SecondsSinceMidnight - LastTime;
- /*
- * Set the current camera view mode
- * Could probably do away with the CurrentCameraViewMode and use a direct cast??
- *
- */
- CurrentCameraViewMode = (OpenBveApi.Runtime.CameraViewMode)World.CameraMode;
- ElapseData data = new ElapseData(vehicle, precedingVehicle, handles, new Time(totalTime), new Time(elapsedTime), currentRouteStations, CurrentCameraViewMode, Interface.CurrentLanguageCode);
- LastTime = Game.SecondsSinceMidnight;
- Elapse(data);
- this.PluginMessage = data.DebugMessage;
- DisableTimeAcceleration = data.DisableTimeAcceleration;
- /*
- * Set the virtual handles.
- * */
- this.PluginValid = true;
- SetHandles(data.Handles, true);
- }
- /// Gets the driver handles.
- /// The driver handles.
- private Handles GetHandles() {
- int reverser = this.Train.Specs.CurrentReverser.Driver;
- int powerNotch = this.Train.Specs.CurrentPowerNotch.Driver;
- int brakeNotch;
- if (this.Train.Cars[this.Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) {
- brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? 3 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service ? 2 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap ? 1 : 0;
- } else {
- if (this.Train.Specs.HasHoldBrake) {
- brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 2 : this.Train.Specs.CurrentBrakeNotch.Driver > 0 ? this.Train.Specs.CurrentBrakeNotch.Driver + 1 : this.Train.Specs.CurrentHoldBrake.Driver ? 1 : 0;
- } else {
- brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 1 : this.Train.Specs.CurrentBrakeNotch.Driver;
- }
- }
- bool constSpeed = this.Train.Specs.CurrentConstSpeed;
- return new Handles(reverser, powerNotch, brakeNotch, constSpeed);
- }
- /// Sets the driver handles or the virtual handles.
- /// The handles.
- /// Whether to set the virtual handles.
- private void SetHandles(Handles handles, bool virtualHandles) {
- /*
- * Process the handles.
- */
- if (this.Train.Specs.SingleHandle & handles.BrakeNotch != 0) {
- handles.PowerNotch = 0;
- }
- /*
- * Process the reverser.
- */
- if (handles.Reverser >= -1 & handles.Reverser <= 1) {
- if (virtualHandles) {
- this.Train.Specs.CurrentReverser.Actual = handles.Reverser;
- } else {
- TrainManager.ApplyReverser(this.Train, handles.Reverser, false);
- }
- } else {
- if (virtualHandles) {
- this.Train.Specs.CurrentReverser.Actual = this.Train.Specs.CurrentReverser.Driver;
- }
- this.PluginValid = false;
- }
- /*
- * Process the power.
- * */
- if (handles.PowerNotch >= 0 & handles.PowerNotch <= this.Train.Specs.MaximumPowerNotch) {
- if (virtualHandles) {
- this.Train.Specs.CurrentPowerNotch.Safety = handles.PowerNotch;
- } else {
- TrainManager.ApplyNotch(this.Train, handles.PowerNotch, false, 0, true);
- }
- } else {
- if (virtualHandles) {
- this.Train.Specs.CurrentPowerNotch.Safety = this.Train.Specs.CurrentPowerNotch.Driver;
- }
- this.PluginValid = false;
- }
-// if (handles.BrakeNotch != 0) {
-// if (virtualHandles) {
-// this.Train.Specs.CurrentPowerNotch.Safety = 0;
-// }
-// }
- /*
- * Process the brakes.
- * */
- if (virtualHandles) {
- this.Train.Specs.CurrentEmergencyBrake.Safety = false;
- this.Train.Specs.CurrentHoldBrake.Actual = false;
- }
- if (this.Train.Cars[this.Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) {
- if (handles.BrakeNotch == 0) {
- if (virtualHandles) {
- this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Release;
- } else {
- TrainManager.UnapplyEmergencyBrake(this.Train);
- TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Release);
- }
- } else if (handles.BrakeNotch == 1) {
- if (virtualHandles) {
- this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Lap;
- } else {
- TrainManager.UnapplyEmergencyBrake(this.Train);
- TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Lap);
- }
- } else if (handles.BrakeNotch == 2) {
- if (virtualHandles) {
- this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service;
- } else {
- TrainManager.UnapplyEmergencyBrake(this.Train);
- TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Release);
- }
- } else if (handles.BrakeNotch == 3) {
- if (virtualHandles) {
- this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service;
- this.Train.Specs.CurrentEmergencyBrake.Safety = true;
- } else {
- TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Service);
- TrainManager.ApplyEmergencyBrake(this.Train);
- }
- } else {
- this.PluginValid = false;
- }
- } else {
- if (this.Train.Specs.HasHoldBrake) {
- if (handles.BrakeNotch == this.Train.Specs.MaximumBrakeNotch + 2) {
- if (virtualHandles) {
- this.Train.Specs.CurrentEmergencyBrake.Safety = true;
- this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.MaximumBrakeNotch;
- } else {
- TrainManager.ApplyHoldBrake(this.Train, false);
- TrainManager.ApplyNotch(this.Train, 0, true, this.Train.Specs.MaximumBrakeNotch, false);
- TrainManager.ApplyEmergencyBrake(this.Train);
- }
- } else if (handles.BrakeNotch >= 2 & handles.BrakeNotch <= this.Train.Specs.MaximumBrakeNotch + 1) {
- if (virtualHandles) {
- this.Train.Specs.CurrentBrakeNotch.Safety = handles.BrakeNotch - 1;
- } else {
- TrainManager.UnapplyEmergencyBrake(this.Train);
- TrainManager.ApplyHoldBrake(this.Train, false);
- TrainManager.ApplyNotch(this.Train, 0, true, handles.BrakeNotch - 1, false);
- }
- } else if (handles.BrakeNotch == 1) {
- if (virtualHandles) {
- this.Train.Specs.CurrentBrakeNotch.Safety = 0;
- this.Train.Specs.CurrentHoldBrake.Actual = true;
- } else {
- TrainManager.UnapplyEmergencyBrake(this.Train);
- TrainManager.ApplyNotch(this.Train, 0, true, 0, false);
- TrainManager.ApplyHoldBrake(this.Train, true);
- }
- } else if (handles.BrakeNotch == 0) {
- if (virtualHandles) {
- this.Train.Specs.CurrentBrakeNotch.Safety = 0;
- } else {
- TrainManager.UnapplyEmergencyBrake(this.Train);
- TrainManager.ApplyNotch(this.Train, 0, true, 0, false);
- TrainManager.ApplyHoldBrake(this.Train, false);
- }
- } else {
- if (virtualHandles) {
- this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.CurrentBrakeNotch.Driver;
- }
- this.PluginValid = false;
- }
- } else {
- if (handles.BrakeNotch == this.Train.Specs.MaximumBrakeNotch + 1) {
- if (virtualHandles) {
- this.Train.Specs.CurrentEmergencyBrake.Safety = true;
- this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.MaximumBrakeNotch;
- } else {
- TrainManager.ApplyHoldBrake(this.Train, false);
- TrainManager.ApplyEmergencyBrake(this.Train);
- }
- } else if (handles.BrakeNotch >= 0 & handles.BrakeNotch <= this.Train.Specs.MaximumBrakeNotch | this.Train.Specs.CurrentBrakeNotch.DelayedChanges.Length == 0) {
- if (virtualHandles) {
- this.Train.Specs.CurrentBrakeNotch.Safety = handles.BrakeNotch;
- } else {
- TrainManager.UnapplyEmergencyBrake(this.Train);
- TrainManager.ApplyNotch(this.Train, 0, true, handles.BrakeNotch, false);
- }
- } else {
- if (virtualHandles) {
- this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.CurrentBrakeNotch.Driver;
- }
- this.PluginValid = false;
- }
- }
- }
- /*
- * Process the const speed system.
- * */
- this.Train.Specs.CurrentConstSpeed = handles.ConstSpeed & this.Train.Specs.HasConstSpeed;
- }
- /// Called every frame to update the plugin.
- /// The data passed to the plugin on Elapse.
- /// This function should not be called directly. Call UpdatePlugin instead.
- internal abstract void Elapse(ElapseData data);
- /// Called to update the reverser. This invokes a call to SetReverser only if a change actually occured.
- internal void UpdateReverser() {
- int reverser = this.Train.Specs.CurrentReverser.Driver;
- if (reverser != this.LastReverser) {
- this.LastReverser = reverser;
- SetReverser(reverser);
- }
- }
- /// Called to indicate a change of the reverser.
- /// The reverser.
- /// This function should not be called directly. Call UpdateReverser instead.
- internal abstract void SetReverser(int reverser);
- /// Called to update the power notch. This invokes a call to SetPower only if a change actually occured.
- internal void UpdatePower() {
- int powerNotch = this.Train.Specs.CurrentPowerNotch.Driver;
- if (powerNotch != this.LastPowerNotch) {
- this.LastPowerNotch = powerNotch;
- SetPower(powerNotch);
- }
- }
- /// Called to indicate a change of the power notch.
- /// The power notch.
- /// This function should not be called directly. Call UpdatePower instead.
- internal abstract void SetPower(int powerNotch);
- /// Called to update the brake notch. This invokes a call to SetBrake only if a change actually occured.
- internal void UpdateBrake() {
- int brakeNotch;
- if (this.Train.Cars[this.Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) {
- if (this.Train.Specs.HasHoldBrake) {
- brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? 4 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service ? 3 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap ? 2 : this.Train.Specs.CurrentHoldBrake.Driver ? 1 : 0;
- } else {
- brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? 3 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service ? 2 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap ? 1 : 0;
- }
- } else {
- if (this.Train.Specs.HasHoldBrake) {
- brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 2 : this.Train.Specs.CurrentBrakeNotch.Driver > 0 ? this.Train.Specs.CurrentBrakeNotch.Driver + 1 : this.Train.Specs.CurrentHoldBrake.Driver ? 1 : 0;
- } else {
- brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 1 : this.Train.Specs.CurrentBrakeNotch.Driver;
- }
- }
- if (brakeNotch != this.LastBrakeNotch) {
- this.LastBrakeNotch = brakeNotch;
- SetBrake(brakeNotch);
- }
- }
- /// Called to indicate a change of the brake notch.
- /// The brake notch.
- /// This function should not be called directly. Call UpdateBrake instead.
- internal abstract void SetBrake(int brakeNotch);
- /// Called when a virtual key is pressed.
- internal abstract void KeyDown(VirtualKeys key);
- /// Called when a virtual key is released.
- internal abstract void KeyUp(VirtualKeys key);
- /// Called when a horn is played or stopped.
- internal abstract void HornBlow(HornTypes type);
- /// Called when the state of the doors changes.
- internal abstract void DoorChange(DoorStates oldState, DoorStates newState);
- /// Called to update the aspects of the section. This invokes a call to SetSignal only if a change in aspect occured or when changing section boundaries.
- /// The sections to submit to the plugin.
- internal void UpdateSignals(SignalData[] data) {
- if (data.Length != 0) {
- bool update;
- if (this.Train.CurrentSectionIndex != this.LastSection) {
- update = true;
- } else if (data.Length != this.LastAspects.Length) {
- update = true;
- } else {
- update = false;
- for (int i = 0; i < data.Length; i++) {
- if (data[i].Aspect != this.LastAspects[i]) {
- update = true;
- break;
- }
- }
- }
- if (update) {
- SetSignal(data);
- this.LastAspects = new int[data.Length];
- for (int i = 0; i < data.Length; i++) {
- this.LastAspects[i] = data[i].Aspect;
- }
- }
- }
- }
- /// Is called when the aspect in the current or any of the upcoming sections changes.
- /// Signal information per section. In the array, index 0 is the current section, index 1 the upcoming section, and so on.
- /// This function should not be called directly. Call UpdateSignal instead.
- internal abstract void SetSignal(SignalData[] signal);
- /// Called when the train passes a beacon.
- /// The beacon type.
- /// The section the beacon is attached to, or -1 for the next red signal.
- /// Optional data attached to the beacon.
- internal void UpdateBeacon(int type, int sectionIndex, int optional) {
- if (sectionIndex == -1) {
- sectionIndex = this.Train.CurrentSectionIndex + 1;
- SignalData signal = null;
- while (sectionIndex < Game.Sections.Length) {
- signal = Game.GetPluginSignal(this.Train, sectionIndex);
- if (signal.Aspect == 0) break;
- sectionIndex++;
- }
- if (sectionIndex < Game.Sections.Length) {
- SetBeacon(new BeaconData(type, optional, signal));
- } else {
- SetBeacon(new BeaconData(type, optional, new SignalData(-1, double.MaxValue)));
- }
- }
- if (sectionIndex >= 0) {
- SignalData signal;
- if (sectionIndex < Game.Sections.Length) {
- signal = Game.GetPluginSignal(this.Train, sectionIndex);
- } else {
- signal = new SignalData(0, double.MaxValue);
- }
- SetBeacon(new BeaconData(type, optional, signal));
- } else {
- SetBeacon(new BeaconData(type, optional, new SignalData(-1, double.MaxValue)));
- }
- }
- /// Called when the train passes a beacon.
- /// The beacon data.
- /// This function should not be called directly. Call UpdateBeacon instead.
- internal abstract void SetBeacon(BeaconData beacon);
- /// Updates the AI.
- /// The AI response.
- internal AIResponse UpdateAI() {
- if (this.SupportsAI) {
- AIData data = new AIData(GetHandles());
- this.PerformAI(data);
- if (data.Response != AIResponse.None) {
- SetHandles(data.Handles, false);
- }
- return data.Response;
- } else {
- return AIResponse.None;
- }
- }
- /// Called when the AI should be performed.
- /// The AI data.
- /// This function should not be called directly. Call UpdateAI instead.
- internal abstract void PerformAI(AIData data);
-
- }
-
- /// Loads a custom plugin for the specified train.
- /// The train to attach the plugin to.
- /// The absolute path to the train folder.
- /// The encoding to be used.
- /// Whether the plugin was loaded successfully.
- internal static bool LoadCustomPlugin(TrainManager.Train train, string trainFolder, System.Text.Encoding encoding) {
- string config = OpenBveApi.Path.CombineFile(trainFolder, "ats.cfg");
- if (!System.IO.File.Exists(config)) {
- return false;
- }
- string[] lines = System.IO.File.ReadAllLines(config, encoding);
- if (lines.Length == 0) {
- return false;
- }
- string file = OpenBveApi.Path.CombineFile(trainFolder, lines[0]);
- string title = System.IO.Path.GetFileName(file);
- if (!System.IO.File.Exists(file)) {
- Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + title + " could not be found in " + config);
- return false;
- }
- Program.AppendToLogFile("Loading train plugin: " + file);
- bool success = LoadPlugin(train, file, trainFolder);
- if (success == false)
- {
- Loading.PluginError = Interface.GetInterfaceString("errors_plugin_failure1").Replace("[plugin]", file);
- }
- else
- {
- Program.AppendToLogFile("Train plugin loaded successfully.");
- }
- return success;
- }
-
- /// Loads the default plugin for the specified train.
- /// The train to attach the plugin to.
- /// The train folder.
- /// Whether the plugin was loaded successfully.
- internal static bool LoadDefaultPlugin(TrainManager.Train train, string trainFolder) {
- string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Plugins"), "OpenBveAts.dll");
- bool success = LoadPlugin(train, file, trainFolder);
- if (success) {
- train.Plugin.IsDefault = true;
- SoundCfgParser.LoadDefaultPluginSounds(train, trainFolder);
- }
- return success;
- }
-
- /// Loads the specified plugin for the specified train.
- /// The train to attach the plugin to.
- /// The file to the plugin.
- /// The train folder.
- /// Whether the plugin was loaded successfully.
- private static bool LoadPlugin(TrainManager.Train train, string pluginFile, string trainFolder) {
- string pluginTitle = System.IO.Path.GetFileName(pluginFile);
- if (!System.IO.File.Exists(pluginFile)) {
- Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + pluginTitle + " could not be found.");
- return false;
- }
- /*
- * Unload plugin if already loaded.
- * */
- if (train.Plugin != null) {
- UnloadPlugin(train);
- }
- /*
- * Prepare initialization data for the plugin.
- * */
- BrakeTypes brakeType = (BrakeTypes)train.Cars[train.DriverCar].Specs.BrakeType;
- int brakeNotches;
- int powerNotches;
- bool hasHoldBrake;
- if (brakeType == BrakeTypes.AutomaticAirBrake) {
- brakeNotches = 2;
- powerNotches = train.Specs.MaximumPowerNotch;
- hasHoldBrake = false;
- } else {
- brakeNotches = train.Specs.MaximumBrakeNotch + (train.Specs.HasHoldBrake ? 1 : 0);
- powerNotches = train.Specs.MaximumPowerNotch;
- hasHoldBrake = train.Specs.HasHoldBrake;
- }
- int cars = train.Cars.Length;
- VehicleSpecs specs = new VehicleSpecs(powerNotches, brakeType, brakeNotches, hasHoldBrake, cars);
- InitializationModes mode = (InitializationModes)Game.TrainStart;
- /*
- * Check if the plugin is a .NET plugin.
- * */
- Assembly assembly;
- try {
- assembly = Assembly.LoadFile(pluginFile);
- } catch (BadImageFormatException) {
- assembly = null;
- } catch (Exception ex) {
- Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " could not be loaded due to the following exception: " + ex.Message);
- return false;
- }
- if (assembly != null) {
- Type[] types;
- try {
- types = assembly.GetTypes();
- } catch (ReflectionTypeLoadException ex) {
- foreach (Exception e in ex.LoaderExceptions) {
- Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " raised an exception on loading: " + e.Message);
- }
- return false;
- }
- foreach (Type type in types) {
- if (typeof(IRuntime).IsAssignableFrom(type)) {
- IRuntime api = assembly.CreateInstance(type.FullName) as IRuntime;
- train.Plugin = new NetPlugin(pluginFile, trainFolder, api, train);
- if (train.Plugin.Load(specs, mode)) {
- return true;
- } else {
- train.Plugin = null;
- return false;
- }
- }
- }
- Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE.");
- return false;
- }
- /*
- * Check if the plugin is a Win32 plugin.
- *
- */
- try {
- if (!CheckWin32Header(pluginFile)) {
- Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " is of an unsupported binary format and therefore cannot be used with openBVE.");
- return false;
- }
- } catch (Exception ex) {
- Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " could not be read due to the following reason: " + ex.Message);
- return false;
- }
- if (!Program.CurrentlyRunningOnWindows | IntPtr.Size != 4) {
- Interface.AddMessage(Interface.MessageType.Warning, false, "The train plugin " + pluginTitle + " can only be used on 32-bit Microsoft Windows or compatible.");
- return false;
- }
- if (Program.CurrentlyRunningOnWindows && !System.IO.File.Exists(AppDomain.CurrentDomain.BaseDirectory + "\\AtsPluginProxy.dll"))
- {
- Interface.AddMessage(Interface.MessageType.Warning, false, "AtsPluginProxy.dll is missing or corrupt- Please reinstall.");
- return false;
- }
- train.Plugin = new Win32Plugin(pluginFile, train);
- if (train.Plugin.Load(specs, mode)) {
- return true;
- } else {
- train.Plugin = null;
- Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE.");
- return false;
- }
- }
-
- /// Checks whether a specified file is a valid Win32 plugin.
- /// The file to check.
- /// Whether the file is a valid Win32 plugin.
- private static bool CheckWin32Header(string file) {
- using (System.IO.FileStream stream = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read)) {
- using (System.IO.BinaryReader reader = new System.IO.BinaryReader(stream)) {
- if (reader.ReadUInt16() != 0x5A4D) {
- /* Not MZ signature */
- return false;
- }
- stream.Position = 0x3C;
- stream.Position = reader.ReadInt32();
- if (reader.ReadUInt32() != 0x00004550) {
- /* Not PE signature */
- return false;
- }
- if (reader.ReadUInt16() != 0x014C) {
- /* Not IMAGE_FILE_MACHINE_I386 */
- return false;
- }
- }
- }
- return true;
- }
-
- /// Unloads the currently loaded plugin, if any.
- /// Unloads the plugin for the specified train.
- internal static void UnloadPlugin(TrainManager.Train train) {
- if (train.Plugin != null) {
- train.Plugin.Unload();
- train.Plugin = null;
- }
- }
-
- }
-}
\ No newline at end of file
diff --git a/source/OpenBVE/OldCode/TrainManager.cs b/source/OpenBVE/OldCode/TrainManager.cs
index 83fe05c951..58e278eeb7 100644
--- a/source/OpenBVE/OldCode/TrainManager.cs
+++ b/source/OpenBVE/OldCode/TrainManager.cs
@@ -3135,6 +3135,15 @@ private static void UpdateSpeeds(Train Train, double TimeElapsed)
// update train physics and controls
private static void UpdateTrainPhysicsAndControls(Train Train, double TimeElapsed)
{
+ if (Train.Plugin == null)
+ {
+ //If the safety system has crashed, set it's handles to be that of the driver
+ Train.Specs.CurrentReverser.Actual = Train.Specs.CurrentReverser.Driver;
+ Train.Specs.CurrentEmergencyBrake.Safety = Train.Specs.CurrentEmergencyBrake.Driver;
+ Train.Specs.CurrentBrakeNotch.Safety = Train.Specs.CurrentBrakeNotch.Driver;
+ Train.Specs.AirBrake.Handle.Safety = Train.Specs.AirBrake.Handle.Driver;
+ Train.Specs.CurrentPowerNotch.Safety = Train.Specs.CurrentPowerNotch.Driver;
+ }
if (TimeElapsed == 0.0)
{
return;
diff --git a/source/OpenBVE/OpenBve.csproj b/source/OpenBVE/OpenBve.csproj
index 85ebb16171..6836d74ffb 100644
--- a/source/OpenBVE/OpenBve.csproj
+++ b/source/OpenBVE/OpenBve.csproj
@@ -143,6 +143,8 @@
+
+
@@ -169,9 +171,6 @@
formAbout.cs
-
- Form
-
@@ -249,22 +248,26 @@
formMain.cs
Form
+
+
+ formMain.cs
+ Form
-
+
PluginManager.cs
-
+
PluginManager.cs
-
+
diff --git a/source/OpenBVE/Simulation/TrainPlugins/Blacklist.cs b/source/OpenBVE/Simulation/TrainPlugins/Blacklist.cs
new file mode 100644
index 0000000000..6301b1602c
--- /dev/null
+++ b/source/OpenBVE/Simulation/TrainPlugins/Blacklist.cs
@@ -0,0 +1,292 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Runtime.Remoting.Messaging;
+using System.Security.Cryptography;
+using System.Xml;
+
+
+#pragma warning disable 660,661
+
+namespace OpenBve
+{
+ internal static partial class PluginManager
+ {
+ internal struct BlackListEntry
+ {
+ /// The file length of the blacklisted plugin
+ internal double FileLength;
+ /// The file name of the blacklisted plugin
+ internal string FileName;
+ /// The MD5 sum of the plugin file
+ internal string MD5;
+ /// The train-name if this plugin is blacklisted for one train only
+ internal string Train;
+ /// A textual string describing the reason this plugin is blacklisted
+ internal string Reason;
+ /// Whether all possible versions of this plugin are blacklisted (Overrides MD5 and length)
+ internal bool AllVersions;
+
+ public static bool operator ==(BlackListEntry a, BlackListEntry b)
+ {
+ if (a.FileLength != b.FileLength) return false;
+ if (a.FileName != b.FileName) return false;
+ if (a.MD5 != b.MD5) return false;
+ if ((a.Train ?? "") != (b.Train ?? "")) return false;
+ if ((a.Reason ?? "") != (b.Reason ?? "")) return false;
+ if (a.AllVersions != b.AllVersions) return false;
+ return true;
+ }
+
+ public static bool operator !=(BlackListEntry a, BlackListEntry b)
+ {
+ if (a.FileLength != b.FileLength) return true;
+ if (a.FileName != b.FileName) return true;
+ if (a.MD5 != b.MD5) return true;
+ if ((a.Train ?? "") != (b.Train ?? "")) return true;
+ if ((a.Reason ?? "") != (b.Reason ?? "")) return true;
+ if (a.AllVersions != b.AllVersions) return true;
+ return false;
+ }
+ }
+
+ /// The currently blacklisted plugins
+ internal static List BlackListedPlugins;
+
+ /// Checks whether the specified plugin is blacklisted
+ /// True if blacklisted, false otherwise
+ internal static bool CheckBlackList(string filePath, string trainFolder)
+ {
+ string pluginTitle = System.IO.Path.GetFileName(filePath);
+ if (BlackListedPlugins == null || BlackListedPlugins.Count == 0)
+ {
+ return false;
+ }
+ var n = System.IO.Path.GetFileName(filePath);
+ for (int i = 0; i < BlackListedPlugins.Count; i++)
+ {
+ if (BlackListedPlugins[i].FileName == n.ToLowerInvariant())
+ {
+ if (BlackListedPlugins[i].AllVersions == true)
+ {
+ Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + pluginTitle + " is blacklisted for the following reason:");
+ if (BlackListedPlugins[i].Reason == String.Empty)
+ {
+ Interface.AddMessage(Interface.MessageType.Error, true, "No reason specified.");
+ }
+ else
+ {
+ Interface.AddMessage(Interface.MessageType.Error, true, BlackListedPlugins[i].Reason);
+ }
+ return true;
+ }
+ var fi = new FileInfo(filePath);
+ if (fi.Length == BlackListedPlugins[i].FileLength && (BlackListedPlugins[i].Train == null || trainFolder.ToLowerInvariant() == BlackListedPlugins[i].Train.ToLowerInvariant()))
+ {
+ var md5 = MD5.Create();
+ using (var stream = File.OpenRead(filePath))
+ {
+ md5.ComputeHash(stream);
+ }
+ //String.Empty is required, as otherwise it adds a null character.....
+ string s = BitConverter.ToString(md5.Hash).Replace("-", String.Empty).ToUpper();
+ if (s == BlackListedPlugins[i].MD5)
+ {
+ Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + pluginTitle + " is blacklisted for the following reason:");
+ if (BlackListedPlugins[i].Reason == String.Empty)
+ {
+ Interface.AddMessage(Interface.MessageType.Error, true, "No reason specified.");
+ }
+ else
+ {
+ Interface.AddMessage(Interface.MessageType.Error, true, BlackListedPlugins[i].Reason);
+ }
+ return true;
+ }
+ }
+
+
+ }
+ }
+ return false;
+ }
+
+ /// Checks whether the specified plugin entry is in the blacklist database
+ /// The plugin entry
+ internal static bool CheckBlackList(BlackListEntry plugin)
+ {
+ if (BlackListedPlugins == null || BlackListedPlugins.Count == 0)
+ {
+ return false;
+ }
+ for (int i = 0; i < BlackListedPlugins.Count; i++)
+ {
+ if (plugin == BlackListedPlugins[i])
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Loads the database of blacklisted plugins from disk (Called once on startup)
+ /// The database path
+ internal static void LoadBlackListDatabase(string databasePath)
+ {
+ if (!System.IO.File.Exists(databasePath))
+ {
+ return;
+ }
+ BlackListedPlugins = new List();
+ XmlDocument currentXML = new XmlDocument();
+ //Load the XML file
+ try
+ {
+ currentXML.Load(databasePath);
+ }
+ catch
+ {
+ return;
+ }
+ LoadBlackListDatabase(currentXML);
+ }
+
+ /// Adds blacklisted plugins to the list from an XML document
+ /// The XML document to load
+ internal static void LoadBlackListDatabase(XmlDocument currentXML)
+ {
+ if (BlackListedPlugins == null)
+ {
+ BlackListedPlugins = new List();
+ }
+ if (currentXML.DocumentElement != null)
+ {
+ XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/TrainPlugins/Blacklist");
+ //Check this file actually contains an openBVE plugin blacklist
+ if (DocumentNodes != null)
+ {
+ for(int i = 0; i < DocumentNodes.Count; i++)
+ {
+ BlackListEntry entry = ParseBlackListEntry(DocumentNodes[i]);
+ if (BlackListedPlugins.Count == 0)
+ {
+ BlackListedPlugins.Add(entry);
+ }
+ else
+ {
+ for (int j = 0; j < BlackListedPlugins.Count; j++)
+ {
+ if (BlackListedPlugins[j] == entry)
+ {
+ break;
+ }
+ if (j == BlackListedPlugins.Count -1)
+ {
+ BlackListedPlugins.Add(entry);
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ internal static BlackListEntry ParseBlackListEntry(XmlNode node)
+ {
+ if (node.HasChildNodes)
+ {
+ BlackListEntry p = new BlackListEntry();
+ bool ch = false;
+ foreach (XmlNode c in node.ChildNodes)
+ {
+ switch (c.Name.ToLowerInvariant())
+ {
+ case "filelength":
+ Double.TryParse(c.InnerText, out p.FileLength);
+ ch = true;
+ break;
+ case "filename":
+ p.FileName = c.InnerText.ToLowerInvariant();
+ ch = true;
+ break;
+ case "md5":
+ p.MD5 = c.InnerText.ToUpper().Trim();
+ ch = true;
+ break;
+ case "train":
+ p.Train = c.InnerText;
+ ch = true;
+ break;
+ case "reason":
+ p.Reason = c.InnerText;
+ ch = true;
+ break;
+ case "allversions":
+ string s = c.InnerText.ToLowerInvariant();
+ if (s == "true" || s == "1")
+ {
+ p.AllVersions = true;
+ }
+ break;
+ }
+ }
+ if (ch && (p.MD5 != string.Empty || p.AllVersions == true))
+ {
+ return p;
+ }
+ }
+ return new BlackListEntry();
+ }
+
+ internal static bool RemoveBlackListEntry(BlackListEntry entry)
+ {
+ if (BlackListedPlugins == null)
+ {
+ return false;
+ }
+ for (int i = BlackListedPlugins.Count -1; i >= 0; i--)
+ {
+ if (BlackListedPlugins[i] == entry)
+ {
+ BlackListedPlugins.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Saves the list of blacklisted plugins to disk
+ internal static void WriteBlackListDatabase()
+ {
+ if (BlackListedPlugins == null || BlackListedPlugins.Count == 0)
+ {
+ try
+ {
+ File.Delete(OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("PluginDatabase"), "blacklist.xml"));
+ }
+ catch {}
+ return;
+ }
+ //This isn't a public class, hence building the XML manually for write out
+ XmlDocument currentXML = new XmlDocument();
+ XmlElement rootElement = (XmlElement)currentXML.AppendChild(currentXML.CreateElement("openBVE"));
+ XmlElement firstElement = (XmlElement)rootElement.AppendChild(currentXML.CreateElement("TrainPlugins"));
+ for (int i = 0; i < BlackListedPlugins.Count; i++)
+ {
+ XmlElement entry = (XmlElement)firstElement.AppendChild(currentXML.CreateElement("BlackList"));
+ entry.AppendChild(currentXML.CreateElement("FileLength")).InnerText = BlackListedPlugins[i].FileLength.ToString(CultureInfo.InvariantCulture);
+ entry.AppendChild(currentXML.CreateElement("FileName")).InnerText = BlackListedPlugins[i].FileName;
+ entry.AppendChild(currentXML.CreateElement("MD5")).InnerText = BlackListedPlugins[i].MD5;
+ entry.AppendChild(currentXML.CreateElement("Train")).InnerText = BlackListedPlugins[i].Train;
+ entry.AppendChild(currentXML.CreateElement("Reason")).InnerText = BlackListedPlugins[i].Reason;
+ entry.AppendChild(currentXML.CreateElement("AllVersions")).InnerText = BlackListedPlugins[i].AllVersions ? "true" : "false";
+ }
+ using (StreamWriter sw = new StreamWriter(OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("PluginDatabase"), "blacklist.xml")))
+ {
+ currentXML.Save(sw);
+ }
+ }
+ }
+}
diff --git a/source/OpenBVE/Simulation/TrainPlugins/CompatibleReplacement.cs b/source/OpenBVE/Simulation/TrainPlugins/CompatibleReplacement.cs
new file mode 100644
index 0000000000..25e9f743fe
--- /dev/null
+++ b/source/OpenBVE/Simulation/TrainPlugins/CompatibleReplacement.cs
@@ -0,0 +1,381 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Threading;
+using System.Windows.Forms;
+using System.Xml;
+using OpenBveApi.Colors;
+
+namespace OpenBve
+{
+ internal static partial class PluginManager
+ {
+ internal struct ReplacementPlugin
+ {
+ /// The original plugin this is to replace
+ internal BlackListEntry OriginalPlugin;
+
+ /// The name of the new plugin
+ internal string PluginName;
+
+ /// The path of the compatible plugin to use
+ internal string PluginPath;
+
+ /// The train-name if this replacement is to be used for only one train, or null for all
+ internal string[] Trains;
+
+ /// The message displayed to the user when the compatible plugin is used for the first time
+ /// eg. Control changes
+ internal string Message;
+
+ /// The required configuration file
+ /// If applicable
+ internal string ConfigurationFile;
+
+ public static bool operator !=(ReplacementPlugin a, ReplacementPlugin b)
+ {
+ if ((a.Message ?? "") != (b.Message ?? "")) return true;
+ if (a.OriginalPlugin != b.OriginalPlugin) return true;
+ if (a.PluginPath.Trim() != b.PluginPath.Trim()) return true;
+ if (a.Trains != null && b.Trains != null)
+ {
+ if (a.Trains.Length == b.Trains.Length)
+ {
+ if (a.Trains.Length < 0)
+ {
+ for (int i = 0; i < a.Trains.Length; i++)
+ {
+ if (a.Trains[i] != b.Trains[i])
+ {
+ return true;
+ }
+ }
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+ if ((a.ConfigurationFile ?? "") != (b.ConfigurationFile ?? "")) return true;
+ return false;
+ }
+
+ public static bool operator ==(ReplacementPlugin a, ReplacementPlugin b)
+ {
+ if ((a.Message ?? "") != (b.Message ?? "")) return false;
+ if (a.OriginalPlugin != b.OriginalPlugin) return false;
+ if (a.PluginPath.Trim() != b.PluginPath.Trim()) return false;
+ if (a.Trains != null && b.Trains != null)
+ {
+ if (a.Trains.Length != b.Trains.Length) return false;
+ if (a.Trains.Length < 0)
+ {
+ for (int i = 0; i < a.Trains.Length; i++)
+ {
+ if (a.Trains[i] != b.Trains[i])
+ {
+ return false;
+ }
+ }
+ }
+ }
+ if ((a.ConfigurationFile ?? "") != (b.ConfigurationFile ?? "")) return false;
+ return true;
+ }
+ }
+
+ /// Holds the list of available replacement plugins
+ internal static List AvailableReplacementPlugins;
+
+ internal static bool FindReplacementPlugin(ref string PluginPath)
+ {
+ if (AvailableReplacementPlugins.Count == 0)
+ {
+ PluginPath = null;
+ return false;
+ }
+ var f = System.IO.Path.GetDirectoryName(PluginPath);
+ var fl = System.IO.Path.GetFileName(PluginPath);
+ if (fl == "OpenBveAts.dll")
+ {
+ return false;
+ }
+
+ for (int i = 0; i < AvailableReplacementPlugins.Count; i++)
+ {
+ if (AvailableReplacementPlugins[i].Trains == null || AvailableReplacementPlugins[i].Trains.Contains(f.Split(Path.DirectorySeparatorChar).Last()))
+ {
+ if (CheckBlackList(AvailableReplacementPlugins[i].OriginalPlugin))
+ {
+ //The original plugin is contained within the blacklist
+ Interface.AddMessage(Interface.MessageType.Warning, true, "The blacklisted train plugin " + fl + " has been replaced with the following compatible alternative: " +
+ AvailableReplacementPlugins[i].PluginName);
+ string s = "The blacklisted train plugin " + fl + " has been replaced with the following compatible alternative: " + AvailableReplacementPlugins[i].PluginName;
+ Loading.MessageQueue.Add(new Game.Message(s, Game.MessageDependency.None, MessageColor.Green, 10.0));
+ Loading.PluginMessageColor = MessageColor.Green;
+ PluginPath = OpenBveApi.Path.CombineFile(Program.FileSystem.DataFolder, AvailableReplacementPlugins[i].PluginPath);
+ if (!String.IsNullOrEmpty(AvailableReplacementPlugins[i].Message))
+ {
+ MessageBox.Show(AvailableReplacementPlugins[i].Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ return true;
+ }
+
+ //Not in the blacklist, so check plugin length etc.
+ var fi = new FileInfo(PluginPath);
+ if (fi.Length == AvailableReplacementPlugins[i].OriginalPlugin.FileLength)
+ {
+ var md5 = MD5.Create();
+ using (var stream = File.OpenRead(PluginPath))
+ {
+ md5.ComputeHash(stream);
+ }
+ //String.Empty is required, as otherwise it adds a null character.....
+ string s = BitConverter.ToString(md5.Hash).Replace("-", String.Empty).ToUpper();
+ if (s.ToLowerInvariant() == AvailableReplacementPlugins[i].OriginalPlugin.MD5.ToLowerInvariant())
+ {
+ if (AvailableReplacementPlugins[i].ConfigurationFile != null)
+ {
+ try
+ {
+ switch (AvailableReplacementPlugins[i].PluginName.ToLowerInvariant())
+ {
+ case "uktrainsys":
+ //Exact case of configuration file required on Linux
+ //TODO: Fix UKTrainsys to use API path resolution
+ System.IO.File.Copy(AvailableReplacementPlugins[i].ConfigurationFile, OpenBveApi.Path.CombineFile(f, "UkTrainSys.cfg"));
+ break;
+ case "bvec_ats":
+ System.IO.File.Copy(AvailableReplacementPlugins[i].ConfigurationFile, OpenBveApi.Path.CombineFile(f, "bvec_ats.cfg"));
+ break;
+ }
+
+ }
+ catch
+ {
+ Interface.AddMessage(Interface.MessageType.Error, true, "A compatible alternative for train plugin " + fl + " was found, but an error occured whilst attempting to copy a required configuration file.");
+ return false;
+ }
+ }
+ Interface.AddMessage(Interface.MessageType.Warning, true, "The train plugin " + fl + " has been replaced with the following compatible alternative: " +
+ AvailableReplacementPlugins[i].PluginName);
+ string t = "The train plugin " + fl + " has been replaced with the following compatible alternative: " + AvailableReplacementPlugins[i].PluginName;
+ Loading.MessageQueue.Add(new Game.Message(t, Game.MessageDependency.None, MessageColor.Green, 10.0));
+ Loading.PluginMessageColor = MessageColor.Green;
+ PluginPath = OpenBveApi.Path.CombineFile(Program.FileSystem.DataFolder, AvailableReplacementPlugins[i].PluginPath);
+
+ if (!String.IsNullOrEmpty(AvailableReplacementPlugins[i].Message))
+ {
+ MessageBox.Show(AvailableReplacementPlugins[i].Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ }
+
+ /// Loads the database of compatible replacement plugins from disk
+ /// The database path
+ internal static void LoadReplacementDatabase(string databasePath)
+ {
+ if (!System.IO.File.Exists(databasePath))
+ {
+ return;
+ }
+ AvailableReplacementPlugins = new List();
+ XmlDocument currentXML = new XmlDocument();
+ //Load the XML file
+ try
+ {
+ currentXML.Load(databasePath);
+ }
+ catch
+ {
+ return;
+ }
+ LoadReplacementDatabase(currentXML);
+ }
+
+ /// Adds compatible replacement plugins to the list from an XML document
+ /// The XML document to load
+ internal static void LoadReplacementDatabase(XmlDocument currentXML)
+ {
+ if (AvailableReplacementPlugins == null)
+ {
+ AvailableReplacementPlugins = new List();
+ }
+ if (currentXML.DocumentElement != null)
+ {
+ XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/TrainPlugins/Replacements");
+ //Check this file actually contains an openBVE plugin blacklist
+ if (DocumentNodes != null)
+ {
+ foreach (XmlNode n in DocumentNodes)
+ {
+ ReplacementPlugin plugin = ParseReplacementPlugin(n);
+ if (AvailableReplacementPlugins.Count == 0)
+ {
+ AvailableReplacementPlugins.Add(plugin);
+ }
+ else
+ {
+ for (int i = 0; i < AvailableReplacementPlugins.Count; i++)
+ {
+ //List.Contains() doesn't use the custom equality operator
+ if (AvailableReplacementPlugins[i] == plugin)
+ {
+ break;
+ }
+ if (i == AvailableReplacementPlugins.Count -1)
+ {
+ AvailableReplacementPlugins.Add(plugin);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ internal static ReplacementPlugin ParseReplacementPlugin(XmlNode node)
+ {
+ if (node.HasChildNodes)
+ {
+ ReplacementPlugin r = new ReplacementPlugin();
+ bool ch1 = false;
+ foreach (XmlNode c in node.ChildNodes)
+ {
+ switch (c.Name.ToLowerInvariant())
+ {
+ case "plugin":
+ if (c.HasChildNodes)
+ {
+ BlackListEntry p = new BlackListEntry();
+ bool ch = false;
+ foreach (XmlNode cn in c.ChildNodes)
+ {
+ switch (cn.Name.ToLowerInvariant())
+ {
+ case "filelength":
+ Double.TryParse(cn.InnerText, out p.FileLength);
+ ch = true;
+ break;
+ case "filename":
+ p.FileName = cn.InnerText;
+ ch = true;
+ break;
+ case "md5":
+ p.MD5 = cn.InnerText;
+ ch = true;
+ break;
+ case "train":
+ p.Train = cn.InnerText;
+ ch = true;
+ break;
+ case "reason":
+ p.Reason = cn.InnerText;
+ ch = true;
+ break;
+ }
+ }
+ if (ch && p.MD5 != string.Empty)
+ {
+ r.OriginalPlugin = p;
+ }
+ }
+ break;
+ case "name":
+ r.PluginName = c.InnerText;
+ ch1 = true;
+ break;
+ case "path":
+ r.PluginPath = c.InnerText;
+ ch1 = true;
+ break;
+ case "train":
+ r.Trains = c.InnerText.Split(';');
+ ch1 = true;
+ break;
+ case "configuration":
+ r.ConfigurationFile = c.InnerText;
+ break;
+ case "message":
+ r.Message = c.InnerText;
+ ch1 = true;
+ break;
+ }
+ }
+ if (ch1 && r.OriginalPlugin != null)
+ {
+ return r;
+ }
+ }
+ return new ReplacementPlugin();
+ }
+
+ internal static bool RemoveReplacementPlugin(ReplacementPlugin plugin)
+ {
+ if (AvailableReplacementPlugins == null)
+ {
+ return false;
+ }
+ int t = AvailableReplacementPlugins.Count -1;
+ for (int i = 0; i < AvailableReplacementPlugins.Count; i++)
+ {
+
+ if (plugin == AvailableReplacementPlugins[i])
+ {
+ AvailableReplacementPlugins.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Saves the list of available replacement plugins to disk
+ internal static void WriteReplacementDatabase()
+ {
+ if (AvailableReplacementPlugins == null || AvailableReplacementPlugins.Count == 0)
+ {
+ try
+ {
+ File.Delete(OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("PluginDatabase"),
+ "compatiblereplacements.xml"));
+ }
+ catch {}
+ return;
+ }
+ //This isn't a public class, hence building the XML manually for write out
+ XmlDocument currentXML = new XmlDocument();
+ XmlElement rootElement = (XmlElement)currentXML.AppendChild(currentXML.CreateElement("openBVE"));
+ XmlElement firstElement = (XmlElement)rootElement.AppendChild(currentXML.CreateElement("TrainPlugins"));
+ for (int i = 0; i < AvailableReplacementPlugins.Count; i++)
+ {
+ XmlElement entry = (XmlElement)firstElement.AppendChild(currentXML.CreateElement("Replacements"));
+ XmlElement plugin = (XmlElement)entry.AppendChild(currentXML.CreateElement("Plugin"));
+ plugin.AppendChild(currentXML.CreateElement("FileLength")).InnerText = AvailableReplacementPlugins[i].OriginalPlugin.FileLength.ToString(CultureInfo.InvariantCulture);
+ plugin.AppendChild(currentXML.CreateElement("FileName")).InnerText = AvailableReplacementPlugins[i].OriginalPlugin.FileName;
+ plugin.AppendChild(currentXML.CreateElement("MD5")).InnerText = AvailableReplacementPlugins[i].OriginalPlugin.MD5;
+ plugin.AppendChild(currentXML.CreateElement("Train")).InnerText = AvailableReplacementPlugins[i].OriginalPlugin.Train;
+ plugin.AppendChild(currentXML.CreateElement("Reason")).InnerText = AvailableReplacementPlugins[i].OriginalPlugin.Reason;
+ plugin.AppendChild(currentXML.CreateElement("AllVersions")).InnerText = AvailableReplacementPlugins[i].OriginalPlugin.AllVersions ? "true" : "false";
+ entry.AppendChild(currentXML.CreateElement("Name")).InnerText = AvailableReplacementPlugins[i].PluginName;
+ entry.AppendChild(currentXML.CreateElement("Path")).InnerText = AvailableReplacementPlugins[i].PluginPath;
+ entry.AppendChild(currentXML.CreateElement("Train")).InnerText = AvailableReplacementPlugins[i].Trains == null ? String.Empty : string.Join(",", AvailableReplacementPlugins[i].Trains);
+ entry.AppendChild(currentXML.CreateElement("Configuration")).InnerText = AvailableReplacementPlugins[i].ConfigurationFile;
+ entry.AppendChild(currentXML.CreateElement("Message")).InnerText = AvailableReplacementPlugins[i].Message;
+ }
+ using (StreamWriter sw = new StreamWriter(OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("PluginDatabase"), "compatiblereplacements.xml")))
+ {
+ currentXML.Save(sw);
+ }
+ }
+ }
+}
diff --git a/source/OpenBVE/OldCode/LegacyPlugin.cs b/source/OpenBVE/Simulation/TrainPlugins/LegacyPlugin.cs
similarity index 100%
rename from source/OpenBVE/OldCode/LegacyPlugin.cs
rename to source/OpenBVE/Simulation/TrainPlugins/LegacyPlugin.cs
diff --git a/source/OpenBVE/OldCode/NetPlugin.cs b/source/OpenBVE/Simulation/TrainPlugins/NetPlugin.cs
similarity index 90%
rename from source/OpenBVE/OldCode/NetPlugin.cs
rename to source/OpenBVE/Simulation/TrainPlugins/NetPlugin.cs
index bbebf768e4..1a17a23601 100644
--- a/source/OpenBVE/OldCode/NetPlugin.cs
+++ b/source/OpenBVE/Simulation/TrainPlugins/NetPlugin.cs
@@ -58,7 +58,7 @@ internal NetPlugin(string pluginFile, string trainFolder, IRuntime api, TrainMan
// --- functions ---
internal override bool Load(VehicleSpecs specs, InitializationModes mode) {
LoadProperties properties = new LoadProperties(this.PluginFolder, this.TrainFolder, this.PlaySound, this.AddInterfaceMessage);
- bool success;
+ bool success = false;
#if !DEBUG
try {
#endif
@@ -66,8 +66,7 @@ internal override bool Load(VehicleSpecs specs, InitializationModes mode) {
base.SupportsAI = properties.AISupport == AISupport.Basic;
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
if (success) {
@@ -79,8 +78,7 @@ internal override bool Load(VehicleSpecs specs, InitializationModes mode) {
Api.Initialize(mode);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
UpdatePower();
@@ -101,9 +99,8 @@ internal override void Unload() {
#endif
this.Api.Unload();
#if !DEBUG
- } catch (Exception ex) {
- base.LastException = ex;
- throw;
+ } catch {
+ //Not too worried about exceptions raised when unloading the plugin really...
}
#endif
}
@@ -114,8 +111,7 @@ internal override void BeginJump(InitializationModes mode) {
this.Api.Initialize(mode);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
@@ -126,6 +122,10 @@ internal override void Elapse(ElapseData data) {
#endif
this.Api.Elapse(data);
for (int i = 0; i < this.SoundHandlesCount; i++) {
+ if (this.SoundHandles[i] == null)
+ {
+ continue;
+ }
if (this.SoundHandles[i].Stopped | this.SoundHandles[i].Source.State == Sounds.SoundSourceState.Stopped) {
this.SoundHandles[i].Stop();
this.SoundHandles[i].Source.Stop();
@@ -139,8 +139,7 @@ internal override void Elapse(ElapseData data) {
}
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
@@ -151,8 +150,7 @@ internal override void SetReverser(int reverser) {
this.Api.SetReverser(reverser);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
@@ -163,8 +161,7 @@ internal override void SetPower(int powerNotch) {
this.Api.SetPower(powerNotch);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
@@ -175,8 +172,7 @@ internal override void SetBrake(int brakeNotch) {
this.Api.SetBrake(brakeNotch);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
@@ -187,8 +183,7 @@ internal override void KeyDown(VirtualKeys key) {
this.Api.KeyDown(key);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
@@ -199,8 +194,7 @@ internal override void KeyUp(VirtualKeys key) {
this.Api.KeyUp(key);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
@@ -211,8 +205,7 @@ internal override void HornBlow(HornTypes type) {
this.Api.HornBlow(type);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
@@ -223,8 +216,7 @@ internal override void DoorChange(DoorStates oldState, DoorStates newState) {
this.Api.DoorChange(oldState, newState);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
@@ -232,31 +224,21 @@ internal override void SetSignal(SignalData[] signal) {
#if !DEBUG
try {
#endif
-// if (this.Train == TrainManager.PlayerTrain) {
-// for (int i = 0; i < signal.Length; i++) {
-// Game.AddDebugMessage(i.ToString() + " - " + signal[i].Aspect.ToString(), 3.0);
-// }
-// }
this.Api.SetSignal(signal);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
internal override void SetBeacon(BeaconData beacon) {
-// if (this.Train == TrainManager.PlayerTrain) {
-// Game.AddDebugMessage("Beacon, type=" + beacon.Type.ToString() + ", aspect=" + beacon.Signal.Aspect.ToString() + ", data=" + beacon.Optional.ToString(), 3.0);
-// }
#if !DEBUG
try {
#endif
this.Api.SetBeacon(beacon);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
@@ -267,12 +249,33 @@ internal override void PerformAI(AIData data) {
this.Api.PerformAI(data);
#if !DEBUG
} catch (Exception ex) {
- base.LastException = ex;
- throw;
+ Crash(ex);
}
#endif
}
+ /// Is called when a .Net based plugin crashes during the game
+ /// The exception raised
+ internal void Crash(Exception ex)
+ {
+ try
+ {
+ base.LastException = ex;
+ CrashHandler.PluginCrash(ex.ToString());
+ Unload();
+ for (int i = 0; i < TrainManager.Trains.Length; i++)
+ {
+ if (TrainManager.Trains[i].Plugin.PluginTitle == base.PluginTitle)
+ {
+ TrainManager.Trains[i].Plugin = null;
+ }
+ }
+ }
+ catch
+ {
+ }
+ }
+
/// May be called from a .Net plugin, in order to add a message to the in-game display
/// The message to display
/// The color in which to display the message
diff --git a/source/OpenBVE/Simulation/TrainPlugins/PluginManager.cs b/source/OpenBVE/Simulation/TrainPlugins/PluginManager.cs
new file mode 100644
index 0000000000..3336b9315a
--- /dev/null
+++ b/source/OpenBVE/Simulation/TrainPlugins/PluginManager.cs
@@ -0,0 +1,833 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using OpenBveApi.Colors;
+using OpenBveApi.Runtime;
+
+namespace OpenBve
+{
+ internal static partial class PluginManager
+ {
+ /// Represents an abstract plugin.
+ internal abstract class Plugin
+ {
+ // --- members ---
+ /// The file title of the plugin, including the file extension.
+ internal string PluginTitle;
+ /// Whether the plugin is the default ATS/ATC plugin.
+ internal bool IsDefault;
+ /// Whether the plugin returned valid information in the last Elapse call.
+ internal bool PluginValid;
+ /// The debug message the plugin returned in the last Elapse call.
+ internal string PluginMessage;
+ /// The train the plugin is attached to.
+ internal TrainManager.Train Train;
+ /// The array of panel variables.
+ internal int[] Panel;
+ /// Whether the plugin supports the AI.
+ internal bool SupportsAI;
+ /// The last in-game time reported to the plugin.
+ internal double LastTime;
+ /// The last reverser reported to the plugin.
+ internal int LastReverser;
+ /// The last power notch reported to the plugin.
+ internal int LastPowerNotch;
+ /// The last brake notch reported to the plugin.
+ internal int LastBrakeNotch;
+ /// The last aspects per relative section reported to the plugin. Section 0 is the current section, section 1 the upcoming section, and so on.
+ internal int[] LastAspects;
+ /// The absolute section the train was last.
+ internal int LastSection;
+ /// The last exception the plugin raised.
+ internal Exception LastException;
+ //NEW: Whether this plugin can disable the time acceleration factor
+ /// Whether this plugin can disable time acceleration.
+ internal static bool DisableTimeAcceleration;
+ /// The current camera view mode
+ internal OpenBveApi.Runtime.CameraViewMode CurrentCameraViewMode;
+
+ private List currentRouteStations;
+ internal bool StationsLoaded;
+ // --- functions ---
+ /// Called to load and initialize the plugin.
+ /// The train specifications.
+ /// The initialization mode of the train.
+ /// Whether loading the plugin was successful.
+ internal abstract bool Load(VehicleSpecs specs, InitializationModes mode);
+ /// Called to unload the plugin.
+ internal abstract void Unload();
+ /// Called before the train jumps to a different location.
+ /// The initialization mode of the train.
+ internal abstract void BeginJump(InitializationModes mode);
+ /// Called when the train has finished jumping to a different location.
+ internal abstract void EndJump();
+ /// Called every frame to update the plugin.
+ internal void UpdatePlugin()
+ {
+ /*
+ * Prepare the vehicle state.
+ * */
+ double location = this.Train.Cars[0].FrontAxle.Follower.TrackPosition - this.Train.Cars[0].FrontAxlePosition + 0.5 * this.Train.Cars[0].Length;
+ //Curve Radius, Cant and Pitch Added
+ double CurrentRadius = this.Train.Cars[0].FrontAxle.Follower.CurveRadius;
+ double CurrentCant = this.Train.Cars[0].FrontAxle.Follower.CurveCant;
+ double CurrentPitch = this.Train.Cars[0].FrontAxle.Follower.Pitch;
+ //If the list of stations has not been loaded, do so
+ if (!StationsLoaded)
+ {
+ currentRouteStations = new List();
+ foreach (Game.Station selectedStation in Game.Stations)
+ {
+ Station i = new Station
+ {
+ Name = selectedStation.Name,
+ ArrivalTime = selectedStation.ArrivalTime,
+ DepartureTime = selectedStation.DepartureTime,
+ StopTime = selectedStation.StopTime,
+ OpenLeftDoors = selectedStation.OpenLeftDoors,
+ OpenRightDoors = selectedStation.OpenRightDoors,
+ ForceStopSignal = selectedStation.ForceStopSignal,
+ DefaultTrackPosition = selectedStation.DefaultTrackPosition
+ };
+ currentRouteStations.Add(i);
+ }
+ StationsLoaded = true;
+ }
+ //End of additions
+ double speed = this.Train.Cars[this.Train.DriverCar].Specs.CurrentPerceivedSpeed;
+ double bcPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.BrakeCylinderCurrentPressure;
+ double mrPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.MainReservoirCurrentPressure;
+ double erPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.EqualizingReservoirCurrentPressure;
+ double bpPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.BrakePipeCurrentPressure;
+ double sapPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.StraightAirPipeCurrentPressure;
+ VehicleState vehicle = new VehicleState(location, new Speed(speed), bcPressure, mrPressure, erPressure, bpPressure, sapPressure, CurrentRadius, CurrentCant, CurrentPitch);
+ /*
+ * Prepare the preceding vehicle state.
+ * */
+ double bestLocation = double.MaxValue;
+ double bestSpeed = 0.0;
+
+ for (int i = 0; i < TrainManager.Trains.Length; i++)
+ {
+ if (TrainManager.Trains[i] != this.Train & TrainManager.Trains[i].State == TrainManager.TrainState.Available)
+ {
+ int c = TrainManager.Trains[i].Cars.Length - 1;
+ double z = TrainManager.Trains[i].Cars[c].RearAxle.Follower.TrackPosition - TrainManager.Trains[i].Cars[c].RearAxlePosition - 0.5 * TrainManager.Trains[i].Cars[c].Length;
+
+ if (z >= location & z < bestLocation)
+ {
+ bestLocation = z;
+ bestSpeed = TrainManager.Trains[i].Specs.CurrentAverageSpeed;
+ }
+ }
+ }
+
+ var precedingVehicle = bestLocation != double.MaxValue ? new PrecedingVehicleState(bestLocation, bestLocation - location, new Speed(bestSpeed)) : null;
+ /*
+ * Get the driver handles.
+ * */
+ Handles handles = GetHandles();
+ /*
+ * Update the plugin.
+ * */
+ double totalTime = Game.SecondsSinceMidnight;
+ double elapsedTime = Game.SecondsSinceMidnight - LastTime;
+ /*
+ * Set the current camera view mode
+ * Could probably do away with the CurrentCameraViewMode and use a direct cast??
+ *
+ */
+ CurrentCameraViewMode = (OpenBveApi.Runtime.CameraViewMode)World.CameraMode;
+ ElapseData data = new ElapseData(vehicle, precedingVehicle, handles, new Time(totalTime), new Time(elapsedTime), currentRouteStations, CurrentCameraViewMode, Interface.CurrentLanguageCode);
+ LastTime = Game.SecondsSinceMidnight;
+ Elapse(data);
+ this.PluginMessage = data.DebugMessage;
+ DisableTimeAcceleration = data.DisableTimeAcceleration;
+ /*
+ * Set the virtual handles.
+ * */
+ this.PluginValid = true;
+ SetHandles(data.Handles, true);
+ }
+ /// Gets the driver handles.
+ /// The driver handles.
+ private Handles GetHandles()
+ {
+ int reverser = this.Train.Specs.CurrentReverser.Driver;
+ int powerNotch = this.Train.Specs.CurrentPowerNotch.Driver;
+ int brakeNotch;
+
+ if (this.Train.Cars[this.Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake)
+ {
+ brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? 3 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service ? 2 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap ? 1 : 0;
+ }
+ else {
+ if (this.Train.Specs.HasHoldBrake)
+ {
+ brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 2 : this.Train.Specs.CurrentBrakeNotch.Driver > 0 ? this.Train.Specs.CurrentBrakeNotch.Driver + 1 : this.Train.Specs.CurrentHoldBrake.Driver ? 1 : 0;
+ }
+ else {
+ brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 1 : this.Train.Specs.CurrentBrakeNotch.Driver;
+ }
+ }
+
+ bool constSpeed = this.Train.Specs.CurrentConstSpeed;
+ return new Handles(reverser, powerNotch, brakeNotch, constSpeed);
+ }
+ /// Sets the driver handles or the virtual handles.
+ /// The handles.
+ /// Whether to set the virtual handles.
+ private void SetHandles(Handles handles, bool virtualHandles)
+ {
+ /*
+ * Process the handles.
+ */
+ if (this.Train.Specs.SingleHandle & handles.BrakeNotch != 0)
+ {
+ handles.PowerNotch = 0;
+ }
+ /*
+ * Process the reverser.
+ */
+ if (handles.Reverser >= -1 & handles.Reverser <= 1)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentReverser.Actual = handles.Reverser;
+ }
+ else {
+ TrainManager.ApplyReverser(this.Train, handles.Reverser, false);
+ }
+ }
+ else {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentReverser.Actual = this.Train.Specs.CurrentReverser.Driver;
+ }
+ this.PluginValid = false;
+ }
+ /*
+ * Process the power.
+ * */
+ if (handles.PowerNotch >= 0 & handles.PowerNotch <= this.Train.Specs.MaximumPowerNotch)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentPowerNotch.Safety = handles.PowerNotch;
+ }
+ else {
+ TrainManager.ApplyNotch(this.Train, handles.PowerNotch, false, 0, true);
+ }
+ }
+ else {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentPowerNotch.Safety = this.Train.Specs.CurrentPowerNotch.Driver;
+ }
+ this.PluginValid = false;
+ }
+
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentEmergencyBrake.Safety = false;
+ this.Train.Specs.CurrentHoldBrake.Actual = false;
+ }
+ if (this.Train.Cars[this.Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake)
+ {
+ if (handles.BrakeNotch == 0)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Release;
+ }
+ else {
+ TrainManager.UnapplyEmergencyBrake(this.Train);
+ TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Release);
+ }
+ }
+ else if (handles.BrakeNotch == 1)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Lap;
+ }
+ else {
+ TrainManager.UnapplyEmergencyBrake(this.Train);
+ TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Lap);
+ }
+ }
+ else if (handles.BrakeNotch == 2)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service;
+ }
+ else {
+ TrainManager.UnapplyEmergencyBrake(this.Train);
+ TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Release);
+ }
+ }
+ else if (handles.BrakeNotch == 3)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service;
+ this.Train.Specs.CurrentEmergencyBrake.Safety = true;
+ }
+ else {
+ TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Service);
+ TrainManager.ApplyEmergencyBrake(this.Train);
+ }
+ }
+ else {
+ this.PluginValid = false;
+ }
+ }
+ else {
+ if (this.Train.Specs.HasHoldBrake)
+ {
+ if (handles.BrakeNotch == this.Train.Specs.MaximumBrakeNotch + 2)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentEmergencyBrake.Safety = true;
+ this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.MaximumBrakeNotch;
+ }
+ else {
+ TrainManager.ApplyHoldBrake(this.Train, false);
+ TrainManager.ApplyNotch(this.Train, 0, true, this.Train.Specs.MaximumBrakeNotch, false);
+ TrainManager.ApplyEmergencyBrake(this.Train);
+ }
+ }
+ else if (handles.BrakeNotch >= 2 & handles.BrakeNotch <= this.Train.Specs.MaximumBrakeNotch + 1)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentBrakeNotch.Safety = handles.BrakeNotch - 1;
+ }
+ else {
+ TrainManager.UnapplyEmergencyBrake(this.Train);
+ TrainManager.ApplyHoldBrake(this.Train, false);
+ TrainManager.ApplyNotch(this.Train, 0, true, handles.BrakeNotch - 1, false);
+ }
+ }
+ else if (handles.BrakeNotch == 1)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentBrakeNotch.Safety = 0;
+ this.Train.Specs.CurrentHoldBrake.Actual = true;
+ }
+ else {
+ TrainManager.UnapplyEmergencyBrake(this.Train);
+ TrainManager.ApplyNotch(this.Train, 0, true, 0, false);
+ TrainManager.ApplyHoldBrake(this.Train, true);
+ }
+ }
+ else if (handles.BrakeNotch == 0)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentBrakeNotch.Safety = 0;
+ }
+ else {
+ TrainManager.UnapplyEmergencyBrake(this.Train);
+ TrainManager.ApplyNotch(this.Train, 0, true, 0, false);
+ TrainManager.ApplyHoldBrake(this.Train, false);
+ }
+ }
+ else {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.CurrentBrakeNotch.Driver;
+ }
+ this.PluginValid = false;
+ }
+ }
+ else {
+ if (handles.BrakeNotch == this.Train.Specs.MaximumBrakeNotch + 1)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentEmergencyBrake.Safety = true;
+ this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.MaximumBrakeNotch;
+ }
+ else {
+ TrainManager.ApplyHoldBrake(this.Train, false);
+ TrainManager.ApplyEmergencyBrake(this.Train);
+ }
+ }
+ else if (handles.BrakeNotch >= 0 & handles.BrakeNotch <= this.Train.Specs.MaximumBrakeNotch | this.Train.Specs.CurrentBrakeNotch.DelayedChanges.Length == 0)
+ {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentBrakeNotch.Safety = handles.BrakeNotch;
+ }
+ else {
+ TrainManager.UnapplyEmergencyBrake(this.Train);
+ TrainManager.ApplyNotch(this.Train, 0, true, handles.BrakeNotch, false);
+ }
+ }
+ else {
+ if (virtualHandles)
+ {
+ this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.CurrentBrakeNotch.Driver;
+ }
+ this.PluginValid = false;
+ }
+ }
+ }
+ /*
+ * Process the const speed system.
+ * */
+ this.Train.Specs.CurrentConstSpeed = handles.ConstSpeed & this.Train.Specs.HasConstSpeed;
+ }
+ /// Called every frame to update the plugin.
+ /// The data passed to the plugin on Elapse.
+ /// This function should not be called directly. Call UpdatePlugin instead.
+ internal abstract void Elapse(ElapseData data);
+ /// Called to update the reverser. This invokes a call to SetReverser only if a change actually occured.
+ internal void UpdateReverser()
+ {
+ int reverser = this.Train.Specs.CurrentReverser.Driver;
+
+ if (reverser != this.LastReverser)
+ {
+ this.LastReverser = reverser;
+ SetReverser(reverser);
+ }
+ }
+ /// Called to indicate a change of the reverser.
+ /// The reverser.
+ /// This function should not be called directly. Call UpdateReverser instead.
+ internal abstract void SetReverser(int reverser);
+ /// Called to update the power notch. This invokes a call to SetPower only if a change actually occured.
+ internal void UpdatePower()
+ {
+ int powerNotch = this.Train.Specs.CurrentPowerNotch.Driver;
+
+ if (powerNotch != this.LastPowerNotch)
+ {
+ this.LastPowerNotch = powerNotch;
+ SetPower(powerNotch);
+ }
+ }
+ /// Called to indicate a change of the power notch.
+ /// The power notch.
+ /// This function should not be called directly. Call UpdatePower instead.
+ internal abstract void SetPower(int powerNotch);
+ /// Called to update the brake notch. This invokes a call to SetBrake only if a change actually occured.
+ internal void UpdateBrake()
+ {
+ int brakeNotch;
+
+ if (this.Train.Cars[this.Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake)
+ {
+ if (this.Train.Specs.HasHoldBrake)
+ {
+ brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? 4 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service ? 3 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap ? 2 : this.Train.Specs.CurrentHoldBrake.Driver ? 1 : 0;
+ }
+ else {
+ brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? 3 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service ? 2 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap ? 1 : 0;
+ }
+ }
+ else {
+ if (this.Train.Specs.HasHoldBrake)
+ {
+ brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 2 : this.Train.Specs.CurrentBrakeNotch.Driver > 0 ? this.Train.Specs.CurrentBrakeNotch.Driver + 1 : this.Train.Specs.CurrentHoldBrake.Driver ? 1 : 0;
+ }
+ else {
+ brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 1 : this.Train.Specs.CurrentBrakeNotch.Driver;
+ }
+ }
+ if (brakeNotch != this.LastBrakeNotch)
+ {
+ this.LastBrakeNotch = brakeNotch;
+ SetBrake(brakeNotch);
+ }
+ }
+ /// Called to indicate a change of the brake notch.
+ /// The brake notch.
+ /// This function should not be called directly. Call UpdateBrake instead.
+ internal abstract void SetBrake(int brakeNotch);
+ /// Called when a virtual key is pressed.
+ internal abstract void KeyDown(VirtualKeys key);
+ /// Called when a virtual key is released.
+ internal abstract void KeyUp(VirtualKeys key);
+ /// Called when a horn is played or stopped.
+ internal abstract void HornBlow(HornTypes type);
+ /// Called when the state of the doors changes.
+ internal abstract void DoorChange(DoorStates oldState, DoorStates newState);
+ /// Called to update the aspects of the section. This invokes a call to SetSignal only if a change in aspect occured or when changing section boundaries.
+ /// The sections to submit to the plugin.
+ internal void UpdateSignals(SignalData[] data)
+ {
+ if (data.Length != 0)
+ {
+ bool update;
+
+ if (this.Train.CurrentSectionIndex != this.LastSection)
+ {
+ update = true;
+ }
+ else if (data.Length != this.LastAspects.Length)
+ {
+ update = true;
+ }
+ else {
+ update = false;
+ for (int i = 0; i < data.Length; i++)
+ {
+ if (data[i].Aspect != this.LastAspects[i])
+ {
+ update = true;
+ break;
+ }
+ }
+ }
+ if (update)
+ {
+ SetSignal(data);
+ this.LastAspects = new int[data.Length];
+ for (int i = 0; i < data.Length; i++)
+ {
+ this.LastAspects[i] = data[i].Aspect;
+ }
+ }
+ }
+ }
+ /// Is called when the aspect in the current or any of the upcoming sections changes.
+ /// Signal information per section. In the array, index 0 is the current section, index 1 the upcoming section, and so on.
+ /// This function should not be called directly. Call UpdateSignal instead.
+ internal abstract void SetSignal(SignalData[] signal);
+ /// Called when the train passes a beacon.
+ /// The beacon type.
+ /// The section the beacon is attached to, or -1 for the next red signal.
+ /// Optional data attached to the beacon.
+ internal void UpdateBeacon(int type, int sectionIndex, int optional)
+ {
+ if (sectionIndex == -1)
+ {
+ sectionIndex = this.Train.CurrentSectionIndex + 1;
+
+ SignalData signal = null;
+
+ while (sectionIndex < Game.Sections.Length)
+ {
+ signal = Game.GetPluginSignal(this.Train, sectionIndex);
+ if (signal.Aspect == 0) break;
+ sectionIndex++;
+ }
+ if (sectionIndex < Game.Sections.Length)
+ {
+ SetBeacon(new BeaconData(type, optional, signal));
+ }
+ else {
+ SetBeacon(new BeaconData(type, optional, new SignalData(-1, double.MaxValue)));
+ }
+ }
+ if (sectionIndex >= 0)
+ {
+ SignalData signal;
+
+ if (sectionIndex < Game.Sections.Length)
+ {
+ signal = Game.GetPluginSignal(this.Train, sectionIndex);
+ }
+ else {
+ signal = new SignalData(0, double.MaxValue);
+ }
+ SetBeacon(new BeaconData(type, optional, signal));
+ }
+ else {
+ SetBeacon(new BeaconData(type, optional, new SignalData(-1, double.MaxValue)));
+ }
+ }
+ /// Called when the train passes a beacon.
+ /// The beacon data.
+ /// This function should not be called directly. Call UpdateBeacon instead.
+ internal abstract void SetBeacon(BeaconData beacon);
+ /// Updates the AI.
+ /// The AI response.
+ internal AIResponse UpdateAI()
+ {
+ if (this.SupportsAI)
+ {
+ AIData data = new AIData(GetHandles());
+ this.PerformAI(data);
+ if (data.Response != AIResponse.None)
+ {
+ SetHandles(data.Handles, false);
+ }
+ return data.Response;
+ }
+ else {
+ return AIResponse.None;
+ }
+ }
+ /// Called when the AI should be performed.
+ /// The AI data.
+ /// This function should not be called directly. Call UpdateAI instead.
+ internal abstract void PerformAI(AIData data);
+ }
+
+ /// Loads a custom plugin for the specified train.
+ /// The train to attach the plugin to.
+ /// The absolute path to the train folder.
+ /// The encoding to be used.
+ /// Whether the plugin was loaded successfully.
+ internal static bool LoadCustomPlugin(TrainManager.Train train, string trainFolder, System.Text.Encoding encoding)
+ {
+ string config = OpenBveApi.Path.CombineFile(trainFolder, "ats.cfg");
+
+ if (!System.IO.File.Exists(config))
+ {
+ return false;
+ }
+
+ string[] lines = System.IO.File.ReadAllLines(config, encoding);
+
+ if (lines.Length == 0)
+ {
+ return false;
+ }
+
+ string file = OpenBveApi.Path.CombineFile(trainFolder, lines[0]);
+ string title = System.IO.Path.GetFileName(file);
+
+ if (!System.IO.File.Exists(file))
+ {
+ Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + title + " could not be found in " + config);
+ return false;
+ }
+ Program.AppendToLogFile("Loading train plugin: " + file);
+
+ bool success = LoadPlugin(train, file, trainFolder);
+
+ if (success == false)
+ {
+ Loading.MessageQueue.Add(new Game.Message((Interface.GetInterfaceString("errors_plugin_failure1").Replace("[plugin]", file)), Game.MessageDependency.None, MessageColor.Red, 5.0));
+ }
+ else
+ {
+ Program.AppendToLogFile("Train plugin loaded successfully.");
+ }
+ return success;
+ }
+
+ /// Loads the default plugin for the specified train.
+ /// The train to attach the plugin to.
+ /// The train folder.
+ /// Whether the plugin was loaded successfully.
+ internal static bool LoadDefaultPlugin(TrainManager.Train train, string trainFolder)
+ {
+ string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Plugins"), "OpenBveAts.dll");
+ bool success = LoadPlugin(train, file, trainFolder);
+
+ if (success)
+ {
+ train.Plugin.IsDefault = true;
+ SoundCfgParser.LoadDefaultPluginSounds(train, trainFolder);
+ }
+ return success;
+ }
+
+ /// Loads the specified plugin for the specified train.
+ /// The train to attach the plugin to.
+ /// The file to the plugin.
+ /// The train folder.
+ /// Whether the plugin was loaded successfully.
+ private static bool LoadPlugin(TrainManager.Train train, string pluginFile, string trainFolder)
+ {
+ string pluginTitle = System.IO.Path.GetFileName(pluginFile);
+
+ if (!System.IO.File.Exists(pluginFile))
+ {
+ Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + pluginTitle + " could not be found.");
+ return false;
+ }
+ if (PluginManager.CheckBlackList(pluginFile, trainFolder))
+ {
+ //Check for a compatible replacement for this blacklisted plugin
+ if (!PluginManager.FindReplacementPlugin(ref pluginFile))
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Unload plugin if already loaded.
+ * */
+ if (train.Plugin != null)
+ {
+ UnloadPlugin(train);
+ }
+ /*
+ * Prepare initialization data for the plugin.
+ * */
+ BrakeTypes brakeType = (BrakeTypes)train.Cars[train.DriverCar].Specs.BrakeType;
+ int brakeNotches;
+ int powerNotches;
+ bool hasHoldBrake;
+
+ if (brakeType == BrakeTypes.AutomaticAirBrake)
+ {
+ brakeNotches = 2;
+ powerNotches = train.Specs.MaximumPowerNotch;
+ hasHoldBrake = false;
+ }
+ else {
+ brakeNotches = train.Specs.MaximumBrakeNotch + (train.Specs.HasHoldBrake ? 1 : 0);
+ powerNotches = train.Specs.MaximumPowerNotch;
+ hasHoldBrake = train.Specs.HasHoldBrake;
+ }
+
+ int cars = train.Cars.Length;
+ VehicleSpecs specs = new VehicleSpecs(powerNotches, brakeType, brakeNotches, hasHoldBrake, cars);
+ InitializationModes mode = (InitializationModes)Game.TrainStart;
+ /*
+ * Check if the plugin is a .NET plugin.
+ * */
+ Assembly assembly;
+
+ try
+ {
+ assembly = Assembly.LoadFile(pluginFile);
+ }
+ catch (BadImageFormatException)
+ {
+ assembly = null;
+ }
+ catch (Exception ex)
+ {
+ Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " could not be loaded due to the following exception: " + ex.Message);
+ return false;
+ }
+ if (assembly != null)
+ {
+ Type[] types;
+
+ try
+ {
+ types = assembly.GetTypes();
+ }
+ catch (ReflectionTypeLoadException ex)
+ {
+ foreach (Exception e in ex.LoaderExceptions)
+ {
+ Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " raised an exception on loading: " + e.Message);
+ }
+ return false;
+ }
+ foreach (Type type in types)
+ {
+ if (typeof(IRuntime).IsAssignableFrom(type))
+ {
+ IRuntime api = assembly.CreateInstance(type.FullName) as IRuntime;
+ train.Plugin = new NetPlugin(pluginFile, trainFolder, api, train);
+ if (train.Plugin.Load(specs, mode))
+ {
+ return true;
+ }
+ else {
+ train.Plugin = null;
+ return false;
+ }
+ }
+ }
+ Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE.");
+ return false;
+ }
+ /*
+ * Check if the plugin is a Win32 plugin.
+ *
+ */
+ try
+ {
+ if (!CheckWin32Header(pluginFile))
+ {
+ Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " is of an unsupported binary format and therefore cannot be used with openBVE.");
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " could not be read due to the following reason: " + ex.Message);
+ return false;
+ }
+ if (!Program.CurrentlyRunningOnWindows | IntPtr.Size != 4)
+ {
+ Interface.AddMessage(Interface.MessageType.Warning, false, "The train plugin " + pluginTitle + " can only be used on 32-bit Microsoft Windows or compatible.");
+ if (Interface.CurrentOptions.UseCompatiblePlugins)
+ {
+ if (!PluginManager.FindReplacementPlugin(ref pluginFile) && pluginTitle != "OpenBveAts.dll")
+ {
+ Interface.AddMessage(Interface.MessageType.Warning, false, "No compatible replacement was found.");
+ }
+ else
+ {
+ return LoadPlugin(train, pluginFile, trainFolder);
+ }
+ }
+ return false;
+ }
+ if (Program.CurrentlyRunningOnWindows && !System.IO.File.Exists(AppDomain.CurrentDomain.BaseDirectory + "\\AtsPluginProxy.dll"))
+ {
+ Interface.AddMessage(Interface.MessageType.Warning, false, "AtsPluginProxy.dll is missing or corrupt- Please reinstall.");
+ return false;
+ }
+ train.Plugin = new Win32Plugin(pluginFile, train);
+ if (train.Plugin.Load(specs, mode))
+ {
+ return true;
+ }
+ else {
+ train.Plugin = null;
+ Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE.");
+ return false;
+ }
+ }
+
+ /// Checks whether a specified file is a valid Win32 plugin.
+ /// The file to check.
+ /// Whether the file is a valid Win32 plugin.
+ private static bool CheckWin32Header(string file)
+ {
+ using (System.IO.FileStream stream = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read))
+ {
+ using (System.IO.BinaryReader reader = new System.IO.BinaryReader(stream))
+ {
+ if (reader.ReadUInt16() != 0x5A4D)
+ {
+ /* Not MZ signature */
+ return false;
+ }
+ stream.Position = 0x3C;
+ stream.Position = reader.ReadInt32();
+ if (reader.ReadUInt32() != 0x00004550)
+ {
+ /* Not PE signature */
+ return false;
+ }
+ if (reader.ReadUInt16() != 0x014C)
+ {
+ /* Not IMAGE_FILE_MACHINE_I386 */
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /// Unloads the currently loaded plugin, if any.
+ /// Unloads the plugin for the specified train.
+ internal static void UnloadPlugin(TrainManager.Train train)
+ {
+ if (train.Plugin != null)
+ {
+ train.Plugin.Unload();
+ train.Plugin = null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/OpenBVE/System/Functions/CrashHandler.cs b/source/OpenBVE/System/Functions/CrashHandler.cs
index 5b880b62d6..eefe37c139 100644
--- a/source/OpenBVE/System/Functions/CrashHandler.cs
+++ b/source/OpenBVE/System/Functions/CrashHandler.cs
@@ -6,221 +6,297 @@
namespace OpenBve
{
- /// Provides functions for handling crashes, and producing an appropriate error log
- class CrashHandler
- {
- static readonly System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
- static readonly string CrashLog = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder,"OpenBVE Crash- " + DateTime.Now.ToString("yyyy.M.dd[HH.mm]") + ".log");
- /// Catches all unhandled exceptions within the current appdomain
- internal static void CurrentDomain_UnhandledException(Object sender, UnhandledExceptionEventArgs e)
- {
- try
- {
- Exception ex = (Exception)e.ExceptionObject;
- if (ex is ArgumentOutOfRangeException && ex.Message == "Specified argument was out of the range of valid values.\r\nParameter name: button")
- {
- //If a joystick with an excessive number of axis or buttons is connected, at the least show a nice error message, rather than simply dissapearing
- MessageBox.Show("An unsupported joystick is connected: \n \n Too many buttons. \n \n Please unplug all USB joysticks & gamepads and try again.");
- Environment.Exit(0);
- }
- if (ex is ArgumentOutOfRangeException && ex.Message == "Specified argument was out of the range of valid values.\r\nParameter name: axis")
- {
- //If a joystick with an excessive number of axis or buttons is connected, at the least show a nice error message, rather than simply dissapearing
- MessageBox.Show("An unsupported joystick is connected: \n \n Too many axis. \n \n Please unplug all USB joysticks & gamepads and try again.");
- Environment.Exit(0);
- }
- MessageBox.Show("Unhandled exception:\n\n" + ex.Message);
- LogCrash(ex + Environment.StackTrace);
+ /// Provides functions for handling crashes, and producing an appropriate error log
+ class CrashHandler
+ {
+ static readonly System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
+ static readonly string CrashLog = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder,"OpenBVE Crash- " + DateTime.Now.ToString("yyyy.M.dd[HH.mm]") + ".log");
+ /// Catches all unhandled exceptions within the current appdomain
+ internal static void CurrentDomain_UnhandledException(Object sender, UnhandledExceptionEventArgs e)
+ {
+ try
+ {
+ Exception ex = (Exception)e.ExceptionObject;
+ if (ex is ArgumentOutOfRangeException && ex.Message == "Specified argument was out of the range of valid values.\r\nParameter name: button")
+ {
+ //If a joystick with an excessive number of axis or buttons is connected, at the least show a nice error message, rather than simply dissapearing
+ MessageBox.Show("An unsupported joystick is connected: \n \n Too many buttons. \n \n Please unplug all USB joysticks & gamepads and try again.");
+ Environment.Exit(0);
+ }
+ if (ex is ArgumentOutOfRangeException && ex.Message == "Specified argument was out of the range of valid values.\r\nParameter name: axis")
+ {
+ //If a joystick with an excessive number of axis or buttons is connected, at the least show a nice error message, rather than simply dissapearing
+ MessageBox.Show("An unsupported joystick is connected: \n \n Too many axis. \n \n Please unplug all USB joysticks & gamepads and try again.");
+ Environment.Exit(0);
+ }
+ MessageBox.Show("Unhandled exception:\n\n" + ex.Message);
+ LogCrash(ex + Environment.StackTrace);
- }
- catch (Exception exc)
- {
- try
- {
- MessageBox.Show("A fatal exception occured inside the UnhandledExceptionHandler: \n\n"
- + exc.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Stop);
- LogCrash(exc + Environment.StackTrace);
- }
- finally
- {
- Environment.Exit(0);
- }
- }
- }
+ }
+ catch (Exception exc)
+ {
+ try
+ {
+ MessageBox.Show("A fatal exception occured inside the UnhandledExceptionHandler: \n\n"
+ + exc.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Stop);
+ LogCrash(exc + Environment.StackTrace);
+ }
+ finally
+ {
+ Environment.Exit(0);
+ }
+ }
+ }
- /// Catches all unhandled exceptions within the current UI thread
- internal static void UIThreadException(object sender, ThreadExceptionEventArgs t)
- {
- try
- {
- MessageBox.Show("Unhandled Windows Forms Exception");
- LogCrash(t + Environment.StackTrace);
- }
- catch (Exception exc)
- {
- try
- {
- MessageBox.Show("A fatal exception occured inside the UIThreadException handler",
- "Fatal Windows Forms Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
- LogCrash(exc + Environment.StackTrace);
- }
- finally
- {
- Environment.Exit(0);
- }
- }
- Environment.Exit(0);
- }
+ /// Catches all unhandled exceptions within the current UI thread
+ internal static void UIThreadException(object sender, ThreadExceptionEventArgs t)
+ {
+ try
+ {
+ MessageBox.Show("Unhandled Windows Forms Exception");
+ LogCrash(t + Environment.StackTrace);
+ }
+ catch (Exception exc)
+ {
+ try
+ {
+ MessageBox.Show("A fatal exception occured inside the UIThreadException handler",
+ "Fatal Windows Forms Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
+ LogCrash(exc + Environment.StackTrace);
+ }
+ finally
+ {
+ Environment.Exit(0);
+ }
+ }
+ Environment.Exit(0);
+ }
- /// This function logs an unhandled crash to disk
- internal static void LogCrash(string ExceptionText)
- {
+ /// This function logs an unhandled crash to disk
+ internal static void LogCrash(string ExceptionText)
+ {
Program.AppendToLogFile("WARNING: Program crashing. Creating CrashLog file: " + CrashLog);
using (StreamWriter outputFile = new StreamWriter(CrashLog))
- {
- //Basic information
- outputFile.WriteLine(DateTime.Now);
- outputFile.WriteLine("OpenBVE " + Application.ProductVersion + " Crash Log");
- var Platform = "Unknown";
- if (OpenTK.Configuration.RunningOnWindows)
- {
- Platform = "Windows";
- }
- else if (OpenTK.Configuration.RunningOnLinux)
- {
- Platform = "Linux";
- }
- else if (OpenTK.Configuration.RunningOnMacOS)
- {
- Platform = "MacOS";
- }
- else if (OpenTK.Configuration.RunningOnSdl2)
- {
- Platform = "SDL2";
- }
- outputFile.WriteLine("Program is running on the " + Platform + " backend");
- if (Interface.CurrentOptions.FullscreenMode)
- {
- outputFile.WriteLine("Current screen resolution is: Full-screen " + Interface.CurrentOptions.FullscreenWidth + "px x " + Interface.CurrentOptions.FullscreenHeight + "px " + Interface.CurrentOptions.FullscreenBits + "bit color-mode");
- }
- else
- {
- outputFile.WriteLine("Current screen resolution is: Windowed " + Interface.CurrentOptions.WindowWidth + "px x " + Interface.CurrentOptions.WindowHeight + "px ");
- }
- //Route and train
- if (Game.RouteInformation.RouteFile != null)
- {
- outputFile.WriteLine("Current routefile is: " + Game.RouteInformation.RouteFile);
- }
- if (Game.RouteInformation.TrainFolder != null)
- {
- outputFile.WriteLine("Current train is: " + Game.RouteInformation.TrainFolder);
- }
- if (TrainManager.PlayerTrain != null)
- {
- outputFile.WriteLine("Current train plugin is: " + TrainManager.PlayerTrain.Plugin.PluginTitle);
- }
- //Errors and Warnings
- if (Game.RouteInformation.FilesNotFound != null)
- {
- outputFile.WriteLine(Game.RouteInformation.FilesNotFound);
- }
- if (Game.RouteInformation.ErrorsAndWarnings != null)
- {
- outputFile.WriteLine(Game.RouteInformation.ErrorsAndWarnings);
- }
- //Track position and viewing distance
- outputFile.WriteLine("Current track position is: " + World.CameraTrackFollower.TrackPosition.ToString("0.00", Culture) + " m");
- outputFile.WriteLine("Current viewing distance is: " + Interface.CurrentOptions.ViewingDistance);
- outputFile.WriteLine("The exception caught was as follows: ");
- outputFile.WriteLine(ExceptionText);
- double MemoryUsed;
- using (Process proc = Process.GetCurrentProcess())
- {
- MemoryUsed = proc.PrivateMemorySize64;
- }
- outputFile.WriteLine("Current program memory usage: " + Math.Round((MemoryUsed / 1024 / 1024), 2) + "mb");
- var freeRamCounter = new PerformanceCounter("Memory", "Available MBytes");
- outputFile.WriteLine("System memory free: " + freeRamCounter.NextValue() + "mb");
- }
+ {
+ //Basic information
+ outputFile.WriteLine(DateTime.Now);
+ outputFile.WriteLine("OpenBVE " + Application.ProductVersion + " Crash Log");
+ var Platform = "Unknown";
+ if (OpenTK.Configuration.RunningOnWindows)
+ {
+ Platform = "Windows";
+ }
+ else if (OpenTK.Configuration.RunningOnLinux)
+ {
+ Platform = "Linux";
+ }
+ else if (OpenTK.Configuration.RunningOnMacOS)
+ {
+ Platform = "MacOS";
+ }
+ else if (OpenTK.Configuration.RunningOnSdl2)
+ {
+ Platform = "SDL2";
+ }
+ outputFile.WriteLine("Program is running on the " + Platform + " backend");
+ if (Interface.CurrentOptions.FullscreenMode)
+ {
+ outputFile.WriteLine("Current screen resolution is: Full-screen " + Interface.CurrentOptions.FullscreenWidth + "px x " + Interface.CurrentOptions.FullscreenHeight + "px " + Interface.CurrentOptions.FullscreenBits + "bit color-mode");
+ }
+ else
+ {
+ outputFile.WriteLine("Current screen resolution is: Windowed " + Interface.CurrentOptions.WindowWidth + "px x " + Interface.CurrentOptions.WindowHeight + "px ");
+ }
+ //Route and train
+ if (Game.RouteInformation.RouteFile != null)
+ {
+ outputFile.WriteLine("Current routefile is: " + Game.RouteInformation.RouteFile);
+ }
+ if (Game.RouteInformation.TrainFolder != null)
+ {
+ outputFile.WriteLine("Current train is: " + Game.RouteInformation.TrainFolder);
+ }
+ if (TrainManager.PlayerTrain != null)
+ {
+ outputFile.WriteLine("Current train plugin is: " + TrainManager.PlayerTrain.Plugin.PluginTitle);
+ }
+ //Errors and Warnings
+ if (Game.RouteInformation.FilesNotFound != null)
+ {
+ outputFile.WriteLine(Game.RouteInformation.FilesNotFound);
+ }
+ if (Game.RouteInformation.ErrorsAndWarnings != null)
+ {
+ outputFile.WriteLine(Game.RouteInformation.ErrorsAndWarnings);
+ }
+ //Track position and viewing distance
+ outputFile.WriteLine("Current track position is: " + World.CameraTrackFollower.TrackPosition.ToString("0.00", Culture) + " m");
+ outputFile.WriteLine("Current viewing distance is: " + Interface.CurrentOptions.ViewingDistance);
+ outputFile.WriteLine("The exception caught was as follows: ");
+ outputFile.WriteLine(ExceptionText);
+ double MemoryUsed;
+ using (Process proc = Process.GetCurrentProcess())
+ {
+ MemoryUsed = proc.PrivateMemorySize64;
+ }
+ outputFile.WriteLine("Current program memory usage: " + Math.Round((MemoryUsed / 1024 / 1024), 2) + "mb");
+ var freeRamCounter = new PerformanceCounter("Memory", "Available MBytes");
+ outputFile.WriteLine("System memory free: " + freeRamCounter.NextValue() + "mb");
+ }
- }
+ }
- /// This function logs an exception caught whilst loading a route/ train to disk
- internal static void LoadingCrash(string ExceptionText, bool Train)
- {
+ /// This function logs an unhandled plugin crash to disk
+ internal static void PluginCrash(string ExceptionText)
+ {
+
+ Program.AppendToLogFile("WARNING: Train plugin " + TrainManager.PlayerTrain.Plugin.PluginTitle + " crashed. Creating CrashLog file: " + CrashLog);
+ MessageBox.Show("Train plugin " + TrainManager.PlayerTrain.Plugin.PluginTitle + " crashed and has been unloaded. \r\n Some train features may no longer work.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
+ using (StreamWriter outputFile = new StreamWriter(CrashLog))
+ {
+ //Basic information
+ outputFile.WriteLine(DateTime.Now);
+ outputFile.WriteLine("OpenBVE " + Application.ProductVersion + " Crash Log");
+ var Platform = "Unknown";
+ if (OpenTK.Configuration.RunningOnWindows)
+ {
+ Platform = "Windows";
+ }
+ else if (OpenTK.Configuration.RunningOnLinux)
+ {
+ Platform = "Linux";
+ }
+ else if (OpenTK.Configuration.RunningOnMacOS)
+ {
+ Platform = "MacOS";
+ }
+ else if (OpenTK.Configuration.RunningOnSdl2)
+ {
+ Platform = "SDL2";
+ }
+ outputFile.WriteLine("Program is running on the " + Platform + " backend");
+ if (Interface.CurrentOptions.FullscreenMode)
+ {
+ outputFile.WriteLine("Current screen resolution is: Full-screen " + Interface.CurrentOptions.FullscreenWidth + "px x " + Interface.CurrentOptions.FullscreenHeight + "px " + Interface.CurrentOptions.FullscreenBits + "bit color-mode");
+ }
+ else
+ {
+ outputFile.WriteLine("Current screen resolution is: Windowed " + Interface.CurrentOptions.WindowWidth + "px x " + Interface.CurrentOptions.WindowHeight + "px ");
+ }
+ //Route and train
+ if (Game.RouteInformation.RouteFile != null)
+ {
+ outputFile.WriteLine("Current routefile is: " + Game.RouteInformation.RouteFile);
+ }
+ if (Game.RouteInformation.TrainFolder != null)
+ {
+ outputFile.WriteLine("Current train is: " + Game.RouteInformation.TrainFolder);
+ }
+ if (TrainManager.PlayerTrain != null)
+ {
+ outputFile.WriteLine("Current train plugin is: " + TrainManager.PlayerTrain.Plugin.PluginTitle);
+ }
+ //Errors and Warnings
+ if (Game.RouteInformation.FilesNotFound != null)
+ {
+ outputFile.WriteLine(Game.RouteInformation.FilesNotFound);
+ }
+ if (Game.RouteInformation.ErrorsAndWarnings != null)
+ {
+ outputFile.WriteLine(Game.RouteInformation.ErrorsAndWarnings);
+ }
+ //Track position and viewing distance
+ outputFile.WriteLine("Current track position is: " + World.CameraTrackFollower.TrackPosition.ToString("0.00", Culture) + " m");
+ outputFile.WriteLine("Current viewing distance is: " + Interface.CurrentOptions.ViewingDistance);
+ outputFile.WriteLine("The train plugin raised the following exception, and was unloaded: ");
+ outputFile.WriteLine(ExceptionText);
+ double MemoryUsed;
+ using (Process proc = Process.GetCurrentProcess())
+ {
+ MemoryUsed = proc.PrivateMemorySize64;
+ }
+ outputFile.WriteLine("Current program memory usage: " + Math.Round((MemoryUsed / 1024 / 1024), 2) + "mb");
+ var freeRamCounter = new PerformanceCounter("Memory", "Available MBytes");
+ outputFile.WriteLine("System memory free: " + freeRamCounter.NextValue() + "mb");
+ }
+
+ }
+
+ /// This function logs an exception caught whilst loading a route/ train to disk
+ internal static void LoadingCrash(string ExceptionText, bool Train)
+ {
Program.AppendToLogFile("WARNING: Program crashing. Creating CrashLog file: " + CrashLog);
using (StreamWriter outputFile = new StreamWriter(CrashLog))
- {
- //Basic information
- outputFile.WriteLine(DateTime.Now);
- outputFile.WriteLine("OpenBVE " + Application.ProductVersion + " Crash Log");
- var Platform = "Unknown";
- if (OpenTK.Configuration.RunningOnWindows)
- {
- Platform = "Windows";
- }
- else if (OpenTK.Configuration.RunningOnLinux)
- {
- Platform = "Linux";
- }
- else if (OpenTK.Configuration.RunningOnMacOS)
- {
- Platform = "MacOS";
- }
- else if (OpenTK.Configuration.RunningOnSdl2)
- {
- Platform = "SDL2";
- }
- outputFile.WriteLine("Program is running on the " + Platform + " backend");
- if (Interface.CurrentOptions.FullscreenMode)
- {
- outputFile.WriteLine("Current screen resolution is: Full-screen " + Interface.CurrentOptions.FullscreenWidth + "px X " + Interface.CurrentOptions.FullscreenHeight + "px " + Interface.CurrentOptions.FullscreenBits + "bit color-mode");
- }
- else
- {
- outputFile.WriteLine("Current screen resolution is: Windowed " + Interface.CurrentOptions.WindowWidth + "px X " + Interface.CurrentOptions.WindowHeight + "px ");
- }
- //Route and train information
- try
- {
- //We need the try/ catch block in order to catch errors which may have occured before initing the current route, train or plugin
- //These may occur if we feed dud data to the sim
- outputFile.WriteLine("Current routefile is: " + Game.RouteInformation.RouteFile);
- outputFile.WriteLine("Current train is: " + Game.RouteInformation.TrainFolder);
- outputFile.WriteLine("Current train plugin is: " + TrainManager.PlayerTrain.Plugin.PluginTitle);
- }
- catch
- {
- }
- //Errors and Warnings
- if (Game.RouteInformation.FilesNotFound != null)
- {
- outputFile.WriteLine(Game.RouteInformation.FilesNotFound);
- }
- if (Game.RouteInformation.ErrorsAndWarnings != null)
- {
- outputFile.WriteLine(Game.RouteInformation.ErrorsAndWarnings);
- }
- if (Train)
- {
- outputFile.WriteLine("The current train plugin caused the following exception: ");
- }
- else
- {
- outputFile.WriteLine("The current routefile caused the following exception: ");
- }
- outputFile.WriteLine(ExceptionText);
- double MemoryUsed;
- using (Process proc = Process.GetCurrentProcess())
- {
- MemoryUsed = proc.PrivateMemorySize64;
- }
- outputFile.WriteLine("Current program memory usage: " + Math.Round((MemoryUsed / 1024 / 1024),2) + "mb");
- var freeRamCounter = new PerformanceCounter("Memory", "Available MBytes");
- outputFile.WriteLine("System memory free: " + freeRamCounter.NextValue() + "mb");
- }
+ {
+ //Basic information
+ outputFile.WriteLine(DateTime.Now);
+ outputFile.WriteLine("OpenBVE " + Application.ProductVersion + " Crash Log");
+ var Platform = "Unknown";
+ if (OpenTK.Configuration.RunningOnWindows)
+ {
+ Platform = "Windows";
+ }
+ else if (OpenTK.Configuration.RunningOnLinux)
+ {
+ Platform = "Linux";
+ }
+ else if (OpenTK.Configuration.RunningOnMacOS)
+ {
+ Platform = "MacOS";
+ }
+ else if (OpenTK.Configuration.RunningOnSdl2)
+ {
+ Platform = "SDL2";
+ }
+ outputFile.WriteLine("Program is running on the " + Platform + " backend");
+ if (Interface.CurrentOptions.FullscreenMode)
+ {
+ outputFile.WriteLine("Current screen resolution is: Full-screen " + Interface.CurrentOptions.FullscreenWidth + "px X " + Interface.CurrentOptions.FullscreenHeight + "px " + Interface.CurrentOptions.FullscreenBits + "bit color-mode");
+ }
+ else
+ {
+ outputFile.WriteLine("Current screen resolution is: Windowed " + Interface.CurrentOptions.WindowWidth + "px X " + Interface.CurrentOptions.WindowHeight + "px ");
+ }
+ //Route and train information
+ try
+ {
+ //We need the try/ catch block in order to catch errors which may have occured before initing the current route, train or plugin
+ //These may occur if we feed dud data to the sim
+ outputFile.WriteLine("Current routefile is: " + Game.RouteInformation.RouteFile);
+ outputFile.WriteLine("Current train is: " + Game.RouteInformation.TrainFolder);
+ outputFile.WriteLine("Current train plugin is: " + TrainManager.PlayerTrain.Plugin.PluginTitle);
+ }
+ catch
+ {
+ }
+ //Errors and Warnings
+ if (Game.RouteInformation.FilesNotFound != null)
+ {
+ outputFile.WriteLine(Game.RouteInformation.FilesNotFound);
+ }
+ if (Game.RouteInformation.ErrorsAndWarnings != null)
+ {
+ outputFile.WriteLine(Game.RouteInformation.ErrorsAndWarnings);
+ }
+ if (Train)
+ {
+ outputFile.WriteLine("The current train plugin caused the following exception: ");
+ }
+ else
+ {
+ outputFile.WriteLine("The current routefile caused the following exception: ");
+ }
+ outputFile.WriteLine(ExceptionText);
+ double MemoryUsed;
+ using (Process proc = Process.GetCurrentProcess())
+ {
+ MemoryUsed = proc.PrivateMemorySize64;
+ }
+ outputFile.WriteLine("Current program memory usage: " + Math.Round((MemoryUsed / 1024 / 1024),2) + "mb");
+ var freeRamCounter = new PerformanceCounter("Memory", "Available MBytes");
+ outputFile.WriteLine("System memory free: " + freeRamCounter.NextValue() + "mb");
+ }
- }
- }
+ }
+ }
}
diff --git a/source/OpenBVE/System/Options.cs b/source/OpenBVE/System/Options.cs
index 4f8582d28b..9be4e9a35c 100644
--- a/source/OpenBVE/System/Options.cs
+++ b/source/OpenBVE/System/Options.cs
@@ -156,6 +156,8 @@ internal class Options
internal bool AllowAxisEB;
/// Whether to prefer the native OpenTK operating system backend
internal bool PreferNativeBackend = true;
+ /// Whether to use compatible plugins on non Win32 systems
+ internal bool UseCompatiblePlugins;
internal TimeTableMode TimeTableStyle;
@@ -229,6 +231,7 @@ internal Options()
this.ProxyPassword = string.Empty;
this.TimeAccelerationFactor = 5;
this.AllowAxisEB = true;
+ this.UseCompatiblePlugins = true;
this.TimeTableStyle = TimeTableMode.Default;
this.packageCompressionType = CompressionType.Zip;
}
@@ -502,6 +505,9 @@ internal static void LoadOptions()
}
Interface.CurrentOptions.TimeAccelerationFactor = tf;
break;
+ case "usecompatibleplugins":
+ Interface.CurrentOptions.UseCompatiblePlugins = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0;
+ break;
} break;
case "controls":
switch (Key)
@@ -770,6 +776,7 @@ internal static void SaveOptions()
default: Builder.AppendLine("normal"); break;
}
Builder.Append("acceleratedtimefactor = " + CurrentOptions.TimeAccelerationFactor);
+ Builder.AppendLine("usecompatibleplugins = " + (CurrentOptions.UseCompatiblePlugins ? "true" : "false"));
Builder.AppendLine();
Builder.AppendLine("[verbosity]");
Builder.AppendLine("showWarningMessages = " + (CurrentOptions.ShowWarningMessages ? "true" : "false"));
diff --git a/source/OpenBVE/System/Program.cs b/source/OpenBVE/System/Program.cs
index a31d3c1b37..e236b41873 100644
--- a/source/OpenBVE/System/Program.cs
+++ b/source/OpenBVE/System/Program.cs
@@ -151,6 +151,9 @@ private static void Main(string[] args) {
//Environment.Exit(0);
}
}
+ //Load lists of blacklisted and compatible replacement plugins
+ PluginManager.LoadBlackListDatabase(OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("PluginDatabase"), "blacklist.xml"));
+ PluginManager.LoadReplacementDatabase(OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("PluginDatabase"), "compatiblereplacements.xml"));
Interface.LoadControls(null, out Interface.CurrentControls);
{
string folder = Program.FileSystem.GetDataFolder("Controls");
diff --git a/source/OpenBVE/UserInterface/formMain.Designer.cs b/source/OpenBVE/UserInterface/formMain.Designer.cs
index 7e0a6884bc..06abf93e1b 100644
--- a/source/OpenBVE/UserInterface/formMain.Designer.cs
+++ b/source/OpenBVE/UserInterface/formMain.Designer.cs
@@ -408,6 +408,8 @@ private void InitializeComponent() {
this.buttonCreatePackage = new System.Windows.Forms.Button();
this.openPackageFileDialog = new System.Windows.Forms.OpenFileDialog();
this.savePackageDialog = new System.Windows.Forms.SaveFileDialog();
+ this.groupBoxCompatability = new System.Windows.Forms.GroupBox();
+ this.checkBoxCompatiblePlugins = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.pictureboxLogo)).BeginInit();
this.panelStart.SuspendLayout();
this.groupboxTrainSelection.SuspendLayout();
@@ -504,6 +506,7 @@ private void InitializeComponent() {
this.splitContainerDependancies.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridViewPackages2)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.dataGridViewPackages3)).BeginInit();
+ this.groupBoxCompatability.SuspendLayout();
this.SuspendLayout();
//
// labelFillerOne
@@ -1341,6 +1344,7 @@ private void InitializeComponent() {
this.panelOptions.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(243)))), ((int)(((byte)(255)))), ((int)(((byte)(243)))));
this.panelOptions.Controls.Add(this.buttonOptionsPrevious);
this.panelOptions.Controls.Add(this.buttonOptionsNext);
+ this.panelOptions.Controls.Add(this.panelOptionsPage2);
this.panelOptions.Controls.Add(this.panelOptionsRight);
this.panelOptions.Controls.Add(this.panelOptionsLeft);
this.panelOptions.Controls.Add(this.pictureboxLanguage);
@@ -1348,7 +1352,6 @@ private void InitializeComponent() {
this.panelOptions.Controls.Add(this.labelOptionsTitleSeparator);
this.panelOptions.Controls.Add(this.labelOptionsTitle);
this.panelOptions.Controls.Add(this.labelOptionsTitleBackground);
- this.panelOptions.Controls.Add(this.panelOptionsPage2);
this.panelOptions.Location = new System.Drawing.Point(160, 0);
this.panelOptions.Name = "panelOptions";
this.panelOptions.Size = new System.Drawing.Size(659, 606);
@@ -2269,6 +2272,7 @@ private void InitializeComponent() {
this.panelOptionsPage2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
+ this.panelOptionsPage2.Controls.Add(this.groupBoxCompatability);
this.panelOptionsPage2.Controls.Add(this.groupBoxPackageOptions);
this.panelOptionsPage2.Location = new System.Drawing.Point(8, 72);
this.panelOptionsPage2.Name = "panelOptionsPage2";
@@ -5167,14 +5171,36 @@ private void InitializeComponent() {
//
this.openPackageFileDialog.FileName = "openFileDialog1";
//
+ // groupBoxCompatability
+ //
+ this.groupBoxCompatability.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.groupBoxCompatability.Controls.Add(this.checkBoxCompatiblePlugins);
+ this.groupBoxCompatability.Location = new System.Drawing.Point(0, 158);
+ this.groupBoxCompatability.Name = "groupBoxCompatability";
+ this.groupBoxCompatability.Size = new System.Drawing.Size(640, 100);
+ this.groupBoxCompatability.TabIndex = 20;
+ this.groupBoxCompatability.TabStop = false;
+ this.groupBoxCompatability.Text = "Compatablilty";
+ //
+ // checkBoxCompatiblePlugins
+ //
+ this.checkBoxCompatiblePlugins.AutoSize = true;
+ this.checkBoxCompatiblePlugins.Location = new System.Drawing.Point(12, 19);
+ this.checkBoxCompatiblePlugins.Name = "checkBoxCompatiblePlugins";
+ this.checkBoxCompatiblePlugins.Size = new System.Drawing.Size(344, 17);
+ this.checkBoxCompatiblePlugins.TabIndex = 0;
+ this.checkBoxCompatiblePlugins.Text = "Use compatible plugins on non-Windows systems (Where available)";
+ this.checkBoxCompatiblePlugins.UseVisualStyleBackColor = true;
+ //
// formMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(819, 606);
- this.Controls.Add(this.panelControls);
this.Controls.Add(this.panelOptions);
+ this.Controls.Add(this.panelControls);
this.Controls.Add(this.labelVerticalSeparator);
this.Controls.Add(this.panelInfo);
this.Controls.Add(this.panelPanels);
@@ -5326,6 +5352,8 @@ private void InitializeComponent() {
this.splitContainerDependancies.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dataGridViewPackages2)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.dataGridViewPackages3)).EndInit();
+ this.groupBoxCompatability.ResumeLayout(false);
+ this.groupBoxCompatability.PerformLayout();
this.ResumeLayout(false);
}
@@ -5716,5 +5744,7 @@ private void InitializeComponent() {
private System.Windows.Forms.Button buttonCancel;
private System.Windows.Forms.Button buttonCancel2;
private System.Windows.Forms.Button buttonAbort;
+ private System.Windows.Forms.GroupBox groupBoxCompatability;
+ private System.Windows.Forms.CheckBox checkBoxCompatiblePlugins;
}
}
\ No newline at end of file
diff --git a/source/OpenBVE/UserInterface/formMain.Options.cs b/source/OpenBVE/UserInterface/formMain.Options.cs
index 893ac51433..1d9e2767fb 100644
--- a/source/OpenBVE/UserInterface/formMain.Options.cs
+++ b/source/OpenBVE/UserInterface/formMain.Options.cs
@@ -74,8 +74,6 @@ private void trackBarTimeAccelerationFactor_ValueChanged(object sender, EventArg
private void checkboxJoysticksUsed_CheckedChanged(object sender, EventArgs e) {
groupboxJoysticks.Enabled = checkboxJoysticksUsed.Checked;
}
-
-
}
}
\ No newline at end of file
diff --git a/source/OpenBVE/UserInterface/formMain.Packages.cs b/source/OpenBVE/UserInterface/formMain.Packages.cs
index 9d26bec0b0..8983a5e563 100644
--- a/source/OpenBVE/UserInterface/formMain.Packages.cs
+++ b/source/OpenBVE/UserInterface/formMain.Packages.cs
@@ -9,6 +9,7 @@
using System.Windows.Forms;
using OpenBveApi.Packages;
using System.Text;
+using System.Xml;
namespace OpenBve
{
@@ -265,6 +266,9 @@ private void Extract(Package packageToReplace = null)
case PackageType.Train:
ExtractionDirectory = Program.FileSystem.TrainInstallationDirectory;
break;
+ case PackageType.CompatibilityData:
+ ExtractionDirectory = Program.FileSystem.DataFolder;
+ break;
default:
ExtractionDirectory = Program.FileSystem.OtherInstallationDirectory;
break;
@@ -324,6 +328,34 @@ private void Extract(Package packageToReplace = null)
}
}
Database.currentDatabase.InstalledOther.Add(currentPackage);
+ if (currentPackage.PackageType == PackageType.CompatibilityData)
+ {
+ //Compatibility data is not installed in a user selected folder, but should show in the 'Other' list
+ //We now need to add our new compatibility XML file to the compatibility database
+ if (!string.IsNullOrEmpty(currentPackage.CompatabilityXml))
+ {
+ //TODO: Handle objects
+ string xmlFile = OpenBveApi.Path.CombineFile(System.IO.Path.GetTempPath(), currentPackage.CompatabilityXml);
+ if (!System.IO.File.Exists(xmlFile))
+ {
+ break;
+ }
+ XmlDocument currentXML = new XmlDocument();
+ currentXML.Load(xmlFile);
+ XmlNodeList nodes = currentXML.SelectNodes("/openBVE/TrainPlugins/Replacements");
+ if (nodes != null && nodes.Count > 0)
+ {
+ PluginManager.LoadReplacementDatabase(currentXML);
+ PluginManager.WriteReplacementDatabase();
+ }
+ nodes = currentXML.SelectNodes("/openBVE/TrainPlugins/Blacklist");
+ if(nodes != null && nodes.Count > 0)
+ {
+ PluginManager.LoadBlackListDatabase(currentXML);
+ PluginManager.WriteBlackListDatabase();
+ }
+ }
+ }
break;
}
labelInstallSuccess1.Text = Interface.GetInterfaceString("packages_install_success");
@@ -485,7 +517,17 @@ internal void UninstallPackage(Package packageToUninstall, ref List Pac
HidePanels();
panelDependancyError.Show();
}
- if (Manipulation.UninstallPackage(packageToUninstall, currentDatabaseFolder, ref uninstallResults))
+ bool uninstallOK;
+ XmlDocument xmlToUninstall = null;
+ if (packageToUninstall.PackageType == PackageType.CompatibilityData)
+ {
+ uninstallOK = Manipulation.UninstallPackage(packageToUninstall, currentDatabaseFolder, out uninstallResults, out xmlToUninstall);
+ }
+ else
+ {
+ uninstallOK = Manipulation.UninstallPackage(packageToUninstall, currentDatabaseFolder, ref uninstallResults);
+ }
+ if (uninstallOK)
{
Packages.Remove(packageToUninstall);
switch (packageToUninstall.PackageType)
@@ -499,6 +541,30 @@ internal void UninstallPackage(Package packageToUninstall, ref List Pac
case PackageType.Train:
DatabaseFunctions.cleanDirectory(Program.FileSystem.TrainInstallationDirectory, ref uninstallResults);
break;
+ case PackageType.CompatibilityData:
+ DatabaseFunctions.cleanDirectory(Program.FileSystem.DataFolder, ref uninstallResults);
+ if (xmlToUninstall != null)
+ {
+ XmlNodeList nodes = xmlToUninstall.SelectNodes("/openBVE/TrainPlugins/Blacklist");
+ if (nodes != null && nodes.Count > 0)
+ {
+ for (int i = 0; i < nodes.Count; i++)
+ {
+ PluginManager.RemoveBlackListEntry(PluginManager.ParseBlackListEntry(nodes[i]));
+ }
+ }
+ PluginManager.WriteBlackListDatabase();
+ nodes = xmlToUninstall.SelectNodes("/openBVE/TrainPlugins/Replacements");
+ if (nodes != null && nodes.Count > 0)
+ {
+ for (int i = 0; i < nodes.Count; i++)
+ {
+ PluginManager.RemoveReplacementPlugin(PluginManager.ParseReplacementPlugin(nodes[i]));
+ }
+ }
+ PluginManager.WriteReplacementDatabase();
+ }
+ break;
}
labelUninstallSuccess.Text = Interface.GetInterfaceString("packages_uninstall_success");
labelUninstallSuccessHeader.Text = Interface.GetInterfaceString("packages_uninstall_success_header");
@@ -588,6 +654,7 @@ private void buttonUninstallPackage_Click(object sender, EventArgs e)
case PackageType.Train:
UninstallPackage(currentPackage, ref Database.currentDatabase.InstalledTrains);
break;
+ case PackageType.CompatibilityData:
case PackageType.Other:
UninstallPackage(currentPackage, ref Database.currentDatabase.InstalledOther);
break;
@@ -616,6 +683,7 @@ private void buttonUninstallFinish_Click(object sender, EventArgs e)
{
switch (currentPackage.PackageType)
{
+ case PackageType.CompatibilityData:
case PackageType.Other:
Database.currentDatabase.InstalledOther.Remove(currentPackage);
break;
@@ -1424,51 +1492,46 @@ private void comboBoxDependancyType_SelectedIndexChanged(object sender, EventArg
private void buttonProceedAnyway1_Click(object sender, EventArgs e)
{
HidePanels();
- if (radioButtonOverwrite.Checked)
- {
- //Plain overwrite
- Extract();
- }
- else if (radioButtonReplace.Checked)
+ string result = String.Empty;
+ if (radioButtonReplace.Checked)
{
//Reinstall
- string result = String.Empty;
Manipulation.UninstallPackage(currentPackage, currentDatabaseFolder, ref result);
- switch (currentPackage.PackageType)
- {
- case PackageType.Route:
- for (int i = Database.currentDatabase.InstalledRoutes.Count -1; i >= 0; i--)
+ }
+ switch (currentPackage.PackageType)
+ {
+ case PackageType.Route:
+ for (int i = Database.currentDatabase.InstalledRoutes.Count - 1; i >= 0; i--)
+ {
+ if (Database.currentDatabase.InstalledRoutes[i].GUID == currentPackage.GUID)
{
- if (Database.currentDatabase.InstalledRoutes[i].GUID == currentPackage.GUID)
- {
- Database.currentDatabase.InstalledRoutes.RemoveAt(i);
- }
+ Database.currentDatabase.InstalledRoutes.RemoveAt(i);
}
- DatabaseFunctions.cleanDirectory(Program.FileSystem.RouteInstallationDirectory, ref result);
- break;
- case PackageType.Train:
- for (int i = Database.currentDatabase.InstalledTrains.Count - 1; i >= 0; i--)
+ }
+ DatabaseFunctions.cleanDirectory(Program.FileSystem.RouteInstallationDirectory, ref result);
+ break;
+ case PackageType.Train:
+ for (int i = Database.currentDatabase.InstalledTrains.Count - 1; i >= 0; i--)
+ {
+ if (Database.currentDatabase.InstalledTrains[i].GUID == currentPackage.GUID)
{
- if (Database.currentDatabase.InstalledTrains[i].GUID == currentPackage.GUID)
- {
- Database.currentDatabase.InstalledTrains.RemoveAt(i);
- }
+ Database.currentDatabase.InstalledTrains.RemoveAt(i);
}
- DatabaseFunctions.cleanDirectory(Program.FileSystem.TrainInstallationDirectory, ref result);
- break;
- case PackageType.Other:
- for (int i = Database.currentDatabase.InstalledOther.Count - 1; i >= 0; i--)
+ }
+ DatabaseFunctions.cleanDirectory(Program.FileSystem.TrainInstallationDirectory, ref result);
+ break;
+ case PackageType.CompatibilityData:
+ case PackageType.Other:
+ for (int i = Database.currentDatabase.InstalledOther.Count - 1; i >= 0; i--)
+ {
+ if (Database.currentDatabase.InstalledOther[i].GUID == currentPackage.GUID)
{
- if (Database.currentDatabase.InstalledOther[i].GUID == currentPackage.GUID)
- {
- Database.currentDatabase.InstalledOther.RemoveAt(i);
- }
+ Database.currentDatabase.InstalledOther.RemoveAt(i);
}
- break;
- }
- Extract();
+ }
+ break;
}
-
+ Extract();
}
private void dataGridViewDependancies_CellContentClick(object sender, DataGridViewCellEventArgs e)
diff --git a/source/OpenBVE/UserInterface/formMain.cs b/source/OpenBVE/UserInterface/formMain.cs
index f2acb840e4..f2c949d4a4 100644
--- a/source/OpenBVE/UserInterface/formMain.cs
+++ b/source/OpenBVE/UserInterface/formMain.cs
@@ -876,6 +876,7 @@ private void formMain_FormClosing(object sender, FormClosingEventArgs e)
Interface.CurrentOptions.TrainFolder = textboxTrainFolder.Text;
Interface.CurrentOptions.MainMenuWidth = this.WindowState == FormWindowState.Maximized ? -1 : this.Size.Width;
Interface.CurrentOptions.MainMenuHeight = this.WindowState == FormWindowState.Maximized ? -1 : this.Size.Height;
+ Interface.CurrentOptions.UseCompatiblePlugins = checkBoxCompatiblePlugins.Checked;
if (Result.Start)
{
// recently used routes
diff --git a/source/OpenBveApi/Packages.cs b/source/OpenBveApi/Packages.cs
index 54c3f190e5..cacd1b0ad5 100644
--- a/source/OpenBveApi/Packages.cs
+++ b/source/OpenBveApi/Packages.cs
@@ -63,6 +63,9 @@ public string Version
/// The image for this package
[XmlIgnore]
public Image PackageImage;
+ /// The compatibility XML file
+ /// (If a CompatibilityData package)
+ public string CompatabilityXml;
/// The list of dependancies for this package
public List Dependancies;
/// The list of packages that this package reccomends you also install
@@ -170,6 +173,8 @@ public enum PackageType
Train = 2,
/// The package is a route, utility etc.
Other = 3,
+ /// The package contains compatibility data.
+ CompatibilityData = 5
}
/// Holds the properties of a file, used during creation of a package.
@@ -206,6 +211,7 @@ public static void ExtractPackage(Package currentPackage, string extractionDirec
int i = 0;
int j = 0;
string fp = String.Empty;
+ XmlDocument uninstallXML = new XmlDocument();
try
{
using (Stream stream = File.OpenRead(currentPackage.PackageFile))
@@ -226,6 +232,18 @@ public static void ExtractPackage(Package currentPackage, string extractionDirec
{
//Skip zero-byte files
}
+ else if(archiveEntry.Key.ToLowerInvariant() == currentPackage.CompatabilityXml)
+ {
+ archiveEntry.WriteToDirectory(System.IO.Path.GetTempPath());
+ try
+ {
+ //Load the compatibility data for uninstall purposes
+ uninstallXML.Load(Path.CombineFile(System.IO.Path.GetTempPath(), currentPackage.CompatabilityXml));
+ }
+ catch
+ {
+ }
+ }
else
{
//Extract everything else, preserving directory structure
@@ -246,7 +264,7 @@ public static void ExtractPackage(Package currentPackage, string extractionDirec
Text += FileName + "\r\n";
}
packageFiles = Text;
- //Write out the package file list
+ //Write out the uninstall XML file
var fileListDirectory = OpenBveApi.Path.CombineDirectory(databaseFolder, "Installed");
if (!Directory.Exists(fileListDirectory))
{
@@ -255,8 +273,30 @@ public static void ExtractPackage(Package currentPackage, string extractionDirec
var fileList = OpenBveApi.Path.CombineFile(fileListDirectory, currentPackage.GUID.ToUpper() + ".xml");
using (StreamWriter sw = new StreamWriter(fileList))
{
- XmlSerializer listWriter = new XmlSerializer(typeof(List));
- listWriter.Serialize(sw, PackageFiles);
+ var currentXML = new XmlDocument();
+ XmlElement rootElement = (XmlElement)currentXML.AppendChild(currentXML.CreateElement("openBVE"));
+ XmlElement firstElement = (XmlElement)rootElement.AppendChild(currentXML.CreateElement("UninstallData"));
+ XmlElement secondElement = (XmlElement)firstElement.AppendChild(currentXML.CreateElement("FilesInstalled"));
+ for (int k = 0; k < PackageFiles.Count; k++)
+ {
+ secondElement.AppendChild(currentXML.CreateElement("FileName")).InnerText = PackageFiles[k];
+ }
+ try
+ {
+ XmlNodeList nodes = uninstallXML.SelectNodes("/openBVE/TrainPlugins");
+ if (nodes != null)
+ {
+ for (int k = 0; k < nodes.Count; k++)
+ {
+ firstElement.AppendChild(firstElement.OwnerDocument.ImportNode(nodes[k], true));
+ }
+ }
+ }
+ catch
+ {
+ }
+
+ currentXML.Save(sw);
}
}
@@ -366,11 +406,123 @@ public static bool UninstallPackage(Package currentPackage, string databaseFolde
//The list of files installed by this package is missing
return false;
}
- XmlSerializer listReader = new XmlSerializer(typeof(List));
List filesToDelete;
using (FileStream readFileStream = new FileStream(fileList, FileMode.Open, FileAccess.Read, FileShare.Read))
{
- filesToDelete = (List)listReader.Deserialize(readFileStream);
+
+ XmlDocument uninstallXml = new XmlDocument();
+ uninstallXml.Load(readFileStream);
+ //Original format, just a serialized array....
+ XmlNodeList nodes = uninstallXml.SelectNodes("/ArrayOfString");
+ filesToDelete = new List();
+ if (nodes != null)
+ {
+ for (int i = 0; i < nodes.Count; i++)
+ {
+ filesToDelete.Add(nodes[i].InnerText);
+ }
+ }
+ //Format V2, better formed XML and allows us to uninstall compatibility items
+ nodes = uninstallXml.SelectNodes("/openBVE/UninstallData/FilesInstalled/FileName");
+ if (nodes != null)
+ {
+ for (int i = 0; i < nodes.Count; i++)
+ {
+ filesToDelete.Add(nodes[i].InnerText);
+ }
+ }
+ }
+ File.Delete(fileList);
+ bool noErrors = true;
+ int errorCount = 0;
+ int deletionCount = 0;
+ string Result = "";
+ foreach (var String in filesToDelete)
+ {
+ try
+ {
+ File.Delete(String);
+ Result += String + " deleted successfully. \r\n ";
+ deletionCount++;
+ }
+ catch (Exception ex)
+ {
+ //We have caught an error....
+ //Set the return type to false, and add the exception to the results string
+ noErrors = false;
+ Result += String + "\r\n";
+ Result += ex.Message + "\r\n";
+ errorCount++;
+ }
+ }
+ //Set the final results string to display
+ PackageFiles = deletionCount + " files deleted successfully. \r\n" + errorCount + " errors were encountered. \r\n \r\n \r\n" + Result;
+ return noErrors;
+ }
+
+ /// Uninstalls a package
+ /// The package to uninstall
+ /// The package database folder
+ /// Returns via 'ref' a list of files uninstalled
+ /// A list of nodes to be uninstalled from the compatibility database
+ /// True if uninstall succeeded with no errors, false otherwise
+ public static bool UninstallPackage(Package currentPackage, string databaseFolder, out string PackageFiles, out XmlDocument uninstallNodes)
+ {
+ var fileList = OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(databaseFolder, "Installed"), currentPackage.GUID.ToUpper() + ".xml");
+ if (!File.Exists(fileList))
+ {
+ PackageFiles = null;
+ uninstallNodes = null;
+ //The list of files installed by this package is missing
+ return false;
+ }
+ List filesToDelete;
+ uninstallNodes = null;
+ using (FileStream readFileStream = new FileStream(fileList, FileMode.Open, FileAccess.Read, FileShare.Read))
+ {
+
+ XmlDocument uninstallXml = new XmlDocument();
+ uninstallXml.Load(readFileStream);
+ //Original format, just a serialized array....
+ XmlNodeList nodes = uninstallXml.SelectNodes("/ArrayOfString");
+ filesToDelete = new List();
+ if (nodes != null)
+ {
+ for (int i = 0; i < nodes.Count; i++)
+ {
+ filesToDelete.Add(nodes[i].InnerText);
+ }
+ }
+ //Format V2, better formed XML and allows us to uninstall compatibility items
+ nodes = uninstallXml.SelectNodes("/openBVE/UninstallData/FilesInstalled/FileName");
+ if (nodes != null)
+ {
+ for (int i = 0; i < nodes.Count; i++)
+ {
+ filesToDelete.Add(nodes[i].InnerText);
+ }
+ }
+ //Now select any train plugin data
+ nodes = uninstallXml.SelectNodes("/openBVE/UninstallData/TrainPlugins");
+ uninstallNodes = new XmlDocument();
+ XmlElement rootElement = (XmlElement)uninstallNodes.AppendChild(uninstallNodes.CreateElement("openBVE"));
+ //XmlElement firstElement = (XmlElement)rootElement.AppendChild(uninstallNodes.CreateElement("TrainPlugins"));
+ if (nodes != null)
+ {
+ for (int i = 0; i < nodes.Count; i++)
+ {
+ rootElement.AppendChild(rootElement.OwnerDocument.ImportNode(nodes[i], true));
+ }
+ }
+ //Finally, compatbility object data
+ nodes = uninstallXml.SelectNodes("/openBVE/UninstallData/Compatibility");
+ if (nodes != null)
+ {
+ for (int i = 0; i < nodes.Count; i++)
+ {
+ rootElement.AppendChild(rootElement.OwnerDocument.ImportNode(nodes[i], true));
+ }
+ }
}
File.Delete(fileList);
bool noErrors = true;
@@ -556,7 +708,7 @@ public static VersionInformation CheckVersion(Package currentPackage, List