1 //Simplified BSD License (BSD-2-Clause)
2 //
3 //Copyright (c) 2020-2021, Marc Riera, The OpenBVE Project
4 //
5 //Redistribution and use in source and binary forms, with or without
6 //modification, are permitted provided that the following conditions are met:
7 //
8 //1. Redistributions of source code must retain the above copyright notice, this
9 //   list of conditions and the following disclaimer.
10 //2. Redistributions in binary form must reproduce the above copyright notice,
11 //   this list of conditions and the following disclaimer in the documentation
12 //   and/or other materials provided with the distribution.
13 //
14 //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 //DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 //ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 //(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 //ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 //(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 
25 using System;
26 using System.Collections.Generic;
27 using OpenTK.Input;
28 
29 namespace DenshaDeGoInput
30 {
31 	/// <summary>
32 	/// Class representing a PlayStation 2 controller
33 	/// </summary>
34 	internal class Ps2Controller : Controller
35 	{
36 		/// <summary>A cached list of supported connected controllers.</summary>
37 		private static Dictionary<Guid, Controller> cachedControllers = new Dictionary<Guid, Controller>();
38 
39 		private static string[] controllerIds =
40 		{
41 			// TCPP-20009 (Type II)
42 			"0ae4:0004",
43 			// TCPP-20011 (Shinkansen)
44 			"0ae4:0005",
45 			// TCPP-20014 (Ryojouhen)
46 			"0ae4:0007"
47 		};
48 
49 		/// <summary>The min/max byte for each brake notch, from Released to Emergency. Each notch consists of two bytes.</summary>
50 		private readonly byte[] brakeBytes;
51 
52 		/// <summary>The min/max byte for each power notch, from Released to maximum. Each notch consists of two bytes.</summary>
53 		private readonly byte[] powerBytes;
54 
55 		/// <summary>The button mask for the buttons. Follows order in InputTranslator.</summary>
56 		private readonly byte[] buttonMask;
57 
58 		/// <summary>An array with raw input data from the controller.</summary>
59 		private byte[] inputBuffer;
60 
61 		/// <summary>An array with raw output data for the controller.</summary>
62 		private byte[] outputBuffer;
63 
64 		/// <summary>
65 		/// Initializes an Unbalance controller.
66 		/// </summary>
Ps2Controller(ControllerButtons buttons, byte[] buttonBytes, byte[] brake, byte[] power)67 		internal Ps2Controller(ControllerButtons buttons, byte[] buttonBytes, byte[] brake, byte[] power)
68 		{
69 			ControllerName = string.Empty;
70 			IsConnected = false;
71 			RequiresCalibration = false;
72 			BrakeNotches = brake.Length / 2 - 2;
73 			PowerNotches = power.Length / 2 - 1;
74 			brakeBytes = brake;
75 			powerBytes = power;
76 			Buttons = buttons;
77 			buttonMask = buttonBytes;
78 		}
79 
80 		/// <summary>
81 		/// Reads the input from the controller.
82 		/// </summary>
ReadInput()83 		internal override void ReadInput()
84 		{
85 			// Sync input/output data
86 			LibUsb.SyncController(Guid, inputBuffer, outputBuffer);
87 
88 			byte brakeData;
89 			byte powerData;
90 			byte buttonData;
91 			byte dpadData;
92 			byte pedalData;
93 			switch (Id)
94 			{
95 				// TCPP-20009 (Type II)
96 				case "0ae4:0004":
97 					brakeData = inputBuffer[1];
98 					powerData = inputBuffer[2];
99 					buttonData = inputBuffer[5];
100 					dpadData = inputBuffer[4];
101 					pedalData = inputBuffer[3];
102 					break;
103 				// TCPP-20011 (Shinkansen)
104 				// TCPP-20014 (Ryojouhen)
105 				default:
106 					brakeData = inputBuffer[0];
107 					powerData = inputBuffer[1];
108 					buttonData = inputBuffer[4];
109 					dpadData = inputBuffer[3];
110 					pedalData = inputBuffer[2];
111 					break;
112 			}
113 
114 			for (int i = 0; i < brakeBytes.Length; i+=2)
115 			{
116 				// Each notch uses two bytes, minimum value and maximum value
117 				if (brakeData >= brakeBytes[i] && brakeData <= brakeBytes[i + 1])
118 				{
119 					if (brakeBytes.Length == i + 2)
120 					{
121 						// Last notch should be Emergency
122 						InputTranslator.BrakeNotch = InputTranslator.BrakeNotches.Emergency;
123 					}
124 					else
125 					{
126 						// Regular brake notch
127 						InputTranslator.BrakeNotch = (InputTranslator.BrakeNotches)(i / 2);
128 					}
129 					break;
130 				}
131 			}
132 			for (int i = 0; i < powerBytes.Length; i+=2)
133 			{
134 				// Each notch uses two bytes, minimum value and maximum value
135 				if (powerData >= powerBytes[i] && powerData <= powerBytes[i + 1])
136 				{
137 					InputTranslator.PowerNotch = (InputTranslator.PowerNotches)(i / 2);
138 					break;
139 				}
140 			}
141 
142 			// Standard buttons
143 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.Select] = (buttonData & buttonMask[(int)InputTranslator.ControllerButton.Select]) != 0 ? ButtonState.Pressed : ButtonState.Released;
144 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.Start] = (buttonData & buttonMask[(int)InputTranslator.ControllerButton.Start]) != 0 ? ButtonState.Pressed : ButtonState.Released;
145 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.A] = (buttonData & buttonMask[(int)InputTranslator.ControllerButton.A]) != 0 ? ButtonState.Pressed : ButtonState.Released;
146 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.B] = (buttonData & buttonMask[(int)InputTranslator.ControllerButton.B]) != 0 ? ButtonState.Pressed : ButtonState.Released;
147 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.C] = (buttonData & buttonMask[(int)InputTranslator.ControllerButton.C]) != 0 ? ButtonState.Pressed : ButtonState.Released;
148 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.D] = (buttonData & buttonMask[(int)InputTranslator.ControllerButton.D]) != 0 ? ButtonState.Pressed : ButtonState.Released;
149 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.LDoor] = (buttonData & buttonMask[(int)InputTranslator.ControllerButton.LDoor]) != 0 ? ButtonState.Pressed : ButtonState.Released;
150 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.RDoor] = (buttonData & buttonMask[(int)InputTranslator.ControllerButton.RDoor]) != 0 ? ButtonState.Pressed : ButtonState.Released;
151 
152 			// D-pad
153 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.Up] = (dpadData <= 1 || dpadData == 7) ? ButtonState.Pressed : ButtonState.Released;
154 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.Right] = (dpadData >= 1 && dpadData <= 3) ? ButtonState.Pressed : ButtonState.Released;
155 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.Down] = (dpadData >= 3 && dpadData <= 5) ? ButtonState.Pressed : ButtonState.Released;
156 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.Left] = (dpadData >= 5 && dpadData <= 7) ? ButtonState.Pressed : ButtonState.Released;
157 
158 			// Horn pedal
159 			InputTranslator.ControllerButtons[(int)InputTranslator.ControllerButton.Pedal] = pedalData == 0x0 ? ButtonState.Pressed : ButtonState.Released;
160 
161 			switch (Id)
162 			{
163 				// TCPP-20009 (Type II)
164 				case "0ae4:0004":
165 					outputBuffer = new byte[] { 0x0, 0x3 };
166 					if (DenshaDeGoInput.Ingame)
167 					{
168 						// Door lamp
169 						outputBuffer[1] = (byte)(DenshaDeGoInput.TrainDoorsClosed ? 1 : 0);
170 					}
171 					break;
172 				// TCPP-20011 (Shinkansen)
173 				case "0ae4:0005":
174 					double speed = Math.Round(DenshaDeGoInput.CurrentTrainSpeed, 0);
175 					double limit = Math.Round(DenshaDeGoInput.CurrentSpeedLimit, 0);
176 					int speed1 = (int)(speed % 10);
177 					int speed2 = (int)(speed % 100 / 10);
178 					int speed3 = (int)(speed % 1000 / 100);
179 					int limit1 = (int)(limit % 10);
180 					int limit2 = (int)(limit % 100 / 10);
181 					int limit3 = (int)(limit % 1000 / 100);
182 					int limit_approach = 0;
183 					if (speed >= limit)
184 					{
185 						limit_approach = 10;
186 					}
187 					else if (speed > limit - 10)
188 					{
189 						limit_approach = -(int)(limit - speed - 10);
190 					}
191 					// Specially crafted array that blanks the display
192 					outputBuffer = new byte[] { 0x0, 0x0, 0x0, 0x0, 0xFF, 0xFF, 0xFF, 0xFF };
193 					if (DenshaDeGoInput.Ingame)
194 					{
195 						if (DenshaDeGoInput.CurrentSpeedLimit >= 0 && DenshaDeGoInput.ATCSection)
196 						{
197 							// Door lamp + limit approach
198 							outputBuffer[2] = (byte)((128 * (DenshaDeGoInput.TrainDoorsClosed ? 1 : 0)) + limit_approach);
199 							// Route limit
200 							outputBuffer[6] = (byte)(16 * limit2 + limit1);
201 							outputBuffer[7] = (byte)limit3;
202 						}
203 						else
204 						{
205 							// Door lamp
206 							outputBuffer[2] = (byte)(128 * (DenshaDeGoInput.TrainDoorsClosed ? 1 : 0));
207 						}
208 
209 						// Speed gauge
210 						outputBuffer[3] = (byte)Math.Ceiling(Math.Round(DenshaDeGoInput.CurrentTrainSpeed) / 15);
211 						// Train speed
212 						outputBuffer[4] = (byte)(16 * speed2 + speed1);
213 						outputBuffer[5] = (byte)speed3;
214 					}
215 					break;
216 			}
217 		}
218 
219 		/// <summary>
220 		/// Configures the supported controllers with LibUsb.
221 		/// </summary>
ConfigureControllers()222 		internal static void ConfigureControllers()
223 		{
224 			LibUsb.AddSupportedControllers(controllerIds);
225 		}
226 
227 		/// <summary>
228 		/// Gets the list of connected controllers
229 		/// </summary>
230 		/// <returns>The list of controllers handled by this class.</returns>
GetControllers()231 		internal static Dictionary<Guid, Controller> GetControllers()
232 		{
233 			foreach (KeyValuePair<Guid, LibUsb.UsbController> usbController in LibUsb.GetSupportedControllers())
234 			{
235 				Guid guid = usbController.Key;
236 				string id = GetControllerID(guid);
237 				string name = usbController.Value.ControllerName;
238 
239 				if (!cachedControllers.ContainsKey(guid))
240 				{
241 					// TCPP-20009 (Type II)
242 					if (id == "0ae4:0004")
243 					{
244 						ControllerButtons buttons = ControllerButtons.Select | ControllerButtons.Start | ControllerButtons.A | ControllerButtons.B | ControllerButtons.C | ControllerButtons.D | ControllerButtons.Pedal | ControllerButtons.DPad;
245 						byte[] buttonBytes = { 0x10, 0x20, 0x2, 0x1, 0x4, 0x8, 0x0, 0x0 };
246 						byte[] brakeBytes = { 0x79, 0x79, 0x8A, 0x8A, 0x94, 0x94, 0x9A, 0x9A, 0xA2, 0xA2, 0xA8, 0xA8, 0xAF, 0xAF, 0xB2, 0xB2, 0xB5, 0xB5, 0xB9, 0xB9 };
247 						byte[] powerBytes = { 0x81, 0x81, 0x6D, 0x6D, 0x54, 0x54, 0x3F, 0x3F, 0x21, 0x21, 0x00, 0x00 };
248 						Ps2Controller newcontroller = new Ps2Controller(buttons, buttonBytes, brakeBytes, powerBytes)
249 						{
250 							// 6 bytes for input, 2 for output
251 							Guid = guid,
252 							Id = id,
253 							ControllerName = name,
254 							inputBuffer = new byte[] { 0x1, 0x0, 0x0, 0xFF, 0x8, 0x0 },
255 							outputBuffer = new byte[] { 0x0, 0x3 }
256 						};
257 						cachedControllers.Add(guid, newcontroller);
258 					}
259 					// TCPP-20011 (Shinkansen)
260 					if (id == "0ae4:0005")
261 					{
262 						ControllerButtons buttons = ControllerButtons.Select | ControllerButtons.Start | ControllerButtons.A | ControllerButtons.B | ControllerButtons.C | ControllerButtons.D | ControllerButtons.Pedal | ControllerButtons.DPad;
263 						byte[] buttonBytes = { 0x10, 0x20, 0x8, 0x4, 0x2, 0x1, 0x0, 0x0 };
264 						byte[] brakeBytes = { 0x1C, 0x1C, 0x38, 0x38, 0x54, 0x54, 0x70, 0x70, 0x8B, 0x8B, 0xA7, 0xA7, 0xC3, 0xC3, 0xDF, 0xDF, 0xFB, 0xFB };
265 						byte[] powerBytes = { 0x12, 0x12, 0x24, 0x24, 0x36, 0x36, 0x48, 0x48, 0x5A, 0x5A, 0x6C, 0x6C, 0x7E, 0x7E, 0x90, 0x90, 0xA2, 0xA2, 0xB4, 0xB4, 0xC6, 0xC6, 0xD7, 0xD7, 0xE9, 0xE9, 0xFB, 0xFB };
266 						Ps2Controller newcontroller = new Ps2Controller(buttons, buttonBytes, brakeBytes, powerBytes)
267 						{
268 							// 6 bytes for input, 8 for output
269 							Guid = guid,
270 							Id = id,
271 							ControllerName = name,
272 							inputBuffer = new byte[] { 0x0, 0x0, 0xFF, 0x8, 0x0, 0x0 },
273 							outputBuffer = new byte[] { 0x0, 0x0, 0x0, 0x0, 0xFF, 0xFF, 0xFF, 0xFF }
274 						};
275 						cachedControllers.Add(guid, newcontroller);
276 					}
277 					// TCPP-20014 (Ryojouhen)
278 					if (id == "0ae4:0007")
279 					{
280 						ControllerButtons buttons = ControllerButtons.Select | ControllerButtons.Start | ControllerButtons.A | ControllerButtons.B | ControllerButtons.C | ControllerButtons.Pedal | ControllerButtons.LDoor | ControllerButtons.RDoor | ControllerButtons.DPad;
281 						byte[] buttonBytes = { 0x20, 0x40, 0x4, 0x2, 0x1, 0x0, 0x10, 0x8 };
282 						byte[] brakeBytes = { 0x23, 0x2C, 0x2D, 0x3E, 0x3F, 0x4E, 0x4F, 0x63, 0x64, 0x8A, 0x8B, 0xB0, 0xB1, 0xD4, 0xD5, 0xDF };
283 						byte[] powerBytes = { 0x0, 0x0, 0x3C, 0x3C, 0x78, 0x78, 0xB4, 0xB4, 0xF0, 0xF0 };
284 						Ps2Controller newcontroller = new Ps2Controller(buttons, buttonBytes, brakeBytes, powerBytes)
285 						{
286 							// 8 bytes for input, no output
287 							Guid = guid,
288 							Id = id,
289 							ControllerName = name,
290 							inputBuffer = new byte[] { 0x0, 0x0, 0xFF, 0x8, 0x0, 0x0, 0x0, 0x0 },
291 							outputBuffer = new byte[0]
292 						};
293 						cachedControllers.Add(guid, newcontroller);
294 					}
295 				}
296 
297 				// Update connection status and name
298 				cachedControllers[guid].IsConnected = usbController.Value.IsConnected;
299 				cachedControllers[guid].ControllerName = name;
300 			}
301 			return cachedControllers;
302 		}
303 	}
304 }
305