diff --git a/BetterJoyForCemu/Joycon.cs b/BetterJoyForCemu/Joycon.cs index 8c241b0..f838bcf 100644 --- a/BetterJoyForCemu/Joycon.cs +++ b/BetterJoyForCemu/Joycon.cs @@ -10,10 +10,13 @@ using System.Threading.Tasks; namespace BetterJoyForCemu { public class Joycon { - float timing = 60.0f; + float timing = 120.0f; - bool isPro = false; + public bool isPro = false; bool isUSB = false; + public Joycon other; + + public bool send = true; public enum DebugType : int { NONE, @@ -210,7 +213,7 @@ namespace BetterJoyForCemu { // For UdpServer public int PadId = 0; - public int battery = 2; + public int battery = 4; public int model = 2; public int constate = 2; public int connection = 3; @@ -261,9 +264,6 @@ namespace BetterJoyForCemu { } public int Attach(byte leds_ = 0x0) { state = state_.ATTACHED; - - // Make sure command is received - HIDapi.hid_set_nonblocking(handle, 0); byte[] a = { 0x0 }; @@ -315,8 +315,6 @@ namespace BetterJoyForCemu { DebugPrint("Done with init.", DebugType.COMMS); - HIDapi.hid_set_nonblocking(handle, 1); - return 0; } @@ -349,10 +347,10 @@ namespace BetterJoyForCemu { private byte ts_en; private int ReceiveRaw() { if (handle == IntPtr.Zero) return -2; - HIDapi.hid_set_nonblocking(handle, 0); + HIDapi.hid_set_nonblocking(handle, 1); byte[] raw_buf = new byte[report_len]; int ret = HIDapi.hid_read(handle, raw_buf, new UIntPtr(report_len)); - if (ret > 0) { + if (ret > 0 && Program.server != null) { // Process packets as soon as they come for (int n = 0; n < 3; n++) { ExtractIMUValues(raw_buf, n); @@ -362,7 +360,7 @@ namespace BetterJoyForCemu { Timestamp += (ulong)lag * 5000; // add lag once ProcessButtonsAndStick(raw_buf); } - Timestamp += (ulong)(2 - n) * 5000; // 5ms difference + Timestamp += 5000; // 5ms difference packetCounter++; Program.server.NewReportIncoming(this); @@ -382,19 +380,19 @@ namespace BetterJoyForCemu { private void Poll() { int attempts = 0; while (!stop_polling & state > state_.NO_JOYCONS) { - SendRumble(rumble_obj.GetData()); + //SendRumble(rumble_obj.GetData()); int a = ReceiveRaw(); //a = ReceiveRaw(); if (a > 0) { state = state_.IMU_DATA_OK; attempts = 0; - } else if (attempts > 1000) { + } else if (attempts > 10000) { state = state_.DROPPED; DebugPrint("Connection lost. Is the Joy-Con connected?", DebugType.ALL); break; } else { DebugPrint("Pause 5ms", DebugType.THREADING); - Thread.Sleep((Int32)5); + Thread.Sleep((Int32)(!isPro ? 1 : 5)); } ++attempts; } @@ -412,6 +410,9 @@ namespace BetterJoyForCemu { } } } + + public float[] otherStick = { 0, 0 }; + private int ProcessButtonsAndStick(byte[] report_buf) { if (report_buf[0] == 0x00) return -1; @@ -427,14 +428,27 @@ namespace BetterJoyForCemu { stick_precal[0] = (UInt16)(stick_raw[0] | ((stick_raw[1] & 0xf) << 8)); stick_precal[1] = (UInt16)((stick_raw[1] >> 4) | (stick_raw[2] << 4)); - stick = CenterSticks(stick_precal); + stick = CenterSticks(stick_precal, stick_cal, deadzone); if (isPro) { stick2_precal[0] = (UInt16)(stick2_raw[0] | ((stick2_raw[1] & 0xf) << 8)); stick2_precal[1] = (UInt16)((stick2_raw[1] >> 4) | (stick2_raw[2] << 4)); - stick2 = CenterSticks(stick2_precal, true); + stick2 = CenterSticks(stick2_precal, stick2_cal, deadzone2); } + // Read other Joycon's sticks + if (isLeft && other != null) { + stick2 = otherStick; + other.otherStick = stick; + } + + if (!isLeft && other != null) { + Array.Copy(stick, stick2, 2); + stick = otherStick; + other.otherStick = stick2; + } + // + lock (buttons) { lock (down_) { for (int i = 0; i < buttons.Length; ++i) { @@ -465,6 +479,33 @@ namespace BetterJoyForCemu { buttons[(int)Button.SHOULDER2_2] = (report_buf[3 + (!isLeft ? 2 : 0)] & 0x80) != 0; } + if (isLeft && other != null) { + buttons[(int)Button.B] = other.buttons[(int)Button.DPAD_DOWN]; + buttons[(int)Button.A] = other.buttons[(int)Button.DPAD_RIGHT]; + buttons[(int)Button.X] = other.buttons[(int)Button.DPAD_UP]; + buttons[(int)Button.Y] = other.buttons[(int)Button.DPAD_LEFT]; + + buttons[(int)Button.STICK2] = other.buttons[(int)Button.STICK]; + buttons[(int)Button.SHOULDER2_1] = other.buttons[(int)Button.SHOULDER_1]; + buttons[(int)Button.SHOULDER2_2] = other.buttons[(int)Button.SHOULDER_2]; + + buttons[(int)Button.HOME] = other.buttons[(int)Button.HOME]; + buttons[(int)Button.PLUS] = other.buttons[(int)Button.PLUS]; + } + + if (!isLeft && other != null) { + buttons[(int)Button.B] = other.buttons[(int)Button.DPAD_DOWN]; + buttons[(int)Button.A] = other.buttons[(int)Button.DPAD_RIGHT]; + buttons[(int)Button.X] = other.buttons[(int)Button.DPAD_UP]; + buttons[(int)Button.Y] = other.buttons[(int)Button.DPAD_LEFT]; + + buttons[(int)Button.STICK2] = other.buttons[(int)Button.STICK]; + buttons[(int)Button.SHOULDER2_1] = other.buttons[(int)Button.SHOULDER_1]; + buttons[(int)Button.SHOULDER2_2] = other.buttons[(int)Button.SHOULDER_2]; + + buttons[(int)Button.MINUS] = other.buttons[(int)Button.MINUS]; + } + lock (buttons_up) { lock (buttons_down) { for (int i = 0; i < buttons.Length; ++i) { @@ -500,13 +541,13 @@ namespace BetterJoyForCemu { break; case 1: - acc_g.Y = (acc_r[i] - offset[i]) * (1.0f / (acc_sensiti[i] - acc_neutral[i])) * 4.0f; - gyr_g.Y = -(gyr_r[i] - gyr_neutral[i] * 0.5f) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i])); + acc_g.Y = (!isLeft ? -1 : 1) * (acc_r[i] - offset[i]) * (1.0f / (acc_sensiti[i] - acc_neutral[i])) * 4.0f; + gyr_g.Y = -(!isLeft ? -1 : 1) * (gyr_r[i] - gyr_neutral[i] * 0.5f) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i])); break; case 2: - acc_g.Z = (acc_r[i] - offset[i]) * (1.0f / (acc_sensiti[i] - acc_neutral[i])) * 4.0f; - gyr_g.Z = -(gyr_r[i] - gyr_neutral[i]) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i])); + acc_g.Z = (!isLeft ? -1 : 1) * (acc_r[i] - offset[i]) * (1.0f / (acc_sensiti[i] - acc_neutral[i])) * 4.0f; + gyr_g.Z = -(!isLeft ? -1 : 1) * (gyr_r[i] - gyr_neutral[i] * 0.5f) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i])); break; } @@ -526,16 +567,13 @@ namespace BetterJoyForCemu { first_imu_packet = true; } - private float[] CenterSticks(UInt16[] vals, bool special = false) { - ushort[] t = stick_cal; - - if (special) - t = stick2_cal; + private float[] CenterSticks(UInt16[] vals, ushort[] cal, ushort dz) { + ushort[] t = cal; float[] s = { 0, 0 }; for (uint i = 0; i < 2; ++i) { float diff = vals[i] - t[2 + i]; - if (Math.Abs(diff) < deadzone) vals[i] = 0; + if (Math.Abs(diff) < dz) vals[i] = 0; else if (diff > 0) // if axis is above center { s[i] = diff / t[i]; diff --git a/BetterJoyForCemu/Program.cs b/BetterJoyForCemu/Program.cs index 38daf15..ff38eb5 100644 --- a/BetterJoyForCemu/Program.cs +++ b/BetterJoyForCemu/Program.cs @@ -18,9 +18,6 @@ namespace BetterJoyForCemu { public bool EnableIMU = true; public bool EnableLocalize = false; - - - // Different operating systems either do or don't like the trailing zero private const ushort vendor_id = 0x57e; private const ushort vendor_id_ = 0x057e; private const ushort product_l = 0x2006; @@ -53,7 +50,6 @@ namespace BetterJoyForCemu { } } - string path = ""; hid_device_info enumerate; while (ptr != IntPtr.Zero) { enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info)); @@ -73,7 +69,12 @@ namespace BetterJoyForCemu { } IntPtr handle = HIDapi.hid_open_path(enumerate.path); - HIDapi.hid_set_nonblocking(handle, 1); + try { + HIDapi.hid_set_nonblocking(handle, 1); + } catch (Exception e) { + Console.WriteLine("Unable to open path to device - are you using the correct (64 vs 32-bit) version for your PC?"); + break; + } j.Add(new Joycon(handle, EnableIMU, EnableLocalize & EnableIMU, 0.05f, isLeft, j.Count, enumerate.product_id == product_pro, enumerate.serial_number == "000000000001")); @@ -87,6 +88,39 @@ namespace BetterJoyForCemu { 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) { + Console.WriteLine("Both joycons successfully found."); + 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) + Console.WriteLine("Only one joycon found. Please connect both and then restart the program."); + HIDapi.hid_free_enumeration(top_ptr); } diff --git a/BetterJoyForCemu/UpdServer.cs b/BetterJoyForCemu/UpdServer.cs index 0b362ac..e2db123 100644 --- a/BetterJoyForCemu/UpdServer.cs +++ b/BetterJoyForCemu/UpdServer.cs @@ -292,7 +292,8 @@ namespace BetterJoyForCemu { udpSock.Close(); udpSock = null; - throw ex; + Console.WriteLine("Could not start server. Make sure that only one instance of the program is running at a time and no other CemuHook applications are running."); + return; } byte[] randomBuf = new byte[4]; @@ -315,27 +316,29 @@ namespace BetterJoyForCemu { private bool ReportToBuffer(Joycon hidReport, byte[] outputData, ref int outIdx) { outputData[outIdx] = 0; - if (hidReport.GetButton(Joycon.Button.DPAD_LEFT)) outputData[outIdx] |= 0x80; - if (hidReport.GetButton(Joycon.Button.DPAD_DOWN)) outputData[outIdx] |= 0x40; - if (hidReport.GetButton(Joycon.Button.DPAD_RIGHT)) outputData[outIdx] |= 0x20; - if (hidReport.GetButton(Joycon.Button.DPAD_UP)) outputData[outIdx] |= 0x10; + bool isLeft = hidReport.isLeft; - if (hidReport.GetButton(Joycon.Button.PLUS)) outputData[outIdx] |= 0x08; - if (hidReport.GetButton(Joycon.Button.STICK2)) outputData[outIdx] |= 0x04; - if (hidReport.GetButton(Joycon.Button.STICK)) outputData[outIdx] |= 0x02; - if (hidReport.GetButton(Joycon.Button.MINUS)) outputData[outIdx] |= 0x01; + if (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_LEFT : Joycon.Button.A)) outputData[outIdx] |= 0x80; + if (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_DOWN : Joycon.Button.B)) outputData[outIdx] |= 0x40; + if (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_RIGHT : Joycon.Button.Y)) outputData[outIdx] |= 0x20; + if (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_UP : Joycon.Button.X)) outputData[outIdx] |= 0x10; + + if (hidReport.GetButton(Joycon.Button.PLUS)) outputData[outIdx] |= 0x08; + if (hidReport.GetButton(isLeft ? Joycon.Button.STICK2 : Joycon.Button.STICK)) outputData[outIdx] |= 0x04; + if (hidReport.GetButton(isLeft ? Joycon.Button.STICK : Joycon.Button.STICK2)) outputData[outIdx] |= 0x02; + if (hidReport.GetButton(Joycon.Button.MINUS)) outputData[outIdx] |= 0x01; outputData[++outIdx] = 0; - if (hidReport.GetButton(Joycon.Button.Y)) outputData[outIdx] |= 0x80; - if (hidReport.GetButton(Joycon.Button.B)) outputData[outIdx] |= 0x40; - if (hidReport.GetButton(Joycon.Button.A)) outputData[outIdx] |= 0x20; - if (hidReport.GetButton(Joycon.Button.X)) outputData[outIdx] |= 0x10; + if (hidReport.GetButton(isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT)) outputData[outIdx] |= 0x80; + if (hidReport.GetButton(isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN)) outputData[outIdx] |= 0x40; + if (hidReport.GetButton(isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT)) outputData[outIdx] |= 0x20; + if (hidReport.GetButton(isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP)) outputData[outIdx] |= 0x10; - if (hidReport.GetButton(Joycon.Button.SHOULDER2_1)) outputData[outIdx] |= 0x08; - if (hidReport.GetButton(Joycon.Button.SHOULDER_1)) outputData[outIdx] |= 0x04; - if (hidReport.GetButton(Joycon.Button.SHOULDER2_2)) outputData[outIdx] |= 0x02; - if (hidReport.GetButton(Joycon.Button.SHOULDER_2)) outputData[outIdx] |= 0x01; + if (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER2_1 : Joycon.Button.SHOULDER_1)) outputData[outIdx] |= 0x08; + if (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER_1 : Joycon.Button.SHOULDER2_1)) outputData[outIdx] |= 0x04; + if (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER2_2 : Joycon.Button.SHOULDER_2)) outputData[outIdx] |= 0x02; + if (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER_2 : Joycon.Button.SHOULDER2_2)) outputData[outIdx] |= 0x01; outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.HOME)) ? (byte)1 : (byte)0; outputData[++outIdx] = 0; // no touch pad @@ -349,21 +352,21 @@ namespace BetterJoyForCemu { outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + rightStick[1] * 127))); //we don't have analog buttons so just use the Button enums (which give either 0 or 0xFF) - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.DPAD_LEFT)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.DPAD_DOWN)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.DPAD_RIGHT)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.DPAD_UP)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_LEFT : Joycon.Button.A)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_DOWN : Joycon.Button.B)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_RIGHT : Joycon.Button.Y)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_UP : Joycon.Button.X)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.Y)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.B)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.A)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.X)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.SHOULDER2_1)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.SHOULDER_1)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER2_1 : Joycon.Button.SHOULDER_1)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER_1 : Joycon.Button.SHOULDER2_1)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.SHOULDER2_2)) ? (byte)0xFF : (byte)0; - outputData[++outIdx] = (hidReport.GetButton(Joycon.Button.SHOULDER_2)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER2_2 : Joycon.Button.SHOULDER_2)) ? (byte)0xFF : (byte)0; + outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.SHOULDER_2 : Joycon.Button.SHOULDER2_2)) ? (byte)0xFF : (byte)0; outIdx++; @@ -384,7 +387,7 @@ namespace BetterJoyForCemu { //Array.Copy(BitConverter.GetBytes((float)0), 0, outputData, outIdx, 4); outIdx += 4; Array.Copy(BitConverter.GetBytes(-accel.Z), 0, outputData, outIdx, 4); - //Array.Copy(BitConverter.GetBytes((float)-1), 0, outputData, outIdx, 4); + //Array.Copy(BitConverter.GetBytes((float)0), 0, outputData, outIdx, 4); outIdx += 4; Array.Copy(BitConverter.GetBytes(accel.X), 0, outputData, outIdx, 4); //Array.Copy(BitConverter.GetBytes((float)0), 0, outputData, outIdx, 4); diff --git a/BetterJoyForCemu/hidapi.dll b/BetterJoyForCemu/hidapi.dll index bfb6c27..28c267a 100644 Binary files a/BetterJoyForCemu/hidapi.dll and b/BetterJoyForCemu/hidapi.dll differ