forked from MWisBest/StadiaToSCP
/
Program.cs
308 lines (277 loc) · 9.04 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HidLibrary;
using System.Windows.Forms;
using ScpDriverInterface;
using System.Threading;
using System.Runtime.InteropServices;
namespace StadiaToSCP
{
class Program
{
private static ScpBus global_scpBus;
static bool ConsoleEventCallback(int eventType)
{
if (eventType == 2)
{
global_scpBus.UnplugAll();
}
return false;
}
static ConsoleEventDelegate handler; // Keeps it from getting garbage collected
// Pinvoke
private delegate bool ConsoleEventDelegate(int eventType);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
public static string ByteArrayToHexString(byte[] bytes)
{
return string.Join(string.Empty, Array.ConvertAll(bytes, b => b.ToString("X2")));
}
static Mutex singleInstanceMutex = new Mutex(true, "{298c40ea-b004-4a7f-9910-d3bf3591b18b}");
[STAThreadAttribute]
static void Main(string[] args)
{
if (!IsSingleInstance()) Environment.Exit(0);
NIcon = new NotifyIcon();
//ScpBus scpBus = null;
ScpBus scpBus = new ScpBus();
scpBus.UnplugAll();
global_scpBus = scpBus;
handler = new ConsoleEventDelegate(ConsoleEventCallback);
SetConsoleCtrlHandler(handler, true);
Thread.Sleep(400);
var controllersManager = new Thread(() => ManageControllers(scpBus));
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
try
{
using (var pi = new ProcessIcon())
{
pi.Display();
controllersManager.Start();
Application.Run();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Program Terminated Unexpectedly",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
controllersManager.Abort();
scpBus.UnplugAll();
foreach (var device in Gamepads.Select(g => g.Device))
{
device.CloseDevice();
}
singleInstanceMutex.ReleaseMutex();
}
finally
{
Environment.Exit(0);
}
}
public static NotifyIcon NIcon { get; set; }
private static bool IsSingleInstance()
{
if (singleInstanceMutex.WaitOne(TimeSpan.Zero, true))
{
return true;
}
else
{
MessageBox.Show("Another copy is already running");
return false;
}
}
private static void ManageControllers(ScpBus scpBus)
{
var nrConnected = 0;
//while (true)
{
var compatibleDevices = HidDevices.Enumerate(0x18D1, 0x9400).ToList();
var existingDevices = Gamepads.Select(g => g.Device).ToList();
var newDevices = compatibleDevices.Where(d => !existingDevices.Select(e => e.DevicePath).Contains(d.DevicePath));
foreach (var gamepad in Gamepads.ToList())
{
if (!gamepad.check_connected())
{
gamepad.unplug();
Gamepads.Remove(gamepad);
}
}
foreach (var deviceInstance in newDevices)
{
var device = deviceInstance;
try
{
device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.Exclusive);
}
catch
{
InformUser("Could not open gamepad in exclusive mode. Try reconnecting the device.");
var instanceId = devicePathToInstanceId(deviceInstance.DevicePath);
if (TryReEnableDevice(instanceId))
{
try
{
device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.Exclusive);
InformUser("Opened in exclusive mode.");
}
catch
{
device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.ShareRead | ShareMode.ShareWrite);
InformUser("Opened in shared mode.");
}
}
else
{
device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.ShareRead | ShareMode.ShareWrite);
InformUser("Opened in shared mode.");
}
}
//byte[] vibration = { 0x05, 0x00, 0x00, 0x00, 0x00 };
//if (device.Write(vibration) == false)
//{
// InformUser("Could not write to gamepad (is it closed?), skipping");
// device.CloseDevice();
// continue;
//}
byte[] serialNumber;
byte[] product;
device.ReadSerialNumber(out serialNumber);
device.ReadProduct(out product);
var usedIndexes = Gamepads.Select(g => g.Index);
var index = 1;
while (usedIndexes.Contains(index))
{
index++;
}
Gamepads.Add(new StadiaController(device, scpBus, index));
}
if (Gamepads.Count != nrConnected)
{
InformUser($"{Gamepads.Count} controllers connected");
nrConnected = Gamepads.Count;
}
//Thread.Sleep(1000);
}
}
private static void InformUser(string text)
{
NIcon.Text = "Export Datatable Utlity";
NIcon.Visible = true;
NIcon.BalloonTipTitle = "Mi controller";
NIcon.BalloonTipText = text;
NIcon.ShowBalloonTip(100);
//var content = new ToastContent()
//{
// Visual = new ToastVisual
// {
// BindingGeneric = new ToastBindingGeneric()
// {
// AppLogoOverride = new ToastGenericAppLogo
// {
// HintCrop = ToastGenericAppLogoCrop.Circle,
// Source = "http://messageme.com/lei/profile.jpg"
// },
// Children =
// {
// new AdaptiveText {Text = text },
// },
// Attribution = new ToastGenericAttributionText
// {
// Text = "Alert"
// },
// }
// }
//};
//var toast = new ToastNotification(content.GetContent());
//// Display toast
//ToastNotificationManager.CreateToastNotifier().Show(toast);
Console.WriteLine( text );
}
public static List<StadiaController> Gamepads { get; set; } = new List<StadiaController>();
private static bool TryReEnableDevice(string deviceInstanceId)
{
try
{
Guid hidGuid = new Guid();
HidLibrary.NativeMethods.HidD_GetHidGuid(ref hidGuid);
IntPtr deviceInfoSet = HidLibrary.NativeMethods.SetupDiGetClassDevs(ref hidGuid, deviceInstanceId, 0,
HidLibrary.NativeMethods.DIGCF_PRESENT | HidLibrary.NativeMethods.DIGCF_DEVICEINTERFACE);
HidLibrary.NativeMethods.SP_DEVINFO_DATA deviceInfoData = new HidLibrary.NativeMethods.SP_DEVINFO_DATA();
deviceInfoData.cbSize = Marshal.SizeOf(deviceInfoData);
var success = HidLibrary.NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData);
if (!success)
{
InformUser("Error getting device info data, error code = " + Marshal.GetLastWin32Error());
}
success = HidLibrary.NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 1, ref deviceInfoData);
// Checks that we have a unique device
if (success)
{
InformUser("Can't find unique device");
}
HidLibrary.NativeMethods.SP_PROPCHANGE_PARAMS propChangeParams = new HidLibrary.NativeMethods.SP_PROPCHANGE_PARAMS();
propChangeParams.classInstallHeader.cbSize = Marshal.SizeOf(propChangeParams.classInstallHeader);
propChangeParams.classInstallHeader.installFunction = HidLibrary.NativeMethods.DIF_PROPERTYCHANGE;
propChangeParams.stateChange = HidLibrary.NativeMethods.DICS_DISABLE;
propChangeParams.scope = HidLibrary.NativeMethods.DICS_FLAG_GLOBAL;
propChangeParams.hwProfile = 0;
success = HidLibrary.NativeMethods.SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInfoData,
ref propChangeParams, Marshal.SizeOf(propChangeParams));
if (!success)
{
InformUser("Error setting class install params, error code = " + Marshal.GetLastWin32Error());
return false;
}
success = HidLibrary.NativeMethods.SetupDiCallClassInstaller(HidLibrary.NativeMethods.DIF_PROPERTYCHANGE,
deviceInfoSet, ref deviceInfoData);
if (!success)
{
InformUser("Error disabling device, error code = " + Marshal.GetLastWin32Error());
return false;
}
propChangeParams.stateChange = HidLibrary.NativeMethods.DICS_ENABLE;
success = HidLibrary.NativeMethods.SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInfoData,
ref propChangeParams, Marshal.SizeOf(propChangeParams));
if (!success)
{
InformUser("Error setting class install params, error code = " + Marshal.GetLastWin32Error());
return false;
}
success = HidLibrary.NativeMethods.SetupDiCallClassInstaller(HidLibrary.NativeMethods.DIF_PROPERTYCHANGE,
deviceInfoSet, ref deviceInfoData);
if (!success)
{
InformUser("Error enabling device, error code = " + Marshal.GetLastWin32Error());
return false;
}
HidLibrary.NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet);
return true;
}
catch
{
InformUser("Can't re-enable device");
return false;
}
}
private static string devicePathToInstanceId(string devicePath)
{
string deviceInstanceId = devicePath;
deviceInstanceId = deviceInstanceId.Remove(0, deviceInstanceId.LastIndexOf('\\') + 1);
deviceInstanceId = deviceInstanceId.Remove(deviceInstanceId.LastIndexOf('{'));
deviceInstanceId = deviceInstanceId.Replace('#', '\\');
if (deviceInstanceId.EndsWith("\\"))
{
deviceInstanceId = deviceInstanceId.Remove(deviceInstanceId.Length - 1);
}
return deviceInstanceId;
}
}
}