1 // ╔═════════════════════════════════════════════════════════════╗
2 // ║ Loading.cs for the Route Viewer                             ║
3 // ╠═════════════════════════════════════════════════════════════╣
4 // ║ This file cannot be used in the openBVE main program.       ║
5 // ║ The file from the openBVE main program cannot be used here. ║
6 // ╚═════════════════════════════════════════════════════════════╝
7 
8 using System;
9 using System.Drawing;
10 using System.Linq;
11 using System.Text;
12 using System.Threading.Tasks;
13 using System.Windows.Forms;
14 using LibRender2.Cameras;
15 using OpenBveApi.Math;
16 using OpenBveApi.Routes;
17 using OpenBveApi.Runtime;
18 using OpenBveApi.Textures;
19 using RouteManager2;
20 
21 namespace RouteViewer {
22 	internal static class Loading {
23 
24 		internal static bool Cancel
25 		{
26 			get
27 			{
28 				return _cancel;
29 			}
30 			set
31 			{
32 				if (value)
33 				{
34 					//Send cancellation call to plugins
35 					for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++)
36 					{
37 						if (Program.CurrentHost.Plugins[i].Route != null && Program.CurrentHost.Plugins[i].Route.IsLoading)
38 						{
39 							Program.CurrentHost.Plugins[i].Route.Cancel = true;
40 						}
41 					}
42 					_cancel = true;
43 				}
44 				else
45 				{
46 					_cancel = false;
47 				}
48 			}
49 		}
50 
51 		private static bool _cancel;
52 		internal static bool Complete;
53 		private static string CurrentRouteFile;
54 		private static Encoding CurrentRouteEncoding;
55 
56 		internal static bool JobAvailable;
57 
58 		// load
Load(string RouteFile, Encoding RouteEncoding, Bitmap bitmap = null)59 		internal static void Load(string RouteFile, Encoding RouteEncoding, Bitmap bitmap = null)
60 		{
61 			// reset
62 			Game.Reset();
63 			Program.Renderer.Loading.InitLoading(Program.FileSystem.GetDataFolder("In-game"), typeof(NewRenderer).Assembly.GetName().Version.ToString(), Interface.CurrentOptions.LoadingLogo, Interface.CurrentOptions.LoadingProgressBar);
64 			if (bitmap != null)
65 			{
66 				Program.Renderer.Loading.SetLoadingBkg(Program.Renderer.TextureManager.RegisterTexture(bitmap, new TextureParameters(null, null)));
67 			}
68 			// members
69 			Cancel = false;
70 			Complete = false;
71 			CurrentRouteFile = RouteFile;
72 			CurrentRouteEncoding = RouteEncoding;
73 			// thread
74 			Loading.LoadAsynchronously(CurrentRouteFile, CurrentRouteEncoding);
75 			RouteViewer.LoadingScreenLoop();
76 		}
77 
78 		/// <summary>Gets the absolute Railway folder for a given route file</summary>
79 		/// <returns>The absolute on-disk path of the railway folder</returns>
GetRailwayFolder(string RouteFile)80 		internal static string GetRailwayFolder(string RouteFile) {
81 			try
82 			{
83 				string Folder = System.IO.Path.GetDirectoryName(RouteFile);
84 
85 				while (true)
86 				{
87 					string Subfolder = OpenBveApi.Path.CombineDirectory(Folder, "Railway");
88 					if (System.IO.Directory.Exists(Subfolder))
89 					{
90 						if (System.IO.Directory.EnumerateDirectories(Subfolder).Any() || System.IO.Directory.EnumerateFiles(Subfolder).Any())
91 						{
92 							//HACK: Ignore completely empty directories
93 							//Doesn't handle wrong directories, or those with stuff missing, TODO.....
94 							return Subfolder;
95 						}
96 					}
97 
98 					if (Folder == null) continue;
99 					System.IO.DirectoryInfo Info = System.IO.Directory.GetParent(Folder);
100 					if (Info == null) break;
101 					Folder = Info.FullName;
102 				}
103 			}
104 			catch
105 			{
106 				//ignored
107 			}
108 
109 			//If the Route, Object and Sound folders exist, but are not in a railway folder.....
110 			try
111 			{
112 				string Folder = System.IO.Path.GetDirectoryName(RouteFile);
113 				string candidate = null;
114 				while (true)
115 				{
116 					string RouteFolder = OpenBveApi.Path.CombineDirectory(Folder, "Route");
117 					string ObjectFolder = OpenBveApi.Path.CombineDirectory(Folder, "Object");
118 					string SoundFolder = OpenBveApi.Path.CombineDirectory(Folder, "Sound");
119 					if (System.IO.Directory.Exists(RouteFolder) && System.IO.Directory.Exists(ObjectFolder) && System.IO.Directory.Exists(SoundFolder))
120 					{
121 						return Folder;
122 					}
123 
124 					if (System.IO.Directory.Exists(RouteFolder) && System.IO.Directory.Exists(ObjectFolder))
125 					{
126 						candidate = Folder;
127 					}
128 
129 					// ReSharper disable once AssignNullToNotNullAttribute
130 					System.IO.DirectoryInfo Info = System.IO.Directory.GetParent(Folder);
131 					if (Info == null)
132 					{
133 						if (candidate != null)
134 						{
135 							return candidate;
136 						}
137 						break;
138 					}
139 					Folder = Info.FullName;
140 				}
141 			}
142 			catch
143 			{
144 				//ignored
145 			}
146 			return Application.StartupPath;
147 		}
148 
149 		// load threaded
LoadThreaded()150 		private static async Task LoadThreaded()
151 		{
152 			try
153 			{
154 				await Task.Run(() => LoadEverythingThreaded());
155 			}
156 			catch (Exception ex)
157 			{
158 				MessageBox.Show("The route loader encountered the following critical error: " + ex.Message, @"OpenBVE", MessageBoxButtons.OK, MessageBoxIcon.Hand);
159 				Cancel = true;
160 			}
161 		}
162 
LoadAsynchronously(string RouteFile, Encoding RouteEncoding)163 		internal static void LoadAsynchronously(string RouteFile, Encoding RouteEncoding)
164 		{
165 			// members
166 			Cancel = false;
167 			Complete = false;
168 			CurrentRouteFile = RouteFile;
169 			CurrentRouteEncoding = RouteEncoding;
170 
171 			//Set the route and train folders in the info class
172 			// ReSharper disable once UnusedVariable
173 			Task loadThreaded = LoadThreaded();
174 		}
175 
LoadEverythingThreaded()176 		private static void LoadEverythingThreaded() {
177 			string RailwayFolder = GetRailwayFolder(CurrentRouteFile);
178 			string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object");
179 			string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound");
180 			Program.Renderer.Camera.CurrentMode = CameraViewMode.Track;
181 			// load route
182 			bool loaded = false;
183 			for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++)
184 			{
185 				if (Program.CurrentHost.Plugins[i].Route != null && Program.CurrentHost.Plugins[i].Route.CanLoadRoute(CurrentRouteFile))
186 				{
187 					object Route = (object)Program.CurrentRoute; //must cast to allow us to use the ref keyword.
188 					if (Program.CurrentHost.Plugins[i].Route.LoadRoute(CurrentRouteFile, CurrentRouteEncoding, null, ObjectFolder, SoundFolder, false, ref Route))
189 					{
190 						Program.CurrentRoute = (CurrentRoute) Route;
191 						Program.CurrentRoute.UpdateLighting();
192 						loaded = true;
193 						break;
194 					}
195 					throw Program.CurrentHost.Plugins[i].Route.LastException;
196 				}
197 			}
198 
199 			if (!loaded)
200 			{
201 				throw new Exception("No plugins capable of loading routefile " + CurrentRouteFile + " were found.");
202 			}
203 			Program.Renderer.CameraTrackFollower = new TrackFollower(Program.CurrentHost);
204 			System.Threading.Thread.Sleep(1); if (Cancel) return;
205 			Program.CurrentRoute.Atmosphere.CalculateSeaLevelConstants();
206 			// camera
207 			Program.Renderer.CameraTrackFollower.UpdateAbsolute( 0.0, true, false);
208 			Program.Renderer.CameraTrackFollower.UpdateAbsolute(0.1, true, false);
209 			Program.Renderer.CameraTrackFollower.UpdateAbsolute(-0.1, true, false);
210 			Program.Renderer.CameraTrackFollower.TriggerType = EventTriggerType.Camera;
211 			// default starting time
212 			Game.SecondsSinceMidnight = 0.0;
213 			// finished created objects
214 			System.Threading.Thread.Sleep(1); if (Cancel) return;
215 			// signals
216 			System.Threading.Thread.Sleep(1); if (Cancel) return;
217 			Program.CurrentRoute.UpdateAllSections();
218 			// starting track position
219 			System.Threading.Thread.Sleep(1); if (Cancel) return;
220 			// int FirstStationIndex = -1;
221 			double FirstStationPosition = 0.0;
222 			for (int i = 0; i < Program.CurrentRoute.Stations.Length; i++) {
223 				if (Program.CurrentRoute.Stations[i].Stops.Length != 0) {
224 					// FirstStationIndex = i;
225 					FirstStationPosition = Program.CurrentRoute.Stations[i].Stops[Program.CurrentRoute.Stations[i].Stops.Length - 1].TrackPosition;
226 					if (Program.CurrentRoute.Stations[i].ArrivalTime < 0.0) {
227 						if (Program.CurrentRoute.Stations[i].DepartureTime < 0.0) {
228 							Game.SecondsSinceMidnight = 0.0;
229 						} else {
230 							Game.SecondsSinceMidnight = Program.CurrentRoute.Stations[i].DepartureTime - Program.CurrentRoute.Stations[i].StopTime;
231 						}
232 					} else {
233 						Game.SecondsSinceMidnight = Program.CurrentRoute.Stations[i].ArrivalTime;
234 					}
235 					break;
236 				}
237 			}
238 			// initialize camera
239 			Program.Renderer.CameraTrackFollower.UpdateAbsolute(-1.0, true, false);
240 			Program.Renderer.CameraTrackFollower.UpdateAbsolute(FirstStationPosition, true, false);
241 			Program.Renderer.Camera.Alignment = new CameraAlignment(new Vector3(0.0, 2.5, 0.0), 0.0, 0.0, 0.0, FirstStationPosition, 1.0);
242 			World.UpdateAbsoluteCamera(0.0);
243 			Complete = true;
244 		}
245 
246 	}
247 }
248