319 lines
8.8 KiB
C#
319 lines
8.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
using System.Numerics;
|
|
using System.Threading;
|
|
using System.Runtime.InteropServices;
|
|
using System.Timers;
|
|
|
|
using System.Net.NetworkInformation;
|
|
using System.Diagnostics;
|
|
|
|
using static BetterJoyForCemu.HIDapi;
|
|
using Nefarius.ViGEm.Client;
|
|
using Nefarius.ViGEm.Client.Targets;
|
|
using System.Net;
|
|
using System.Configuration;
|
|
using System.Net.Http;
|
|
using System.IO;
|
|
using System.Windows.Forms;
|
|
|
|
using System.ServiceProcess;
|
|
|
|
namespace BetterJoyForCemu {
|
|
public class JoyconManager {
|
|
public bool EnableIMU = true;
|
|
public bool EnableLocalize = false;
|
|
|
|
private const ushort vendor_id = 0x57e;
|
|
private const ushort vendor_id_ = 0x057e;
|
|
private const ushort product_l = 0x2006;
|
|
private const ushort product_r = 0x2007;
|
|
private const ushort product_pro = 0x2009;
|
|
|
|
public List<Joycon> j; // Array of all connected Joy-Cons
|
|
static JoyconManager instance;
|
|
|
|
public MainForm form;
|
|
|
|
public static JoyconManager Instance {
|
|
get { return instance; }
|
|
}
|
|
|
|
public void Awake() {
|
|
instance = this;
|
|
int i = 0;
|
|
|
|
j = new List<Joycon>();
|
|
bool isLeft = false;
|
|
HIDapi.hid_init();
|
|
|
|
IntPtr ptr = HIDapi.hid_enumerate(vendor_id, 0x0);
|
|
IntPtr top_ptr = ptr;
|
|
|
|
if (ptr == IntPtr.Zero) {
|
|
ptr = HIDapi.hid_enumerate(vendor_id_, 0x0);
|
|
if (ptr == IntPtr.Zero) {
|
|
HIDapi.hid_free_enumeration(ptr);
|
|
form.console.Text += "No Joy-Cons found!\r\n";
|
|
}
|
|
}
|
|
|
|
hid_device_info enumerate;
|
|
while (ptr != IntPtr.Zero) {
|
|
enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info));
|
|
|
|
if (enumerate.product_id == product_l || enumerate.product_id == product_r || enumerate.product_id == product_pro) {
|
|
if (enumerate.product_id == product_l) {
|
|
isLeft = true;
|
|
form.console.Text += "Left Joy-Con connected.\r\n";
|
|
} else if (enumerate.product_id == product_r) {
|
|
isLeft = false;
|
|
form.console.Text += "Right Joy-Con connected.\r\n";
|
|
} else if (enumerate.product_id == product_pro) {
|
|
isLeft = true;
|
|
form.console.Text += "Pro controller connected.\r\n";
|
|
} else {
|
|
form.console.Text += "Non Joy-Con input device skipped.\r\n";
|
|
}
|
|
|
|
// Add controller to block-list for HidGuardian
|
|
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/affected/add/");
|
|
string postData = @"hwids=HID\" + enumerate.path.Split('#')[1].ToUpper();
|
|
var data = Encoding.UTF8.GetBytes(postData);
|
|
|
|
request.Method = "POST";
|
|
request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
|
|
request.ContentLength = data.Length;
|
|
|
|
using (var stream = request.GetRequestStream())
|
|
stream.Write(data, 0, data.Length);
|
|
|
|
try {
|
|
var response = (HttpWebResponse)request.GetResponse();
|
|
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
|
|
} catch (Exception e) {
|
|
form.console.Text += "Unable to add controller to block-list.\r\n";
|
|
}
|
|
// -------------------- //
|
|
|
|
IntPtr handle = HIDapi.hid_open_path(enumerate.path);
|
|
try {
|
|
HIDapi.hid_set_nonblocking(handle, 1);
|
|
} catch (Exception e) {
|
|
form.console.Text += "Unable to open path to device - are you using the correct (64 vs 32-bit) version for your PC?\r\n";
|
|
break;
|
|
}
|
|
|
|
j.Add(new Joycon(handle, EnableIMU, EnableLocalize & EnableIMU, 0.05f, isLeft, j.Count, enumerate.product_id == product_pro, enumerate.serial_number == "000000000001"));
|
|
|
|
j.Last().form = form;
|
|
|
|
byte[] mac = new byte[6];
|
|
for (int n = 0; n < 6; n++)
|
|
mac[n] = byte.Parse(enumerate.serial_number.Substring(n * 2, 2), System.Globalization.NumberStyles.HexNumber);
|
|
j[j.Count - 1].PadMacAddress = new PhysicalAddress(mac);
|
|
|
|
++i;
|
|
}
|
|
ptr = enumerate.next;
|
|
}
|
|
|
|
int found = 0;
|
|
foreach (Joycon v in j) {
|
|
if (v.isLeft && !v.isPro)
|
|
found++;
|
|
if (!v.isLeft && !v.isPro)
|
|
found++;
|
|
}
|
|
|
|
if (found == 2) {
|
|
form.console.Text += "Both joycons successfully found.\r\n";
|
|
Joycon temp = null;
|
|
foreach (Joycon v in j) {
|
|
if (v.isLeft && !v.isPro) {
|
|
if (temp == null)
|
|
temp = v;
|
|
else {
|
|
temp.other = v;
|
|
v.other = temp;
|
|
}
|
|
}
|
|
|
|
if (!v.isLeft && !v.isPro) {
|
|
if (temp == null)
|
|
temp = v;
|
|
else {
|
|
temp.other = v;
|
|
v.other = temp;
|
|
}
|
|
}
|
|
} // Join up the two joycons
|
|
} else if (found != 0)
|
|
form.console.Text += "Only one joycon found. Please connect both and then restart the program.\r\n";
|
|
|
|
HIDapi.hid_free_enumeration(top_ptr);
|
|
}
|
|
|
|
public void Start() {
|
|
for (int i = 0; i < j.Count; ++i) {
|
|
Joycon jc = j[i];
|
|
|
|
if (jc.xin != null)
|
|
jc.xin.Connect();
|
|
|
|
byte LEDs = 0x0;
|
|
LEDs |= (byte)(0x1 << i);
|
|
jc.Attach(leds_: LEDs);
|
|
jc.Begin();
|
|
}
|
|
}
|
|
|
|
public void Update() {
|
|
for (int i = 0; i < j.Count; ++i)
|
|
j[i].Update();
|
|
}
|
|
|
|
public void OnApplicationQuit() {
|
|
for (int i = 0; i < j.Count; ++i) {
|
|
j[i].Detach();
|
|
|
|
if (j[i].xin != null) {
|
|
j[i].xin.Disconnect();
|
|
j[i].xin.Dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Custom timer class because system timers have a limit of 15.6ms
|
|
class HighResTimer {
|
|
double interval = 0;
|
|
double frequency = 0;
|
|
|
|
Thread thread;
|
|
|
|
public delegate void ActionDelegate();
|
|
ActionDelegate func;
|
|
|
|
bool run = false;
|
|
|
|
public HighResTimer(double f, ActionDelegate a) {
|
|
frequency = f;
|
|
interval = 1.0 / f;
|
|
|
|
func = a;
|
|
}
|
|
|
|
public void Start() {
|
|
run = true;
|
|
thread = new Thread(new ThreadStart(Run));
|
|
thread.Start();
|
|
}
|
|
|
|
void Run() {
|
|
while (run) {
|
|
func();
|
|
int timeToSleep = (int)(interval * 1000);
|
|
Thread.Sleep(timeToSleep);
|
|
}
|
|
}
|
|
|
|
public void Stop() {
|
|
run = false;
|
|
}
|
|
}
|
|
|
|
class Program {
|
|
public static PhysicalAddress btMAC = new PhysicalAddress(new byte[] { 0, 0, 0, 0, 0, 0 });
|
|
public static UdpServer server;
|
|
static double pollsPerSecond = 120.0;
|
|
|
|
public static ViGEmClient emClient;
|
|
|
|
private static readonly HttpClient client = new HttpClient();
|
|
|
|
static JoyconManager mgr;
|
|
static HighResTimer timer;
|
|
static string pid;
|
|
|
|
static MainForm form;
|
|
|
|
public static void Start() {
|
|
pid = Process.GetCurrentProcess().Id.ToString(); // get current process id for HidCerberus.Srv
|
|
|
|
var HidCerberusService = new ServiceController("HidCerberus Service");
|
|
if (HidCerberusService.Status == ServiceControllerStatus.Stopped) {
|
|
form.console.Text += "HidGuardian was stopped. Starting...\r\n";
|
|
|
|
try {
|
|
HidCerberusService.Start();
|
|
} catch (Exception e) {
|
|
form.console.Text += "Unable to start HidGuardian - everything should work fine without it, but if you need it, run the app again as an admin.\r\n";
|
|
}
|
|
}
|
|
|
|
HttpWebResponse response;
|
|
if (Boolean.Parse(ConfigurationSettings.AppSettings["PurgeWhitelist"])) {
|
|
try {
|
|
response = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/whitelist/purge/").GetResponse(); // remove all programs allowed to see controller
|
|
} catch (Exception e) {
|
|
form.console.Text += "Unable to purge whitelist.\r\n";
|
|
}
|
|
}
|
|
|
|
try {
|
|
response = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/whitelist/add/" + pid).GetResponse(); // add BetterJoyForCemu to allowed processes
|
|
} catch (Exception e) {
|
|
form.console.Text += "Unable to add program to whitelist.\r\n";
|
|
}
|
|
|
|
emClient = new ViGEmClient(); // Manages emulated XInput
|
|
|
|
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) {
|
|
// Get local BT host MAC
|
|
if (nic.NetworkInterfaceType != NetworkInterfaceType.FastEthernetFx && nic.NetworkInterfaceType != NetworkInterfaceType.Wireless80211) {
|
|
if (nic.Name.Split()[0] == "Bluetooth") {
|
|
btMAC = nic.GetPhysicalAddress();
|
|
}
|
|
}
|
|
}
|
|
|
|
mgr = new JoyconManager();
|
|
mgr.form = form;
|
|
mgr.Awake();
|
|
mgr.Start();
|
|
|
|
server = new UdpServer(mgr.j);
|
|
server.form = form;
|
|
|
|
server.Start(IPAddress.Parse(ConfigurationSettings.AppSettings["IP"]), Int32.Parse(ConfigurationSettings.AppSettings["Port"]));
|
|
timer = new HighResTimer(pollsPerSecond, new HighResTimer.ActionDelegate(mgr.Update));
|
|
timer.Start();
|
|
|
|
form.console.Text += "All systems go\r\n";
|
|
}
|
|
|
|
public static void Stop() {
|
|
try {
|
|
HttpWebResponse response = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/whitelist/remove/" + pid).GetResponse(); // add BetterJoyForCemu to allowed processes
|
|
} catch (Exception e) {
|
|
form.console.Text += "Unable to remove program from whitelist.\r\n";
|
|
}
|
|
|
|
server.Stop();
|
|
timer.Stop();
|
|
mgr.OnApplicationQuit();
|
|
}
|
|
|
|
static void Main(string[] args) {
|
|
Application.EnableVisualStyles();
|
|
Application.SetCompatibleTextRenderingDefault(false);
|
|
form = new MainForm();
|
|
Application.Run(form);
|
|
}
|
|
}
|
|
}
|