1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 using System.IO;
5 using System.Reflection;
6 using System.Windows.Forms;
7 using OpenTK.Input;
8 using OpenBveApi.Interface;
9 using Control = OpenBveApi.Interface.Control;
10 
11 namespace OpenBve
12 {
13 	internal static partial class Interface
14 	{
15 		/// <summary>The list of current in-game controls</summary>
16 		internal static Control[] CurrentControls = { };
17 
18 		/// <summary>Saves a control configuration to disk</summary>
19 		/// <param name="FileOrNull">An absolute file path if we are exporting the controls, or a null reference to save to the default configuration location</param>
20 		/// <param name="controlsToSave">The list of controls to save</param>
SaveControls(string FileOrNull, Control[] controlsToSave)21 		internal static void SaveControls(string FileOrNull, Control[] controlsToSave) {
22 			CultureInfo Culture = CultureInfo.InvariantCulture;
23 			System.Text.StringBuilder Builder = new System.Text.StringBuilder();
24 			Builder.AppendLine("; Current control configuration");
25 			Builder.AppendLine("; =============================");
26 			Builder.AppendLine("; This file was automatically generated. Please modify only if you know what you're doing.");
27 			Builder.AppendLine("; This file is INCOMPATIBLE with versions older than 1.4.4.");
28 			Builder.AppendLine();
29 			for (int i = 0; i < controlsToSave.Length; i++) {
30 				Translations.CommandInfo Info = Translations.CommandInfos.TryGetInfo(controlsToSave[i].Command);
31 				Builder.Append(Info.Name + ", ");
32 				switch (controlsToSave[i].Method) {
33 					case ControlMethod.Keyboard:
34 						Builder.Append("keyboard, " + controlsToSave[i].Key + ", " + ((int)controlsToSave[i].Modifier).ToString(Culture) + ", " + controlsToSave[i].Option.ToString(Culture));
35 						break;
36 					case ControlMethod.Joystick:
37 						Builder.Append("joystick, " + controlsToSave[i].Device + ", ");
38 						switch (controlsToSave[i].Component) {
39 							case JoystickComponent.Axis:
40 								Builder.Append("axis, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture));
41 								break;
42 							case JoystickComponent.Ball:
43 								Builder.Append("ball, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture));
44 								break;
45 							case JoystickComponent.Hat:
46 								Builder.Append("hat, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture));
47 								break;
48 							case JoystickComponent.Button:
49 								Builder.Append("button, " + controlsToSave[i].Element.ToString(Culture));
50 								break;
51 							default:
52 								Builder.Append("invalid");
53 								break;
54 						}
55 						Builder.Append(", " + controlsToSave[i].Option.ToString(Culture));
56 						break;
57 					case ControlMethod.RailDriver:
58 						Builder.Append("raildriver, 0, ");
59 						switch (controlsToSave[i].Component) {
60 							case JoystickComponent.Axis:
61 								Builder.Append("axis, " + controlsToSave[i].Element.ToString(Culture) + ", " + controlsToSave[i].Direction.ToString(Culture));
62 								break;
63 							case JoystickComponent.Button:
64 								Builder.Append("button, " + controlsToSave[i].Element.ToString(Culture));
65 								break;
66 							default:
67 								Builder.Append("invalid");
68 								break;
69 						}
70 						Builder.Append(", " + controlsToSave[i].Option.ToString(Culture));
71 						break;
72 				}
73 				Builder.Append("\n");
74 			}
75 			string File;
76 			if (FileOrNull == null) {
77 				File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "1.5.0/controls.cfg");
78 			} else {
79 				File = FileOrNull;
80 			}
81 			System.IO.File.WriteAllText(File, Builder.ToString(), new System.Text.UTF8Encoding(true));
82 		}
83 
84 		private static bool ControlsReset = false;
85 
GetLines(Stream resourceStream)86 		private static string[] GetLines(Stream resourceStream)
87 		{
88 			List<string> lines = new List<string>();
89 			using (StreamReader reader = new StreamReader(resourceStream))
90 			{
91 				string currentLine;
92 				while ((currentLine = reader.ReadLine()) != null)
93 				{
94 					lines.Add(currentLine);
95 				}
96 			}
97 			return lines.ToArray();
98 		}
99 
100 		/// <summary>Loads the current controls from the controls.cfg file</summary>
101 		/// <param name="FileOrNull">An absolute path reference to a saved controls.cfg file, or a null reference to check the default locations</param>
102 		/// <param name="Controls">The current controls array</param>
LoadControls(string FileOrNull, out Control[] Controls)103 		internal static void LoadControls(string FileOrNull, out Control[] Controls)
104 		{
105 			string File;
106 			string[] Lines = {};
107 			try
108 			{
109 				//Don't crash horribly if the embedded default controls file is missing (makefile.....)
110 				Lines = GetLines(Assembly.GetExecutingAssembly().GetManifestResourceStream("OpenBve.Default.controls"));
111 			}
112 			catch
113 			{
114 				//ignored
115 			}
116 
117 			if (FileOrNull == null)
118 			{
119 				File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "1.5.0/controls.cfg");
120 				if (!System.IO.File.Exists(File))
121 				{
122 					File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "controls.cfg");
123 				}
124 
125 				if (!System.IO.File.Exists(File))
126 				{
127 					//Load the default key assignments if the user settings don't exist
128 					File = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Controls"), "Default.controls");
129 					if (!System.IO.File.Exists(File))
130 					{
131 						MessageBox.Show(Translations.GetInterfaceString("errors_warning") + Environment.NewLine + Translations.GetInterfaceString("errors_controls_missing"),
132 							Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
133 					}
134 				}
135 			}
136 			else
137 			{
138 				File = FileOrNull;
139 			}
140 
141 			Controls = new Control[256];
142 			int Length = 0;
143 			CultureInfo Culture = CultureInfo.InvariantCulture;
144 			if (System.IO.File.Exists(File))
145 			{
146 				Lines = System.IO.File.ReadAllLines(File, new System.Text.UTF8Encoding());
147 			}
148 
149 			for (int i = 0; i < Lines.Length; i++)
150 			{
151 				Lines[i] = Lines[i].Trim();
152 				if (Lines[i].Length != 0 && !Lines[i].StartsWith(";", StringComparison.OrdinalIgnoreCase))
153 				{
154 					string[] Terms = Lines[i].Split(',');
155 					for (int j = 0; j < Terms.Length; j++)
156 					{
157 						Terms[j] = Terms[j].Trim();
158 					}
159 
160 					if (Terms.Length >= 2)
161 					{
162 						if (Length >= Controls.Length)
163 						{
164 							Array.Resize(ref Controls, Controls.Length << 1);
165 						}
166 
167 						int j;
168 						for (j = 0; j < Translations.CommandInfos.Length; j++)
169 						{
170 							if (string.Compare(Translations.CommandInfos[j].Name, Terms[0], StringComparison.OrdinalIgnoreCase) == 0) break;
171 						}
172 
173 						if (j == Translations.CommandInfos.Length)
174 						{
175 							Controls[Length].Command = Translations.Command.None;
176 							Controls[Length].InheritedType = Translations.CommandType.Digital;
177 							Controls[Length].Method = ControlMethod.Invalid;
178 							Controls[Length].Device = new Guid();
179 							Controls[Length].Component = JoystickComponent.Invalid;
180 							Controls[Length].Element = -1;
181 							Controls[Length].Direction = 0;
182 							Controls[Length].Modifier = KeyboardModifier.None;
183 							Controls[Length].Option = 0;
184 						}
185 						else
186 						{
187 							Controls[Length].Command = Translations.CommandInfos[j].Command;
188 							Controls[Length].InheritedType = Translations.CommandInfos[j].Type;
189 							string Method = Terms[1].ToLowerInvariant();
190 							bool Valid = false;
191 							if (Method == "keyboard" & Terms.Length >= 4)
192 							{
193 								Key CurrentKey;
194 								// ReSharper disable once NotAccessedVariable
195 								int SDLTest;
196 								if (int.TryParse(Terms[2], out SDLTest))
197 								{
198 									//We've discovered a SDL keybinding is present, so reset the loading process with the default keyconfig & show an appropriate error message
199 									if (ControlsReset == false)
200 									{
201 										MessageBox.Show(Translations.GetInterfaceString("errors_controls_oldversion") + Environment.NewLine + Translations.GetInterfaceString("errors_controls_reset"), Application.ProductName,
202 											MessageBoxButtons.OK, MessageBoxIcon.Hand);
203 									}
204 
205 									var DefaultControls = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Controls"), "Default keyboard assignment.controls");
206 									if (System.IO.File.Exists(DefaultControls))
207 									{
208 										if (ControlsReset == false)
209 										{
210 											ControlsReset = true;
211 											LoadControls(DefaultControls, out CurrentControls);
212 										}
213 										else
214 										{
215 											MessageBox.Show(Translations.GetInterfaceString("errors_warning") + Environment.NewLine + Translations.GetInterfaceString("errors_controls_default_oldversion"),
216 												Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
217 											i = 0;
218 											Lines = GetLines(Assembly.GetExecutingAssembly().GetManifestResourceStream("OpenBve.Default.controls"));
219 											continue;
220 										}
221 
222 									}
223 									else
224 									{
225 										MessageBox.Show(Translations.GetInterfaceString("errors_warning") + Environment.NewLine + Translations.GetInterfaceString("errors_controls_default_missing"),
226 											Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
227 										Controls = new Control[0];
228 									}
229 
230 									return;
231 								}
232 
233 								if (Enum.TryParse(Terms[2], true, out CurrentKey))
234 								{
235 									int Modifiers;
236 									if (int.TryParse(Terms[3], NumberStyles.Integer, Culture, out Modifiers))
237 									{
238 										Controls[Length].Method = ControlMethod.Keyboard;
239 										Controls[Length].Device = new Guid(); //will create invalid all zero GUID
240 										Controls[Length].Component = JoystickComponent.Invalid;
241 										Controls[Length].Key = (OpenBveApi.Input.Key)CurrentKey;
242 										Controls[Length].Direction = 0;
243 										Controls[Length].Modifier = (KeyboardModifier) Modifiers;
244 										int Option;
245 										if (Terms.Length >= 5 && int.TryParse(Terms[4], NumberStyles.Integer, Culture, out Option))
246 										{
247 											Controls[Length].Option = Option;
248 										}
249 
250 										Valid = true;
251 									}
252 								}
253 							}
254 
255 
256 							else if (Method == "joystick" & Terms.Length >= 4)
257 							{
258 								int oldDevice;
259 								Guid Device = new Guid();
260 								if (int.TryParse(Terms[2], NumberStyles.Integer, Culture, out oldDevice))
261 								{
262 									Device = Joystick.GetGuid(oldDevice);
263 								}
264 
265 								if (Device != new Guid() || Guid.TryParse(Terms[2], out Device))
266 								{
267 									string Component = Terms[3].ToLowerInvariant();
268 									if (Component == "axis" & Terms.Length >= 6)
269 									{
270 										int CurrentAxis;
271 										if (Int32.TryParse(Terms[4], out CurrentAxis))
272 										{
273 											int Direction;
274 											if (int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Direction))
275 											{
276 
277 												Controls[Length].Method = ControlMethod.Joystick;
278 												Controls[Length].Device = Device;
279 												Controls[Length].Component = JoystickComponent.Axis;
280 												Controls[Length].Element = CurrentAxis;
281 												Controls[Length].Direction = Direction;
282 												Controls[Length].Modifier = KeyboardModifier.None;
283 												int Option;
284 												if (Terms.Length >= 7 && int.TryParse(Terms[6], NumberStyles.Integer, Culture, out Option))
285 												{
286 													Controls[Length].Option = Option;
287 												}
288 
289 												Valid = true;
290 											}
291 										}
292 									}
293 									else if (Component == "hat" & Terms.Length >= 6)
294 									{
295 										int CurrentHat;
296 										if (Int32.TryParse(Terms[4], out CurrentHat))
297 										{
298 											int HatDirection;
299 											if (Int32.TryParse(Terms[5], out HatDirection))
300 											{
301 												Controls[Length].Method = ControlMethod.Joystick;
302 												Controls[Length].Device = Device;
303 												Controls[Length].Component = JoystickComponent.Hat;
304 												Controls[Length].Element = CurrentHat;
305 												Controls[Length].Direction = HatDirection;
306 												Controls[Length].Modifier = KeyboardModifier.None;
307 												int Option;
308 												if (Terms.Length >= 7 && int.TryParse(Terms[6], NumberStyles.Integer, Culture, out Option))
309 												{
310 													Controls[Length].Option = Option;
311 												}
312 
313 												Valid = true;
314 											}
315 
316 										}
317 									}
318 									else if (Component == "button" & Terms.Length >= 5)
319 									{
320 										int CurrentButton;
321 										if (Int32.TryParse(Terms[4], out CurrentButton))
322 										{
323 											Controls[Length].Method = ControlMethod.Joystick;
324 											Controls[Length].Device = Device;
325 											Controls[Length].Component = JoystickComponent.Button;
326 											Controls[Length].Element = CurrentButton;
327 											Controls[Length].Direction = 0;
328 											Controls[Length].Modifier = KeyboardModifier.None;
329 											int Option;
330 											if (Terms.Length >= 6 && int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Option))
331 											{
332 												Controls[Length].Option = Option;
333 											}
334 
335 											Valid = true;
336 										}
337 									}
338 
339 								}
340 							}
341 							else if (Method == "raildriver" & Terms.Length >= 4)
342 							{
343 								int oldDevice;
344 								Guid Device = new Guid();
345 								if (int.TryParse(Terms[2], NumberStyles.Integer, Culture, out oldDevice))
346 								{
347 									Device = Joystick.GetGuid(oldDevice);
348 								}
349 
350 								if (Device != new Guid() || Guid.TryParse(Terms[2], out Device))
351 								{
352 									string Component = Terms[3].ToLowerInvariant();
353 									if (Component == "axis" & Terms.Length >= 6)
354 									{
355 										int CurrentAxis;
356 										if (Int32.TryParse(Terms[4], out CurrentAxis))
357 										{
358 											int Direction;
359 											if (int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Direction))
360 											{
361 
362 												Controls[Length].Method = ControlMethod.RailDriver;
363 												Controls[Length].Device = Device;
364 												Controls[Length].Component = JoystickComponent.Axis;
365 												Controls[Length].Element = CurrentAxis;
366 												Controls[Length].Direction = Direction;
367 												Controls[Length].Modifier = KeyboardModifier.None;
368 												int Option;
369 												if (Terms.Length >= 7 && int.TryParse(Terms[6], NumberStyles.Integer, Culture, out Option))
370 												{
371 													Controls[Length].Option = Option;
372 												}
373 
374 												Valid = true;
375 											}
376 										}
377 									}
378 									else if (Component == "button" & Terms.Length >= 5)
379 									{
380 										int CurrentButton;
381 										if (Int32.TryParse(Terms[4], out CurrentButton))
382 										{
383 											Controls[Length].Method = ControlMethod.RailDriver;
384 											Controls[Length].Device = Device;
385 											Controls[Length].Component = JoystickComponent.Button;
386 											Controls[Length].Element = CurrentButton;
387 											Controls[Length].Direction = 0;
388 											Controls[Length].Modifier = KeyboardModifier.None;
389 											int Option;
390 											if (Terms.Length >= 6 && int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Option))
391 											{
392 												Controls[Length].Option = Option;
393 											}
394 
395 											Valid = true;
396 										}
397 									}
398 
399 								}
400 							}
401 
402 							if (!Valid)
403 							{
404 								Controls[Length].Method = ControlMethod.Invalid;
405 								Controls[Length].Device = new Guid(); //Invalid all zero GUID
406 								Controls[Length].Component = JoystickComponent.Invalid;
407 								Controls[Length].Element = -1;
408 								Controls[Length].Direction = 0;
409 								Controls[Length].Modifier = KeyboardModifier.None;
410 								Controls[Length].Option = 0;
411 							}
412 						}
413 
414 						Length++;
415 					}
416 				}
417 			}
418 			Array.Resize(ref Controls, Length);
419 		}
420 
421 
422 		/// <summary>Adds an array of controls to an existing control array</summary>
423 		/// <param name="Base">The base control array</param>
424 		/// <param name="Add">The new controls to add</param>
AddControls(ref Control[] Base, Control[] Add)425 		internal static void AddControls(ref Control[] Base, Control[] Add) {
426 			for (int i = 0; i < Add.Length; i++) {
427 				int j;
428 				for (j = 0; j < Base.Length; j++) {
429 					if (Add[i].Command == Base[j].Command) break;
430 				}
431 				if (j == Base.Length) {
432 					Array.Resize(ref Base, Base.Length + 1);
433 					Base[Base.Length - 1] = Add[i];
434 				}
435 			}
436 		}
437 	}
438 }
439