diff --git a/BetterJoyForCemu/BetterJoy.csproj b/BetterJoyForCemu/BetterJoy.csproj index 5de20ab7..8ee409fa 100644 --- a/BetterJoyForCemu/BetterJoy.csproj +++ b/BetterJoyForCemu/BetterJoy.csproj @@ -239,6 +239,7 @@ Always + diff --git a/BetterJoyForCemu/Icons/nes.png b/BetterJoyForCemu/Icons/nes.png new file mode 100644 index 00000000..162afd5a Binary files /dev/null and b/BetterJoyForCemu/Icons/nes.png differ diff --git a/BetterJoyForCemu/Joycon.cs b/BetterJoyForCemu/Joycon.cs index b3cede83..ce642f36 100644 --- a/BetterJoyForCemu/Joycon.cs +++ b/BetterJoyForCemu/Joycon.cs @@ -16,6 +16,7 @@ public class Joycon { public string path = String.Empty; public bool isPro = false; public bool isSnes = false; + public bool isNes = false; public bool is64 = false; bool isUSB = false; private Joycon _other = null; @@ -438,6 +439,28 @@ public int Attach() { Subcommand(0x48, new byte[] { 0x01 }, 1); Subcommand(0x3, new byte[] { 0x30 }, 1); + + //Make sure we're not actually a nes controller + if (!this.isLeft) { + byte[] resp; + + for (int i = 0; i < 100; ++i) { + resp = Subcommand(0x02, Array.Empty(), 0, false, 200); + + if (resp.Length >= 20 && resp[0] == 0x21 && resp[14] == 0x02) + { + if (resp[17] == 0x0A || resp[17] == 0x09) //NES controllers share the Right hardware ID of the right joycon, but respond here differently. + { + this.isLeft = true; + this.isPro = true; + this.isNes = true; + } + + break; + } + } + } + DebugPrint("Done with init.", DebugType.COMMS); HIDapi.hid_set_nonblocking(handle, 1); @@ -510,7 +533,7 @@ private void BatteryChanged() { // battery changed level if (battery <= 1) { form.notifyIcon.Visible = true; - form.notifyIcon.BalloonTipText = String.Format("Controller {0} ({1}) - low battery notification!", PadId, isPro ? "Pro Controller" : (isSnes ? "SNES Controller" : (is64? "N64 Controller" : (isLeft ? "Joycon Left" : "Joycon Right")))); + form.notifyIcon.BalloonTipText = String.Format("Controller {0} ({1}) - low battery notification!", PadId, isPro ? "Pro Controller" : isNes ? "NES Controller" : isLeft ? "Joycon Left" : "Joycon Right"); form.notifyIcon.ShowBalloonTip(0); } } @@ -599,7 +622,7 @@ private int ReceiveRaw() { } - if (ts_en == raw_buf[1] && !(isSnes || is64)) { + if (ts_en == raw_buf[1] && !(isSnes || isNes || is64)) { form.AppendTextBox("Duplicate timestamp enqueued.\r\n"); DebugPrint(string.Format("Duplicate timestamp enqueued. TS: {0:X2}", ts_en), DebugType.THREADING); } @@ -906,7 +929,7 @@ private void Poll() { private int ProcessButtonsAndStick(byte[] report_buf) { if (report_buf[0] == 0x00) throw new ArgumentException("received undefined report. This is probably a bug"); - if (!isSnes) { + if (!isSnes && !isNes) { stick_raw[0] = report_buf[6 + (isLeft ? 0 : 3)]; stick_raw[1] = report_buf[7 + (isLeft ? 0 : 3)]; stick_raw[2] = report_buf[8 + (isLeft ? 0 : 3)]; @@ -1019,7 +1042,7 @@ private int ProcessButtonsAndStick(byte[] report_buf) { // Get Gyro/Accel data private void ExtractIMUValues(byte[] report_buf, int n = 0) { - if (!(isSnes || is64)) { + if (!(isSnes || is64 || isNes)) { gyr_r[0] = (Int16)(report_buf[19 + n * 12] | ((report_buf[20 + n * 12] << 8) & 0xff00)); gyr_r[1] = (Int16)(report_buf[21 + n * 12] | ((report_buf[22 + n * 12] << 8) & 0xff00)); gyr_r[2] = (Int16)(report_buf[23 + n * 12] | ((report_buf[24 + n * 12] << 8) & 0xff00)); @@ -1167,7 +1190,7 @@ private void SendRumble(byte[] buf) { HIDapi.hid_write(handle, buf_, new UIntPtr(report_len)); } - private byte[] Subcommand(byte sc, byte[] buf, uint len, bool print = true) { + private byte[] Subcommand(byte sc, byte[] buf, uint len, bool print = true, int retries = 10) { byte[] buf_ = new byte[report_len]; byte[] response = new byte[report_len]; Array.Copy(default_buf, 0, buf_, 2, 8); @@ -1185,13 +1208,13 @@ private byte[] Subcommand(byte sc, byte[] buf, uint len, bool print = true) { if (res < 1) DebugPrint("No response.", DebugType.COMMS); else if (print) { PrintArray(response, DebugType.COMMS, report_len - 1, 1, "Response ID 0x" + string.Format("{0:X2}", response[0]) + ". Data: 0x{0:S}"); } tries++; - } while (tries < 10 && response[0] != 0x21 && response[14] != sc); + } while (tries < retries && response[0] != 0x21 && response[14] != sc); return response; } private void dump_calibration_data() { - if (isSnes || is64 || thirdParty) { + if (isSnes || is64 || thirdParty || isNes) { short[] temp = (short[])ConfigurationManager.AppSettings["acc_sensiti"].Split(',').Select(s => short.Parse(s)).ToArray(); acc_sensiti[0] = temp[0]; acc_sensiti[1] = temp[1]; acc_sensiti[2] = temp[2]; temp = (short[])ConfigurationManager.AppSettings["gyr_sensiti"].Split(',').Select(s => short.Parse(s)).ToArray(); @@ -1431,6 +1454,7 @@ private static OutputControllerXbox360InputState MapToXbox360Input(Joycon input) var isPro = input.isPro; var isLeft = input.isLeft; var isSnes = input.isSnes; + var isNes = input.isNes; var is64 = input.is64; var other = input.other; var GyroAnalogSliders = input.GyroAnalogSliders; @@ -1528,7 +1552,7 @@ private static OutputControllerXbox360InputState MapToXbox360Input(Joycon input) if (Config.Value("home") != "0") output.guide = false; - if (!(isSnes || is64)) { + if (!(isSnes || is64 || isNes)) { if (other != null || isPro) { // no need for && other != this output.axis_left_x = CastStickValue((other == input && !isLeft) ? stick2[0] : stick[0]); output.axis_left_y = CastStickValue((other == input && !isLeft) ? stick2[1] : stick[1]); @@ -1566,6 +1590,7 @@ public static OutputControllerDualShock4InputState MapToDualShock4Input(Joycon i var isPro = input.isPro; var isLeft = input.isLeft; var isSnes = input.isSnes; + var isNes = input.isNes; var is64 = input.is64; var other = input.other; var GyroAnalogSliders = input.GyroAnalogSliders; @@ -1704,7 +1729,7 @@ public static OutputControllerDualShock4InputState MapToDualShock4Input(Joycon i if (Config.Value("home") != "0") output.ps = false; - if (!(isSnes || is64)) { + if (!(isSnes || is64 || isNes)) { if (other != null || isPro) { // no need for && other != this output.thumb_left_x = CastStickValueByte((other == input && !isLeft) ? -stick2[0] : -stick[0]); output.thumb_left_y = CastStickValueByte((other == input && !isLeft) ? stick2[1] : stick[1]); diff --git a/BetterJoyForCemu/Program.cs b/BetterJoyForCemu/Program.cs index 40e894d9..a10aa8d4 100644 --- a/BetterJoyForCemu/Program.cs +++ b/BetterJoyForCemu/Program.cs @@ -214,7 +214,8 @@ public void CheckForNewControllers() { case (product_l): temp = Properties.Resources.jc_left_s; break; case (product_r): - temp = Properties.Resources.jc_right_s; break; + temp = Properties.Resources.jc_right_s; + break; case (product_pro): temp = Properties.Resources.pro; break; case (product_snes): @@ -254,6 +255,36 @@ public void CheckForNewControllers() { ptr = enumerate.next; } + + bool on = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings.Settings["HomeLEDOn"].Value.ToLower() == "true"; + foreach (Joycon jc in j) { // Connect device straight away + if (jc.state == Joycon.state_.NOT_ATTACHED) { + if (jc.out_xbox != null) + jc.out_xbox.Connect(); + if (jc.out_ds4 != null) + jc.out_ds4.Connect(); + + try { + jc.Attach(); + } catch (Exception e) { + jc.state = Joycon.state_.DROPPED; + continue; + } + + jc.SetHomeLight(on); + + jc.Begin(); + if (form.allowCalibration) { + jc.getActiveData(); + } + } + } + + foreach (Button b in form.con) { + if (b.Enabled && b.Tag is Joycon jc && jc.isNes) { + b.BackgroundImage = Properties.Resources.nes; + } + } if (foundNew) { // attempt to auto join-up joycons on connection Joycon temp = null; @@ -303,29 +334,7 @@ public void CheckForNewControllers() { HIDapi.hid_free_enumeration(top_ptr); - bool on = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings.Settings["HomeLEDOn"].Value.ToLower() == "true"; - foreach (Joycon jc in j) { // Connect device straight away - if (jc.state == Joycon.state_.NOT_ATTACHED) { - if (jc.out_xbox != null) - jc.out_xbox.Connect(); - if (jc.out_ds4 != null) - jc.out_ds4.Connect(); - - try { - jc.Attach(); - } catch (Exception e) { - jc.state = Joycon.state_.DROPPED; - continue; - } - - jc.SetHomeLight(on); - - jc.Begin(); - if (form.allowCalibration) { - jc.getActiveData(); - } - } - } + } public void OnApplicationQuit() { diff --git a/BetterJoyForCemu/Properties/Resources.Designer.cs b/BetterJoyForCemu/Properties/Resources.Designer.cs index 1d3c1e03..6fb190ce 100644 --- a/BetterJoyForCemu/Properties/Resources.Designer.cs +++ b/BetterJoyForCemu/Properties/Resources.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -9,145 +8,155 @@ //------------------------------------------------------------------------------ namespace BetterJoyForCemu.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BetterJoyForCemu.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). - /// - internal static System.Drawing.Icon betterjoyforcemu_icon { - get { - object obj = ResourceManager.GetObject("betterjoyforcemu_icon", resourceCulture); - return ((System.Drawing.Icon)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap cross { - get { - object obj = ResourceManager.GetObject("cross", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap jc_left { - get { - object obj = ResourceManager.GetObject("jc_left", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap jc_left_s { - get { - object obj = ResourceManager.GetObject("jc_left_s", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap jc_right { - get { - object obj = ResourceManager.GetObject("jc_right", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap jc_right_s { - get { - object obj = ResourceManager.GetObject("jc_right_s", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap pro { - get { - object obj = ResourceManager.GetObject("pro", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap snes { - get { - object obj = ResourceManager.GetObject("snes", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BetterJoyForCemu.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon betterjoyforcemu_icon { + get { + object obj = ResourceManager.GetObject("betterjoyforcemu_icon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap cross { + get { + object obj = ResourceManager.GetObject("cross", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap jc_left { + get { + object obj = ResourceManager.GetObject("jc_left", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap jc_left_s { + get { + object obj = ResourceManager.GetObject("jc_left_s", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap jc_right { + get { + object obj = ResourceManager.GetObject("jc_right", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap jc_right_s { + get { + object obj = ResourceManager.GetObject("jc_right_s", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap nes { + get { + object obj = ResourceManager.GetObject("nes", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap pro { + get { + object obj = ResourceManager.GetObject("pro", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap snes { + get { + object obj = ResourceManager.GetObject("snes", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap ultra { - get { - object obj = ResourceManager.GetObject("ultra", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - } + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ultra { + get { + object obj = ResourceManager.GetObject("ultra", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } } diff --git a/BetterJoyForCemu/Properties/Resources.resx b/BetterJoyForCemu/Properties/Resources.resx index 08c8d17b..55b93c40 100644 --- a/BetterJoyForCemu/Properties/Resources.resx +++ b/BetterJoyForCemu/Properties/Resources.resx @@ -121,6 +121,9 @@ ..\Icons\snes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Icons\nes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Icons\ultra.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/README.md b/README.md index c0166fa6..0794b576 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Allows the Nintendo Switch Pro Controller, Joycons, and Switch SNES controller t It also allows using the gyro to control your mouse and remap the special buttons (SL, SR, Capture) to key bindings of your choice. -If anyone would like to donate (for whatever reason), [you can do so here](https://www.paypal.me/DavidKhachaturov/5). +If anyone would like to donate (for whatever reason), [you can do so here](https://www.paypal.me/DavidKhachaturov/5). #### Personal note Thank you for using my software and all the constructive feedback I've been getting about it. I started writing this project a while back and have since then learnt a lot more about programming and software development in general. I don't have too much time to work on this project, but I will try to fix bugs when and if they arise. Thank you for your patience in that regard too! @@ -25,7 +25,7 @@ Go to the [Releases tab](https://github.com/Davidobot/BetterJoy/releases/)! 1. Read the READMEs (they're there for a reason!) 1. Run *Drivers/ViGEmBus_Setup_1.16.116.exe* 1. Restart your computer -2. Run *BetterJoyForCemu.exe* +2. Run *BetterJoyForCemu.exe* 1. Run as Administrator if your keyboard/mouse button mappings don't work 3. Connect your controllers. 4. Start Cemu and ensure CemuHook has the controller selected. @@ -47,7 +47,7 @@ Check out the [wiki](https://github.com/Davidobot/BetterJoy/wiki)! There, you'll ## USB Mode * Plug the controller into your computer. - + ## Disconnecting \[Windows 10] 1. Go into "Bluetooth and other devices settings" 1. Under the first category "Mouse, keyboard, & pen", there should be the pro controller. @@ -90,7 +90,7 @@ The built binaries are located under *BetterJoyForCemu\bin\PLATFORM\CONFIGURATION* -where `PLATFORM` and `CONFIGURATION` are the one provided at build time. +where `PLATFORM` and `CONFIGURATION` are the one provided at build time. # Acknowledgements A massive thanks goes out to [rajkosto](https://github.com/rajkosto/) for putting up with 17 emails and replying very quickly to my silly queries. The UDP server is also mostly taken from his [ScpToolkit](https://github.com/rajkosto/ScpToolkit) repo. @@ -104,4 +104,4 @@ A last thanks goes out to [dekuNukem](https://github.com/dekuNukem/Nintendo_Swit Massive *thank you* to **all** code contributors! Icons (modified): "[Switch Pro Controller](https://thenounproject.com/term/nintendo-switch/930119/)", "[ -Switch Detachable Controller Left](https://thenounproject.com/remsing/uploads/?i=930115)", "[Switch Detachable Controller Right](https://thenounproject.com/remsing/uploads/?i=930121)" icons by Chad Remsing from [the Noun Project](http://thenounproject.com/). [Super Nintendo Controller](https://thenounproject.com/themizarkshow/collection/vectogram/?i=193592) icon by Mark Davis from the [the Noun Project](http://thenounproject.com/); icon modified by [Amy Alexander](https://www.linkedin.com/in/-amy-alexander/). [Nintendo 64 Controller](https://thenounproject.com/icon/game-controller-193588/) icon by Mark Davis from the [the Noun Project](http://thenounproject.com/); icon modified by [Gino Moena](https://www.github.com/GinoMoena). +Switch Detachable Controller Left](https://thenounproject.com/remsing/uploads/?i=930115)", "[Switch Detachable Controller Right](https://thenounproject.com/remsing/uploads/?i=930121)" icons by Chad Remsing from [the Noun Project](http://thenounproject.com/). [Super Nintendo Controller](https://thenounproject.com/themizarkshow/collection/vectogram/?i=193592) icon by Mark Davis from the [the Noun Project](http://thenounproject.com/); icon modified by [Amy Alexander](https://www.linkedin.com/in/-amy-alexander/). [Nintendo 64 Controller](https://thenounproject.com/icon/game-controller-193588/) icon by Mark Davis from the [the Noun Project](http://thenounproject.com/); icon modified by [Gino Moena](https://www.github.com/GinoMoena); [NES Controller](https://thenounproject.com/icon/nes-gamepad-949405/) icon by Viktor Korobkov from [the Noun Project](http://thenounproject.com/); icon modified by anon.