diff --git a/BetterJoyForCemu/.editorconfig b/BetterJoyForCemu/.editorconfig
index 4a55feb..f0b4f77 100644
--- a/BetterJoyForCemu/.editorconfig
+++ b/BetterJoyForCemu/.editorconfig
@@ -5,7 +5,8 @@
root = true
# All files
[*]
-indent_style = tab
+indent_size = 4
+indent_style = space
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
diff --git a/BetterJoyForCemu/3rdPartyControllers.Designer.cs b/BetterJoyForCemu/3rdPartyControllers.Designer.cs
index 70844e8..11e087e 100644
--- a/BetterJoyForCemu/3rdPartyControllers.Designer.cs
+++ b/BetterJoyForCemu/3rdPartyControllers.Designer.cs
@@ -1,212 +1,212 @@
namespace BetterJoyForCemu {
- partial class _3rdPartyControllers {
- ///
- /// Required designer variable.
- ///
- private System.ComponentModel.IContainer components = null;
+ partial class _3rdPartyControllers {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
- ///
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing) {
- if (disposing && (components != null)) {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing) {
+ if (disposing && (components != null)) {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
- #region Windows Form Designer generated code
+ #region Windows Form Designer generated code
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent() {
- this.components = new System.ComponentModel.Container();
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(_3rdPartyControllers));
- this.list_allControllers = new System.Windows.Forms.ListBox();
- this.list_customControllers = new System.Windows.Forms.ListBox();
- this.btn_add = new System.Windows.Forms.Button();
- this.btn_remove = new System.Windows.Forms.Button();
- this.group_props = new System.Windows.Forms.GroupBox();
- this.chk_isLeft = new System.Windows.Forms.CheckBox();
- this.chk_isPro = new System.Windows.Forms.CheckBox();
- this.btn_applyAndClose = new System.Windows.Forms.Button();
- this.btn_apply = new System.Windows.Forms.Button();
- this.lbl_all = new System.Windows.Forms.Label();
- this.label1 = new System.Windows.Forms.Label();
- this.tip_device = new System.Windows.Forms.ToolTip(this.components);
- this.btn_refresh = new System.Windows.Forms.Button();
- this.group_props.SuspendLayout();
- this.SuspendLayout();
- //
- // list_allControllers
- //
- this.list_allControllers.FormattingEnabled = true;
- this.list_allControllers.Location = new System.Drawing.Point(12, 27);
- this.list_allControllers.Name = "list_allControllers";
- this.list_allControllers.Size = new System.Drawing.Size(103, 225);
- this.list_allControllers.TabIndex = 0;
- this.list_allControllers.SelectedValueChanged += new System.EventHandler(this.list_allControllers_SelectedValueChanged);
- this.list_allControllers.MouseDown += new System.Windows.Forms.MouseEventHandler(this.list_allControllers_MouseDown);
- //
- // list_customControllers
- //
- this.list_customControllers.FormattingEnabled = true;
- this.list_customControllers.Location = new System.Drawing.Point(169, 27);
- this.list_customControllers.Name = "list_customControllers";
- this.list_customControllers.Size = new System.Drawing.Size(103, 108);
- this.list_customControllers.TabIndex = 1;
- this.list_customControllers.SelectedValueChanged += new System.EventHandler(this.list_customControllers_SelectedValueChanged);
- this.list_customControllers.MouseDown += new System.Windows.Forms.MouseEventHandler(this.list_customControllers_MouseDown);
- //
- // btn_add
- //
- this.btn_add.Location = new System.Drawing.Point(121, 27);
- this.btn_add.Name = "btn_add";
- this.btn_add.Size = new System.Drawing.Size(42, 23);
- this.btn_add.TabIndex = 2;
- this.btn_add.Text = "->";
- this.btn_add.UseVisualStyleBackColor = true;
- this.btn_add.Click += new System.EventHandler(this.btn_add_Click);
- //
- // btn_remove
- //
- this.btn_remove.Location = new System.Drawing.Point(121, 112);
- this.btn_remove.Name = "btn_remove";
- this.btn_remove.Size = new System.Drawing.Size(42, 23);
- this.btn_remove.TabIndex = 3;
- this.btn_remove.Text = "<-";
- this.btn_remove.UseVisualStyleBackColor = true;
- this.btn_remove.Click += new System.EventHandler(this.btn_remove_Click);
- //
- // group_props
- //
- this.group_props.Controls.Add(this.chk_isLeft);
- this.group_props.Controls.Add(this.chk_isPro);
- this.group_props.Location = new System.Drawing.Point(122, 142);
- this.group_props.Name = "group_props";
- this.group_props.Size = new System.Drawing.Size(150, 81);
- this.group_props.TabIndex = 4;
- this.group_props.TabStop = false;
- this.group_props.Text = "Settings";
- //
- // chk_isLeft
- //
- this.chk_isLeft.AutoSize = true;
- this.chk_isLeft.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
- this.chk_isLeft.Location = new System.Drawing.Point(6, 42);
- this.chk_isLeft.Name = "chk_isLeft";
- this.chk_isLeft.Size = new System.Drawing.Size(96, 17);
- this.chk_isLeft.TabIndex = 1;
- this.chk_isLeft.Text = "Left Joycon? ";
- this.chk_isLeft.UseVisualStyleBackColor = true;
- this.chk_isLeft.CheckedChanged += new System.EventHandler(this.chk_isLeft_CheckedChanged);
- //
- // chk_isPro
- //
- this.chk_isPro.AutoSize = true;
- this.chk_isPro.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
- this.chk_isPro.Location = new System.Drawing.Point(6, 19);
- this.chk_isPro.Name = "chk_isPro";
- this.chk_isPro.Size = new System.Drawing.Size(95, 17);
- this.chk_isPro.TabIndex = 0;
- this.chk_isPro.Text = "Pro Controller?";
- this.chk_isPro.UseVisualStyleBackColor = true;
- this.chk_isPro.CheckedChanged += new System.EventHandler(this.chk_isPro_CheckedChanged);
- //
- // btn_applyAndClose
- //
- this.btn_applyAndClose.Location = new System.Drawing.Point(203, 229);
- this.btn_applyAndClose.Name = "btn_applyAndClose";
- this.btn_applyAndClose.Size = new System.Drawing.Size(69, 23);
- this.btn_applyAndClose.TabIndex = 5;
- this.btn_applyAndClose.Text = "Close";
- this.btn_applyAndClose.UseVisualStyleBackColor = true;
- this.btn_applyAndClose.Click += new System.EventHandler(this.btn_applyAndClose_Click);
- //
- // btn_apply
- //
- this.btn_apply.Location = new System.Drawing.Point(121, 229);
- this.btn_apply.Name = "btn_apply";
- this.btn_apply.Size = new System.Drawing.Size(69, 23);
- this.btn_apply.TabIndex = 6;
- this.btn_apply.Text = "Apply";
- this.btn_apply.UseVisualStyleBackColor = true;
- this.btn_apply.Click += new System.EventHandler(this.btn_apply_Click);
- //
- // lbl_all
- //
- this.lbl_all.AutoSize = true;
- this.lbl_all.Location = new System.Drawing.Point(12, 11);
- this.lbl_all.Name = "lbl_all";
- this.lbl_all.Size = new System.Drawing.Size(60, 13);
- this.lbl_all.TabIndex = 7;
- this.lbl_all.Text = "All Devices";
- //
- // label1
- //
- this.label1.AutoSize = true;
- this.label1.Location = new System.Drawing.Point(166, 11);
- this.label1.Name = "label1";
- this.label1.Size = new System.Drawing.Size(91, 13);
- this.label1.TabIndex = 8;
- this.label1.Text = "Switch Controllers";
- //
- // btn_refresh
- //
- this.btn_refresh.Location = new System.Drawing.Point(121, 56);
- this.btn_refresh.Name = "btn_refresh";
- this.btn_refresh.Size = new System.Drawing.Size(42, 50);
- this.btn_refresh.TabIndex = 9;
- this.btn_refresh.Text = "Re-\r\nfresh";
- this.btn_refresh.UseVisualStyleBackColor = true;
- this.btn_refresh.Click += new System.EventHandler(this.btn_refresh_Click);
- //
- // _3rdPartyControllers
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(284, 261);
- this.Controls.Add(this.btn_refresh);
- this.Controls.Add(this.label1);
- this.Controls.Add(this.lbl_all);
- this.Controls.Add(this.btn_apply);
- this.Controls.Add(this.btn_applyAndClose);
- this.Controls.Add(this.group_props);
- this.Controls.Add(this.btn_remove);
- this.Controls.Add(this.btn_add);
- this.Controls.Add(this.list_customControllers);
- this.Controls.Add(this.list_allControllers);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
- this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "_3rdPartyControllers";
- this.Text = "Add 3rd-Party Controllers";
- this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this._3rdPartyControllers_FormClosing);
- this.group_props.ResumeLayout(false);
- this.group_props.PerformLayout();
- this.ResumeLayout(false);
- this.PerformLayout();
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent() {
+ this.components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(_3rdPartyControllers));
+ this.list_allControllers = new System.Windows.Forms.ListBox();
+ this.list_customControllers = new System.Windows.Forms.ListBox();
+ this.btn_add = new System.Windows.Forms.Button();
+ this.btn_remove = new System.Windows.Forms.Button();
+ this.group_props = new System.Windows.Forms.GroupBox();
+ this.chk_isLeft = new System.Windows.Forms.CheckBox();
+ this.chk_isPro = new System.Windows.Forms.CheckBox();
+ this.btn_applyAndClose = new System.Windows.Forms.Button();
+ this.btn_apply = new System.Windows.Forms.Button();
+ this.lbl_all = new System.Windows.Forms.Label();
+ this.label1 = new System.Windows.Forms.Label();
+ this.tip_device = new System.Windows.Forms.ToolTip(this.components);
+ this.btn_refresh = new System.Windows.Forms.Button();
+ this.group_props.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // list_allControllers
+ //
+ this.list_allControllers.FormattingEnabled = true;
+ this.list_allControllers.Location = new System.Drawing.Point(12, 27);
+ this.list_allControllers.Name = "list_allControllers";
+ this.list_allControllers.Size = new System.Drawing.Size(103, 225);
+ this.list_allControllers.TabIndex = 0;
+ this.list_allControllers.SelectedValueChanged += new System.EventHandler(this.list_allControllers_SelectedValueChanged);
+ this.list_allControllers.MouseDown += new System.Windows.Forms.MouseEventHandler(this.list_allControllers_MouseDown);
+ //
+ // list_customControllers
+ //
+ this.list_customControllers.FormattingEnabled = true;
+ this.list_customControllers.Location = new System.Drawing.Point(169, 27);
+ this.list_customControllers.Name = "list_customControllers";
+ this.list_customControllers.Size = new System.Drawing.Size(103, 108);
+ this.list_customControllers.TabIndex = 1;
+ this.list_customControllers.SelectedValueChanged += new System.EventHandler(this.list_customControllers_SelectedValueChanged);
+ this.list_customControllers.MouseDown += new System.Windows.Forms.MouseEventHandler(this.list_customControllers_MouseDown);
+ //
+ // btn_add
+ //
+ this.btn_add.Location = new System.Drawing.Point(121, 27);
+ this.btn_add.Name = "btn_add";
+ this.btn_add.Size = new System.Drawing.Size(42, 23);
+ this.btn_add.TabIndex = 2;
+ this.btn_add.Text = "->";
+ this.btn_add.UseVisualStyleBackColor = true;
+ this.btn_add.Click += new System.EventHandler(this.btn_add_Click);
+ //
+ // btn_remove
+ //
+ this.btn_remove.Location = new System.Drawing.Point(121, 112);
+ this.btn_remove.Name = "btn_remove";
+ this.btn_remove.Size = new System.Drawing.Size(42, 23);
+ this.btn_remove.TabIndex = 3;
+ this.btn_remove.Text = "<-";
+ this.btn_remove.UseVisualStyleBackColor = true;
+ this.btn_remove.Click += new System.EventHandler(this.btn_remove_Click);
+ //
+ // group_props
+ //
+ this.group_props.Controls.Add(this.chk_isLeft);
+ this.group_props.Controls.Add(this.chk_isPro);
+ this.group_props.Location = new System.Drawing.Point(122, 142);
+ this.group_props.Name = "group_props";
+ this.group_props.Size = new System.Drawing.Size(150, 81);
+ this.group_props.TabIndex = 4;
+ this.group_props.TabStop = false;
+ this.group_props.Text = "Settings";
+ //
+ // chk_isLeft
+ //
+ this.chk_isLeft.AutoSize = true;
+ this.chk_isLeft.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
+ this.chk_isLeft.Location = new System.Drawing.Point(6, 42);
+ this.chk_isLeft.Name = "chk_isLeft";
+ this.chk_isLeft.Size = new System.Drawing.Size(96, 17);
+ this.chk_isLeft.TabIndex = 1;
+ this.chk_isLeft.Text = "Left Joycon? ";
+ this.chk_isLeft.UseVisualStyleBackColor = true;
+ this.chk_isLeft.CheckedChanged += new System.EventHandler(this.chk_isLeft_CheckedChanged);
+ //
+ // chk_isPro
+ //
+ this.chk_isPro.AutoSize = true;
+ this.chk_isPro.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
+ this.chk_isPro.Location = new System.Drawing.Point(6, 19);
+ this.chk_isPro.Name = "chk_isPro";
+ this.chk_isPro.Size = new System.Drawing.Size(95, 17);
+ this.chk_isPro.TabIndex = 0;
+ this.chk_isPro.Text = "Pro Controller?";
+ this.chk_isPro.UseVisualStyleBackColor = true;
+ this.chk_isPro.CheckedChanged += new System.EventHandler(this.chk_isPro_CheckedChanged);
+ //
+ // btn_applyAndClose
+ //
+ this.btn_applyAndClose.Location = new System.Drawing.Point(203, 229);
+ this.btn_applyAndClose.Name = "btn_applyAndClose";
+ this.btn_applyAndClose.Size = new System.Drawing.Size(69, 23);
+ this.btn_applyAndClose.TabIndex = 5;
+ this.btn_applyAndClose.Text = "Close";
+ this.btn_applyAndClose.UseVisualStyleBackColor = true;
+ this.btn_applyAndClose.Click += new System.EventHandler(this.btn_applyAndClose_Click);
+ //
+ // btn_apply
+ //
+ this.btn_apply.Location = new System.Drawing.Point(121, 229);
+ this.btn_apply.Name = "btn_apply";
+ this.btn_apply.Size = new System.Drawing.Size(69, 23);
+ this.btn_apply.TabIndex = 6;
+ this.btn_apply.Text = "Apply";
+ this.btn_apply.UseVisualStyleBackColor = true;
+ this.btn_apply.Click += new System.EventHandler(this.btn_apply_Click);
+ //
+ // lbl_all
+ //
+ this.lbl_all.AutoSize = true;
+ this.lbl_all.Location = new System.Drawing.Point(12, 11);
+ this.lbl_all.Name = "lbl_all";
+ this.lbl_all.Size = new System.Drawing.Size(60, 13);
+ this.lbl_all.TabIndex = 7;
+ this.lbl_all.Text = "All Devices";
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(166, 11);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(91, 13);
+ this.label1.TabIndex = 8;
+ this.label1.Text = "Switch Controllers";
+ //
+ // btn_refresh
+ //
+ this.btn_refresh.Location = new System.Drawing.Point(121, 56);
+ this.btn_refresh.Name = "btn_refresh";
+ this.btn_refresh.Size = new System.Drawing.Size(42, 50);
+ this.btn_refresh.TabIndex = 9;
+ this.btn_refresh.Text = "Re-\r\nfresh";
+ this.btn_refresh.UseVisualStyleBackColor = true;
+ this.btn_refresh.Click += new System.EventHandler(this.btn_refresh_Click);
+ //
+ // _3rdPartyControllers
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(284, 261);
+ this.Controls.Add(this.btn_refresh);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.lbl_all);
+ this.Controls.Add(this.btn_apply);
+ this.Controls.Add(this.btn_applyAndClose);
+ this.Controls.Add(this.group_props);
+ this.Controls.Add(this.btn_remove);
+ this.Controls.Add(this.btn_add);
+ this.Controls.Add(this.list_customControllers);
+ this.Controls.Add(this.list_allControllers);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "_3rdPartyControllers";
+ this.Text = "Add 3rd-Party Controllers";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this._3rdPartyControllers_FormClosing);
+ this.group_props.ResumeLayout(false);
+ this.group_props.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
- }
+ }
- #endregion
+ #endregion
- private System.Windows.Forms.ListBox list_allControllers;
- private System.Windows.Forms.ListBox list_customControllers;
- private System.Windows.Forms.Button btn_add;
- private System.Windows.Forms.Button btn_remove;
- private System.Windows.Forms.GroupBox group_props;
- private System.Windows.Forms.Button btn_applyAndClose;
- private System.Windows.Forms.Button btn_apply;
- private System.Windows.Forms.Label lbl_all;
- private System.Windows.Forms.CheckBox chk_isPro;
- private System.Windows.Forms.Label label1;
- private System.Windows.Forms.CheckBox chk_isLeft;
- private System.Windows.Forms.ToolTip tip_device;
- private System.Windows.Forms.Button btn_refresh;
- }
+ private System.Windows.Forms.ListBox list_allControllers;
+ private System.Windows.Forms.ListBox list_customControllers;
+ private System.Windows.Forms.Button btn_add;
+ private System.Windows.Forms.Button btn_remove;
+ private System.Windows.Forms.GroupBox group_props;
+ private System.Windows.Forms.Button btn_applyAndClose;
+ private System.Windows.Forms.Button btn_apply;
+ private System.Windows.Forms.Label lbl_all;
+ private System.Windows.Forms.CheckBox chk_isPro;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.CheckBox chk_isLeft;
+ private System.Windows.Forms.ToolTip tip_device;
+ private System.Windows.Forms.Button btn_refresh;
+ }
}
\ No newline at end of file
diff --git a/BetterJoyForCemu/3rdPartyControllers.cs b/BetterJoyForCemu/3rdPartyControllers.cs
index 3227886..780ce80 100644
--- a/BetterJoyForCemu/3rdPartyControllers.cs
+++ b/BetterJoyForCemu/3rdPartyControllers.cs
@@ -12,111 +12,110 @@ using System.Windows.Forms;
using static BetterJoyForCemu.HIDapi;
namespace BetterJoyForCemu {
- public partial class _3rdPartyControllers : Form {
- public _3rdPartyControllers() {
- InitializeComponent();
- list_allControllers.DisplayMember = "Text";
- list_allControllers.ValueMember = "Value";
- list_customControllers.DisplayMember = "Text";
- list_customControllers.ValueMember = "Value";
+ public partial class _3rdPartyControllers : Form {
+ public _3rdPartyControllers() {
+ InitializeComponent();
+ list_allControllers.DisplayMember = "Text";
+ list_allControllers.ValueMember = "Value";
+ list_customControllers.DisplayMember = "Text";
+ list_customControllers.ValueMember = "Value";
- RefreshControllerList();
+ RefreshControllerList();
- group_props.Controls.Add(chk_isLeft);
- group_props.Controls.Add(chk_isPro);
- group_props.Enabled = false;
- }
+ group_props.Controls.Add(chk_isLeft);
+ group_props.Controls.Add(chk_isPro);
+ group_props.Enabled = false;
+ }
- private bool ContainsText(ListBox a, String manu) {
- foreach (var v in a.Items)
- {
- dynamic d = v as dynamic;
- if (d.Text == null)
- continue;
- if (d.Text.Equals(manu))
- return true;
- }
- return false;
- }
+ private bool ContainsText(ListBox a, String manu) {
+ foreach (var v in a.Items) {
+ dynamic d = v as dynamic;
+ if (d.Text == null)
+ continue;
+ if (d.Text.Equals(manu))
+ return true;
+ }
+ return false;
+ }
- private void RefreshControllerList() {
- list_allControllers.Items.Clear();
- IntPtr ptr = HIDapi.hid_enumerate(0x0, 0x0);
- IntPtr top_ptr = ptr;
+ private void RefreshControllerList() {
+ list_allControllers.Items.Clear();
+ IntPtr ptr = HIDapi.hid_enumerate(0x0, 0x0);
+ IntPtr top_ptr = ptr;
- hid_device_info enumerate; // Add device to list
- while (ptr != IntPtr.Zero) {
- enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info));
+ hid_device_info enumerate; // Add device to list
+ while (ptr != IntPtr.Zero) {
+ enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info));
- if (!ContainsText(list_customControllers, enumerate.product_string) && !ContainsText(list_allControllers, enumerate.product_string))
- list_allControllers.Items.Add(new { Text = enumerate.product_string, Value = enumerate });
+ if (!ContainsText(list_customControllers, enumerate.product_string) && !ContainsText(list_allControllers, enumerate.product_string))
+ list_allControllers.Items.Add(new { Text = enumerate.product_string, Value = enumerate });
- ptr = enumerate.next;
- }
- }
+ ptr = enumerate.next;
+ }
+ }
- private void btn_add_Click(object sender, EventArgs e) {
- if (list_allControllers.SelectedItem != null) {
- list_customControllers.Items.Add(list_allControllers.SelectedItem);
- list_allControllers.Items.Remove(list_allControllers.SelectedItem);
- list_allControllers.ClearSelected();
- }
- }
+ private void btn_add_Click(object sender, EventArgs e) {
+ if (list_allControllers.SelectedItem != null) {
+ list_customControllers.Items.Add(list_allControllers.SelectedItem);
+ list_allControllers.Items.Remove(list_allControllers.SelectedItem);
+ list_allControllers.ClearSelected();
+ }
+ }
- private void btn_remove_Click(object sender, EventArgs e) {
- if (list_customControllers.SelectedItem != null) {
- list_allControllers.Items.Add(list_customControllers.SelectedItem);
- list_customControllers.Items.Remove(list_customControllers.SelectedItem);
- list_customControllers.ClearSelected();
- }
- }
+ private void btn_remove_Click(object sender, EventArgs e) {
+ if (list_customControllers.SelectedItem != null) {
+ list_allControllers.Items.Add(list_customControllers.SelectedItem);
+ list_customControllers.Items.Remove(list_customControllers.SelectedItem);
+ list_customControllers.ClearSelected();
+ }
+ }
- private void chk_isPro_CheckedChanged(object sender, EventArgs e) {
+ private void chk_isPro_CheckedChanged(object sender, EventArgs e) {
- }
+ }
- private void chk_isLeft_CheckedChanged(object sender, EventArgs e) {
+ private void chk_isLeft_CheckedChanged(object sender, EventArgs e) {
- }
+ }
- private void btn_apply_Click(object sender, EventArgs e) {
+ private void btn_apply_Click(object sender, EventArgs e) {
- }
+ }
- private void btn_applyAndClose_Click(object sender, EventArgs e) {
- btn_apply_Click(sender, e);
- Close();
- }
+ private void btn_applyAndClose_Click(object sender, EventArgs e) {
+ btn_apply_Click(sender, e);
+ Close();
+ }
- private void _3rdPartyControllers_FormClosing(object sender, FormClosingEventArgs e) {
- btn_apply_Click(sender, e);
- }
+ private void _3rdPartyControllers_FormClosing(object sender, FormClosingEventArgs e) {
+ btn_apply_Click(sender, e);
+ }
- private void btn_refresh_Click(object sender, EventArgs e) {
- RefreshControllerList();
- }
+ private void btn_refresh_Click(object sender, EventArgs e) {
+ RefreshControllerList();
+ }
- private void list_allControllers_SelectedValueChanged(object sender, EventArgs e) {
- if (list_allControllers.SelectedItem != null)
- tip_device.Show((list_allControllers.SelectedItem as dynamic).Text, list_allControllers);
- }
+ private void list_allControllers_SelectedValueChanged(object sender, EventArgs e) {
+ if (list_allControllers.SelectedItem != null)
+ tip_device.Show((list_allControllers.SelectedItem as dynamic).Text, list_allControllers);
+ }
- private void list_customControllers_SelectedValueChanged(object sender, EventArgs e) {
- if (list_customControllers.SelectedItem != null) {
- tip_device.Show((list_customControllers.SelectedItem as dynamic).Text, list_customControllers);
- group_props.Enabled = true;
- } else
- group_props.Enabled = false;
- }
+ private void list_customControllers_SelectedValueChanged(object sender, EventArgs e) {
+ if (list_customControllers.SelectedItem != null) {
+ tip_device.Show((list_customControllers.SelectedItem as dynamic).Text, list_customControllers);
+ group_props.Enabled = true;
+ } else
+ group_props.Enabled = false;
+ }
- private void list_customControllers_MouseDown(object sender, MouseEventArgs e) {
- if (e.Y > list_customControllers.ItemHeight * list_customControllers.Items.Count)
- list_customControllers.SelectedItems.Clear();
- }
+ private void list_customControllers_MouseDown(object sender, MouseEventArgs e) {
+ if (e.Y > list_customControllers.ItemHeight * list_customControllers.Items.Count)
+ list_customControllers.SelectedItems.Clear();
+ }
- private void list_allControllers_MouseDown(object sender, MouseEventArgs e) {
- if (e.Y > list_allControllers.ItemHeight * list_allControllers.Items.Count)
- list_allControllers.SelectedItems.Clear();
- }
- }
+ private void list_allControllers_MouseDown(object sender, MouseEventArgs e) {
+ if (e.Y > list_allControllers.ItemHeight * list_allControllers.Items.Count)
+ list_allControllers.SelectedItems.Clear();
+ }
+ }
}
diff --git a/BetterJoyForCemu/HIDapi.cs b/BetterJoyForCemu/HIDapi.cs
index 119eea4..40c2048 100644
--- a/BetterJoyForCemu/HIDapi.cs
+++ b/BetterJoyForCemu/HIDapi.cs
@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace BetterJoyForCemu {
public class HIDapi {
#if LINUX
- const string dll = "libhidapi.so";
+ const string dll = "libhidapi.so";
#else
const string dll = "hidapi.dll";
#endif
diff --git a/BetterJoyForCemu/Joycon.cs b/BetterJoyForCemu/Joycon.cs
index e5cdfa2..4dae799 100644
--- a/BetterJoyForCemu/Joycon.cs
+++ b/BetterJoyForCemu/Joycon.cs
@@ -12,1378 +12,1378 @@ using Nefarius.ViGEm.Client.Targets.DualShock4;
using Nefarius.ViGEm.Client.Targets.Xbox360;
namespace BetterJoyForCemu {
- public class Joycon {
- float timing = 120.0f;
-
- public string path = String.Empty;
- public bool isPro = false;
- public bool isSnes = false;
- bool isUSB = false;
- public Joycon other;
- public bool active_gyro = false;
-
- public bool send = true;
-
- public enum DebugType : int {
- NONE,
- ALL,
- COMMS,
- THREADING,
- IMU,
- RUMBLE,
- };
- public DebugType debug_type = DebugType.NONE;
- public bool isLeft;
- public enum state_ : uint {
- NOT_ATTACHED,
- DROPPED,
- NO_JOYCONS,
- ATTACHED,
- INPUT_MODE_0x30,
- IMU_DATA_OK,
- };
- public state_ state;
- public enum Button : int {
- DPAD_DOWN = 0,
- DPAD_RIGHT = 1,
- DPAD_LEFT = 2,
- DPAD_UP = 3,
- SL = 4,
- SR = 5,
- MINUS = 6,
- HOME = 7,
- PLUS = 8,
- CAPTURE = 9,
- STICK = 10,
- SHOULDER_1 = 11,
- SHOULDER_2 = 12,
-
- // For pro controller
- B = 13,
- A = 14,
- Y = 15,
- X = 16,
- STICK2 = 17,
- SHOULDER2_1 = 18,
- SHOULDER2_2 = 19,
- };
- private bool[] buttons_down = new bool[20];
- private bool[] buttons_up = new bool[20];
- private bool[] buttons = new bool[20];
- private bool[] down_ = new bool[20];
- private long[] buttons_down_timestamp = new long[20];
-
- private float[] stick = { 0, 0 };
- private float[] stick2 = { 0, 0 };
-
- private IntPtr handle;
-
- byte[] default_buf = { 0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40 };
-
- private byte[] stick_raw = { 0, 0, 0 };
- private UInt16[] stick_cal = { 0, 0, 0, 0, 0, 0 };
- private UInt16 deadzone;
- private UInt16[] stick_precal = { 0, 0 };
-
- private byte[] stick2_raw = { 0, 0, 0 };
- private UInt16[] stick2_cal = { 0, 0, 0, 0, 0, 0 };
- private UInt16 deadzone2;
- private UInt16[] stick2_precal = { 0, 0 };
-
- private bool stop_polling = false;
- private int timestamp;
- private bool first_imu_packet = true;
- private bool imu_enabled = false;
- private Int16[] acc_r = { 0, 0, 0 };
- private Int16[] acc_neutral = { 0, 0, 0 };
- private Int16[] acc_sensiti = { 0, 0, 0 };
- private Vector3 acc_g;
-
- private Int16[] gyr_r = { 0, 0, 0 };
- private Int16[] gyr_neutral = { 0, 0, 0 };
- private Int16[] gyr_sensiti = { 0, 0, 0 };
- private Vector3 gyr_g;
-
- private short[] acc_sen = new short[3]{
- 16384,
- 16384,
- 16384
- };
- private short[] gyr_sen = new short[3]{
- 18642,
- 18642,
- 18642
- };
-
- private Int16[] pro_hor_offset = { -710, 0, 0 };
- private Int16[] left_hor_offset = { 0, 0, 0 };
- private Int16[] right_hor_offset = { 0, 0, 0 };
-
- private bool do_localize;
- private float filterweight;
- private const uint report_len = 49;
-
- private struct Rumble {
- private float h_f, l_f;
- public float t, amp, fullamp;
- public bool timed_rumble;
-
- public void set_vals(float low_freq, float high_freq, float amplitude, int time = 0) {
- h_f = high_freq;
- amp = amplitude;
- fullamp = amplitude;
- l_f = low_freq;
- timed_rumble = false;
- t = 0;
- if (time != 0) {
- t = time / 1000f;
- timed_rumble = true;
- }
- }
- public Rumble(float low_freq, float high_freq, float amplitude, int time = 0) {
- h_f = high_freq;
- amp = amplitude;
- fullamp = amplitude;
- l_f = low_freq;
- timed_rumble = false;
- t = 0;
- if (time != 0) {
- t = time / 1000f;
- timed_rumble = true;
- }
- }
- private float clamp(float x, float min, float max) {
- if (x < min) return min;
- if (x > max) return max;
- return x;
- }
- public byte[] GetData() {
- byte[] rumble_data = new byte[8];
- if (amp == 0.0f) {
- rumble_data[0] = 0x0;
- rumble_data[1] = 0x1;
- rumble_data[2] = 0x40;
- rumble_data[3] = 0x40;
- } else {
- l_f = clamp(l_f, 40.875885f, 626.286133f);
- amp = clamp(amp, 0.0f, 1.0f);
- h_f = clamp(h_f, 81.75177f, 1252.572266f);
- UInt16 hf = (UInt16)((Math.Round(32f * Math.Log(h_f * 0.1f, 2)) - 0x60) * 4);
- byte lf = (byte)(Math.Round(32f * Math.Log(l_f * 0.1f, 2)) - 0x40);
- byte hf_amp;
- if (amp == 0) hf_amp = 0;
- else if (amp < 0.117) hf_amp = (byte)(((Math.Log(amp * 1000, 2) * 32) - 0x60) / (5 - Math.Pow(amp, 2)) - 1);
- else if (amp < 0.23) hf_amp = (byte)(((Math.Log(amp * 1000, 2) * 32) - 0x60) - 0x5c);
- else hf_amp = (byte)((((Math.Log(amp * 1000, 2) * 32) - 0x60) * 2) - 0xf6);
-
- UInt16 lf_amp = (UInt16)(Math.Round((double)hf_amp) * .5);
- byte parity = (byte)(lf_amp % 2);
- if (parity > 0) {
- --lf_amp;
- }
-
- lf_amp = (UInt16)(lf_amp >> 1);
- lf_amp += 0x40;
- if (parity > 0) lf_amp |= 0x8000;
- rumble_data = new byte[8];
- rumble_data[0] = (byte)(hf & 0xff);
- rumble_data[1] = (byte)((hf >> 8) & 0xff);
- rumble_data[2] = lf;
- rumble_data[1] += hf_amp;
- rumble_data[2] += (byte)((lf_amp >> 8) & 0xff);
- rumble_data[3] += (byte)(lf_amp & 0xff);
- }
- for (int i = 0; i < 4; ++i) {
- rumble_data[4 + i] = rumble_data[i];
- }
-
- return rumble_data;
- }
- }
-
- private Rumble rumble_obj;
-
- private byte global_count = 0;
- private string debug_str;
-
- // For UdpServer
- public int PadId = 0;
- public int battery = -1;
- public int model = 2;
- public int constate = 2;
- public int connection = 3;
-
- public PhysicalAddress PadMacAddress = new PhysicalAddress(new byte[] { 01, 02, 03, 04, 05, 06 });
- public ulong Timestamp = 0;
- public int packetCounter = 0;
-
- public OutputControllerXbox360 out_xbox;
- public OutputControllerDualShock4 out_ds4;
- ushort ds4_ts = 0;
- ulong lag;
-
- int rumblePeriod = Int32.Parse(ConfigurationManager.AppSettings["RumblePeriod"]);
- int lowFreq = Int32.Parse(ConfigurationManager.AppSettings["LowFreqRumble"]);
- int highFreq = Int32.Parse(ConfigurationManager.AppSettings["HighFreqRumble"]);
-
- bool toRumble = Boolean.Parse(ConfigurationManager.AppSettings["EnableRumble"]);
-
- bool showAsXInput = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsXInput"]);
- bool showAsDS4 = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsDS4"]);
-
- public MainForm form;
-
- public byte LED = 0x0;
-
- public string serial_number;
-
- private float[] activeData;
-
- public Joycon(IntPtr handle_, bool imu, bool localize, float alpha, bool left, string path, string serialNum, int id = 0, bool isPro = false, bool isSnes = false) {
- serial_number = serialNum;
- activeData = new float[6];
- handle = handle_;
- imu_enabled = imu;
- do_localize = localize;
- rumble_obj = new Rumble(160, 320, 0);
- for (int i = 0; i < buttons_down_timestamp.Length; i++)
- buttons_down_timestamp[i] = -1;
- filterweight = alpha;
- isLeft = left;
-
- PadId = id;
- LED = (byte)(0x1 << PadId);
- this.isPro = isPro || isSnes;
- this.isSnes = isSnes;
- isUSB = serialNum == "000000000001";
-
- this.path = path;
-
- connection = isUSB ? 0x01 : 0x02;
-
- if (showAsXInput) {
- out_xbox = new OutputControllerXbox360();
- out_xbox.FeedbackReceived += ReceiveRumble;
- }
-
- if (showAsDS4) {
- out_ds4 = new OutputControllerDualShock4();
-
- if (toRumble)
- out_ds4.FeedbackReceived += Ds4_FeedbackReceived;
- }
- }
-
- public void getActiveData() {
- this.activeData = form.activeCaliData(serial_number);
- }
-
- public void ReceiveRumble(Xbox360FeedbackReceivedEventArgs e) {
- SetRumble(lowFreq, highFreq, (float)(e.LargeMotor + e.SmallMotor) / (float)255, rumblePeriod);
-
- if (other != null && other != this)
- other.SetRumble(lowFreq, highFreq, (float)(e.LargeMotor + e.SmallMotor) / (float)255, rumblePeriod);
- }
-
- public void Ds4_FeedbackReceived(DualShock4FeedbackReceivedEventArgs e) {
- SetRumble(lowFreq, highFreq, (float)(e.LargeMotor + e.SmallMotor) / (float)255, rumblePeriod);
-
- if (other != null && other != this)
- other.SetRumble(lowFreq, highFreq, (float)(e.LargeMotor + e.SmallMotor) / (float)255, rumblePeriod);
- }
-
- public void DebugPrint(String s, DebugType d) {
- if (debug_type == DebugType.NONE) return;
- if (d == DebugType.ALL || d == debug_type || debug_type == DebugType.ALL) {
- form.AppendTextBox(s + "\r\n");
- }
- }
- public bool GetButtonDown(Button b) {
- return buttons_down[(int)b];
- }
- public bool GetButton(Button b) {
- return buttons[(int)b];
- }
- public bool GetButtonUp(Button b) {
- return buttons_up[(int)b];
- }
- public float[] GetStick() {
- return stick;
- }
- public float[] GetStick2() {
- return stick2;
- }
- public Vector3 GetGyro() {
- return gyr_g;
- }
- public Vector3 GetAccel() {
- return acc_g;
- }
- public int Attach(byte leds_ = 0x0) {
- state = state_.ATTACHED;
-
- // Make sure command is received
- HIDapi.hid_set_nonblocking(handle, 0);
-
- byte[] a = { 0x0 };
-
- // Connect
- if (!isUSB) {
- // Input report mode
- Subcommand(0x03, new byte[] { 0x30 }, 1, false);
-
- a[0] = 0x1;
- dump_calibration_data();
- } else {
- Subcommand(0x03, new byte[] { 0x3f }, 1, false);
-
- a = Enumerable.Repeat((byte)0, 64).ToArray();
- form.AppendTextBox("Using USB.\r\n");
-
- a[0] = 0x80;
- a[1] = 0x1;
- HIDapi.hid_write(handle, a, new UIntPtr(2));
- HIDapi.hid_read(handle, a, new UIntPtr(64));
-
- if (a[2] != 0x3) {
- PadMacAddress = new PhysicalAddress(new byte[] { a[9], a[8], a[7], a[6], a[5], a[4] });
- }
-
- // USB Pairing
- a = Enumerable.Repeat((byte)0, 64).ToArray();
- a[0] = 0x80; a[1] = 0x2; // Handshake
- HIDapi.hid_write(handle, a, new UIntPtr(2));
-
- a[0] = 0x80; a[1] = 0x3; // 3Mbit baud rate
- HIDapi.hid_write(handle, a, new UIntPtr(2));
-
- a[0] = 0x80; a[1] = 0x2; // Handshake at new baud rate
- HIDapi.hid_write(handle, a, new UIntPtr(2));
-
- a[0] = 0x80; a[1] = 0x4; // Prevent HID timeout
- HIDapi.hid_write(handle, a, new UIntPtr(2)); // doesn't actually prevent timout...
-
- dump_calibration_data();
- }
-
- // Bluetooth manual pairing
- byte[] btmac_host = Program.btMAC.GetAddressBytes();
- // send host MAC and acquire Joycon MAC
- //byte[] reply = Subcommand(0x01, new byte[] { 0x01, btmac_host[5], btmac_host[4], btmac_host[3], btmac_host[2], btmac_host[1], btmac_host[0] }, 7, true);
- //byte[] LTKhash = Subcommand(0x01, new byte[] { 0x02 }, 1, true);
- // save pairing info
- //Subcommand(0x01, new byte[] { 0x03 }, 1, true);
-
- BlinkHomeLight();
-
- a[0] = leds_;
- Subcommand(0x30, a, 1);
- Subcommand(0x40, new byte[] { (imu_enabled ? (byte)0x1 : (byte)0x0) }, 1, true);
- Subcommand(0x3, new byte[] { 0x30 }, 1, true);
- Subcommand(0x48, new byte[] { 0x01 }, 1, true);
-
- Subcommand(0x41, new byte[] { 0x03, 0x00, 0x00, 0x01 }, 4, false); // higher gyro performance rate
-
- DebugPrint("Done with init.", DebugType.COMMS);
-
- HIDapi.hid_set_nonblocking(handle, 1);
-
- // send ping to USB to not time out instantly
- if (isUSB)
- SendRumble(rumble_obj.GetData());
-
- return 0;
- }
-
- public void SetPlayerLED(byte leds_ = 0x0) {
- Subcommand(0x30, new byte[] { leds_ }, 1);
- }
-
- public void BlinkHomeLight() { // do not call after initial setup
- byte[] a = Enumerable.Repeat((byte)0xFF, 25).ToArray();
- a[0] = 0x18;
- a[1] = 0x01;
- Subcommand(0x38, a, 25, false);
- }
-
- public void SetHomeLight(bool on) {
- byte[] a = Enumerable.Repeat((byte)0xFF, 25).ToArray();
- if (on) {
- a[0] = 0x1F;
- a[1] = 0xF0;
- } else {
- a[0] = 0x10;
- a[1] = 0x01;
- }
- Subcommand(0x38, a, 25, false);
- }
-
- private void SetHCIState(byte state) {
- byte[] a = { state };
- Subcommand(0x06, a, 1, false);
- }
-
- public void PowerOff() {
- if (state > state_.DROPPED) {
- HIDapi.hid_set_nonblocking(handle, 0);
- SetHCIState(0x00);
- state = state_.DROPPED;
- }
- }
-
- private void BatteryChanged() { // battery changed level
- foreach (var v in form.con) {
- if (v.Tag == this) {
- switch (battery) {
- case 4:
- v.BackColor = System.Drawing.Color.FromArgb(0xAA, System.Drawing.Color.Green);
- break;
- case 3:
- v.BackColor = System.Drawing.Color.FromArgb(0xAA, System.Drawing.Color.Green);
- break;
- case 2:
- v.BackColor = System.Drawing.Color.FromArgb(0xAA, System.Drawing.Color.GreenYellow);
- break;
- case 1:
- v.BackColor = System.Drawing.Color.FromArgb(0xAA, System.Drawing.Color.Orange);
- break;
- default:
- v.BackColor = System.Drawing.Color.FromArgb(0xAA, System.Drawing.Color.Red);
- break;
- }
- }
- }
- }
-
- public void SetFilterCoeff(float a) {
- filterweight = a;
- }
-
- public void Detach(bool close = false) {
- stop_polling = true;
-
- if (out_xbox != null) {
- out_xbox.Disconnect();
- }
-
- if (out_ds4 != null) {
- out_ds4.Disconnect();
- }
-
- if (state > state_.NO_JOYCONS) {
- HIDapi.hid_set_nonblocking(handle, 0);
-
- Subcommand(0x40, new byte[] { 0x0 }, 1); // disable IMU sensor
- //Subcommand(0x48, new byte[] { 0x0 }, 1); // Would turn off rumble?
-
- if (isUSB) {
- byte[] a = Enumerable.Repeat((byte)0, 64).ToArray();
- a[0] = 0x80; a[1] = 0x5; // Allow device to talk to BT again
- HIDapi.hid_write(handle, a, new UIntPtr(2));
- a[0] = 0x80; a[1] = 0x6; // Allow device to talk to BT again
- HIDapi.hid_write(handle, a, new UIntPtr(2));
- }
- }
- if (close || state > state_.DROPPED) {
- HIDapi.hid_close(handle);
- }
- state = state_.NOT_ATTACHED;
- }
-
- // TODO: Improve this loop, make USB not laggy
- private byte ts_en;
- private int ReceiveRaw() {
- if (handle == IntPtr.Zero) return -2;
- HIDapi.hid_set_nonblocking(handle, 1);
- byte[] raw_buf = new byte[report_len];
- int ret = HIDapi.hid_read_timeout(handle, raw_buf, new UIntPtr(report_len), 5);
- if (ret > 0) {
- // Process packets as soon as they come
- for (int n = 0; n < 3; n++) {
- ExtractIMUValues(raw_buf, n);
-
- byte lag = (byte)Math.Max(0, raw_buf[1] - ts_en - 3);
- if (n == 0) {
- Timestamp += (ulong)lag * 5000; // add lag once
- ProcessButtonsAndStick(raw_buf);
-
- // process buttons here to have them affect DS4
- DoThingsWithButtons();
-
- int newbat = battery;
- battery = (raw_buf[2] >> 4) / 2;
- if (newbat != battery)
- BatteryChanged();
- }
- Timestamp += 5000; // 5ms difference
-
- packetCounter++;
- if (Program.server != null)
- Program.server.NewReportIncoming(this);
-
- if (out_ds4 != null) {
- try {
- out_ds4.UpdateInput(MapToDualShock4Input(this));
- } catch (Exception e) {
- // ignore /shrug
- }
- }
- }
-
- // no reason to send XInput reports so often
- if (out_xbox != null) {
- try {
- out_xbox.UpdateInput(MapToXbox360Input(this));
- } catch (Exception e) {
- // ignore /shrug
- }
- }
-
-
- if (ts_en == raw_buf[1] && !isSnes) {
- form.AppendTextBox("Duplicate timestamp enqueued.\r\n");
- DebugPrint(string.Format("Duplicate timestamp enqueued. TS: {0:X2}", ts_en), DebugType.THREADING);
- }
- ts_en = raw_buf[1];
- DebugPrint(string.Format("Enqueue. Bytes read: {0:D}. Timestamp: {1:X2}", ret, raw_buf[1]), DebugType.THREADING);
- }
- return ret;
- }
-
- bool dragToggle = Boolean.Parse(ConfigurationManager.AppSettings["DragToggle"]);
- Dictionary mouse_toggle_btn = new Dictionary();
- private void Simulate(string s, bool click = true, bool up = false) {
- if (s.StartsWith("key_")) {
- WindowsInput.Events.KeyCode key = (WindowsInput.Events.KeyCode)Int32.Parse(s.Substring(4));
- if (click) {
- WindowsInput.Simulate.Events().Click(key).Invoke();
- } else {
- if (up) {
- WindowsInput.Simulate.Events().Release(key).Invoke();
- } else {
- WindowsInput.Simulate.Events().Hold(key).Invoke();
- }
- }
- } else if (s.StartsWith("mse_")) {
- WindowsInput.Events.ButtonCode button = (WindowsInput.Events.ButtonCode)Int32.Parse(s.Substring(4));
- if (click) {
- WindowsInput.Simulate.Events().Click(button).Invoke();
- } else {
- if (dragToggle) {
- if (!up) {
- bool release;
- mouse_toggle_btn.TryGetValue((int)button, out release);
- if (release)
- WindowsInput.Simulate.Events().Release(button).Invoke();
- else
- WindowsInput.Simulate.Events().Hold(button).Invoke();
- mouse_toggle_btn[(int)button] = !release;
- }
- } else {
- if (up) {
- WindowsInput.Simulate.Events().Release(button).Invoke();
- } else {
- WindowsInput.Simulate.Events().Hold(button).Invoke();
- }
- }
- }
- }
- }
-
- // For Joystick->Joystick inputs
- private void SimulateContinous(int origin, string s) {
- if (s.StartsWith("joy_")) {
- int button = Int32.Parse(s.Substring(4));
- buttons[button] |= buttons[origin];
- }
- }
-
- string extraGyroFeature = ConfigurationManager.AppSettings["GyroToJoyOrMouse"];
- int GyroMouseSensitivity = Int32.Parse(ConfigurationManager.AppSettings["GyroMouseSensitivity"]);
- bool HomeLongPowerOff = Boolean.Parse(ConfigurationManager.AppSettings["HomeLongPowerOff"]);
-
- bool GyroAnalogSliders = Boolean.Parse(ConfigurationManager.AppSettings["GyroAnalogSliders"]);
- int GyroAnalogSensitivity = Int32.Parse(ConfigurationManager.AppSettings["GyroAnalogSensitivity"]);
- byte[] sliderVal = new byte[] { 0, 0 };
- private void DoThingsWithButtons() {
- int powerOffButton = (int)((isPro || !isLeft || other != null) ? Button.HOME : Button.CAPTURE);
-
- if (HomeLongPowerOff && buttons[powerOffButton]) {
- long timestamp = Stopwatch.GetTimestamp();
- if ((timestamp - buttons_down_timestamp[powerOffButton]) / 10000 > 2000.0) {
- if (other != null)
- other.PowerOff();
-
- PowerOff();
- return;
- }
- }
-
- if (buttons_down[(int)Button.CAPTURE])
- Simulate(Config.Value("capture"));
- if (buttons_down[(int)Button.HOME])
- Simulate(Config.Value("home"));
- SimulateContinous((int)Button.CAPTURE, Config.Value("capture"));
- SimulateContinous((int)Button.HOME, Config.Value("home"));
-
- if (isLeft) {
- if (buttons_down[(int)Button.SL])
- Simulate(Config.Value("sl_l"), false, false);
- if (buttons_up[(int)Button.SL])
- Simulate(Config.Value("sl_l"), false, true);
- if (buttons_down[(int)Button.SR])
- Simulate(Config.Value("sr_l"), false, false);
- if (buttons_up[(int)Button.SR])
- Simulate(Config.Value("sr_l"), false, true);
-
- SimulateContinous((int)Button.SL, Config.Value("sl_l"));
- SimulateContinous((int)Button.SR, Config.Value("sr_l"));
- } else {
- if (buttons_down[(int)Button.SL])
- Simulate(Config.Value("sl_r"), false, false);
- if (buttons_up[(int)Button.SL])
- Simulate(Config.Value("sl_r"), false, true);
- if (buttons_down[(int)Button.SR])
- Simulate(Config.Value("sr_r"), false, false);
- if (buttons_up[(int)Button.SR])
- Simulate(Config.Value("sr_r"), false, true);
-
- SimulateContinous((int)Button.SL, Config.Value("sl_r"));
- SimulateContinous((int)Button.SR, Config.Value("sr_r"));
- }
-
- if (GyroAnalogSliders && (other != null || isPro)) {
- Button leftT = isLeft ? Button.SHOULDER_2 : Button.SHOULDER2_2;
- Button rightT = isLeft ? Button.SHOULDER2_2 : Button.SHOULDER_2;
- float dt = 0.015f; // 15ms
- Joycon left = isLeft ? this : (isPro ? this : this.other); Joycon right = !isLeft ? this : (isPro ? this : this.other);
- int ldy = (int)(GyroAnalogSensitivity * (left.gyr_g.Y * dt) * (Math.Abs(left.gyr_g.Y) < 1 ? 0 : 1));
- int rdy = (int)(GyroAnalogSensitivity * (right.gyr_g.Y * dt) * (Math.Abs(right.gyr_g.Y) < 1 ? 0 : 1));
-
- if (buttons[(int)leftT]) {
- sliderVal[0] = (byte)Math.Min(Byte.MaxValue, Math.Max(0, (int)sliderVal[0] + ldy));
- } else {
- sliderVal[0] = 0;
- }
-
- if (buttons[(int)rightT]) {
- sliderVal[1] = (byte)Math.Min(Byte.MaxValue, Math.Max(0, (int)sliderVal[1] + rdy));
- } else {
- sliderVal[1] = 0;
- }
- }
-
- if (extraGyroFeature == "joy") {
- // TODO
- } else if (extraGyroFeature == "mouse" && (isPro || (other == null) || (other != null && (Boolean.Parse(ConfigurationManager.AppSettings["GyroMouseLeftHanded"]) ? isLeft : !isLeft)))) {
- string res_val = Config.Value("active_gyro");
- if (res_val.StartsWith("joy_")) {
- int i = Int32.Parse(res_val.Substring(4));
- if (buttons_down[i] || (other != null && other.buttons_down[i]))
- active_gyro = true;
- else if (buttons_up[i] || (other != null && other.buttons_up[i]))
- active_gyro = false;
- }
-
- float dt = 0.015f; // 15ms
-
- // gyro data is in degrees/s
- if (Config.Value("active_gyro") == "0" || active_gyro) {
- int dx = (int)(GyroMouseSensitivity * (gyr_g.Z * dt) * (Math.Abs(gyr_g.Z) < 1 ? 0 : 1));
- int dy = (int)-(GyroMouseSensitivity * (gyr_g.Y * dt) * (Math.Abs(gyr_g.Y) < 1 ? 0 : 1));
-
- WindowsInput.Simulate.Events().MoveBy(dx, dy).Invoke();
- }
-
- // reset mouse position to centre of primary monitor
- res_val = Config.Value("reset_mouse");
- if (res_val.StartsWith("joy_")) {
- int i = Int32.Parse(res_val.Substring(4));
- if (buttons_down[i] || (other != null && other.buttons_down[i]))
- WindowsInput.Simulate.Events().MoveTo(Screen.PrimaryScreen.Bounds.Width / 2, Screen.PrimaryScreen.Bounds.Height / 2).Invoke();
- }
- }
- }
-
- // TODO: Fix?
- private Thread PollThreadObj; // pro times out over time randomly if it was USB and then bluetooth??
- private void Poll() {
- int attempts = 0;
- Stopwatch watch = new Stopwatch();
- watch.Start();
- while (!stop_polling & state > state_.NO_JOYCONS) {
- if (!isSnes && (rumble_obj.t > 0))
- SendRumble(rumble_obj.GetData());
-
- int a = ReceiveRaw();
-
- if (a > 0 && state > state_.DROPPED) {
- state = state_.IMU_DATA_OK;
- attempts = 0;
-
- // Needed for USB to not time out; I think USB requires a reply message after every packet sent
- if (isUSB)
- SendRumble(rumble_obj.GetData());
- } else if (attempts > 240) {
- state = state_.DROPPED;
- form.AppendTextBox("Dropped.\r\n");
-
- DebugPrint("Connection lost. Is the Joy-Con connected?", DebugType.ALL);
- break;
- } else if (a < 0) {
- // An error on read.
- //form.AppendTextBox("Pause 5ms");
- Thread.Sleep((Int32)5);
- ++attempts;
- } else if (a == 0) {
- // The non-blocking read timed out. No need to sleep.
- // No need to increase attempts because it's not an error.
- }
- }
- }
-
- public void Update() {
- if (state > state_.NO_JOYCONS) {
- if (rumble_obj.timed_rumble) {
- if (rumble_obj.t < 0) {
- rumble_obj.set_vals(lowFreq, highFreq, 0, 0);
- } else {
- rumble_obj.t -= (1 / timing);
- //rumble_obj.amp = (float) Math.Sin(((timing - rumble_obj.t * 1000f) / timing) * Math.PI) * rumble_obj.fullamp;
- }
- }
- }
- }
-
- public float[] otherStick = { 0, 0 };
-
- bool swapAB = Boolean.Parse(ConfigurationManager.AppSettings["SwapAB"]);
- bool swapXY = Boolean.Parse(ConfigurationManager.AppSettings["SwapXY"]);
- private int ProcessButtonsAndStick(byte[] report_buf) {
- if (report_buf[0] == 0x00) return -1;
- if (!isSnes) {
- 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)];
-
- if (isPro) {
- stick2_raw[0] = report_buf[6 + (!isLeft ? 0 : 3)];
- stick2_raw[1] = report_buf[7 + (!isLeft ? 0 : 3)];
- stick2_raw[2] = report_buf[8 + (!isLeft ? 0 : 3)];
- }
-
- stick_precal[0] = (UInt16)(stick_raw[0] | ((stick_raw[1] & 0xf) << 8));
- stick_precal[1] = (UInt16)((stick_raw[1] >> 4) | (stick_raw[2] << 4));
- ushort[] cal = form.nonOriginal ? new ushort[6] { 2048, 2048, 2048, 2048, 2048, 2048 } : stick_cal;
- ushort dz = form.nonOriginal ? (ushort)200 : deadzone;
- stick = CenterSticks(stick_precal, cal, dz);
-
- 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, form.nonOriginal ? cal : stick2_cal, deadzone2);
- }
-
- // Read other Joycon's sticks
- if (isLeft && other != null && other != this) {
- stick2 = otherStick;
- other.otherStick = stick;
- }
-
- if (!isLeft && other != null && other != this) {
- Array.Copy(stick, stick2, 2);
- stick = otherStick;
- other.otherStick = stick2;
- }
- }
- //
-
- // Set button states both for server and ViGEm
- lock (buttons) {
- lock (down_) {
- for (int i = 0; i < buttons.Length; ++i) {
- down_[i] = buttons[i];
- }
- }
- buttons = new bool[20];
-
- buttons[(int)Button.DPAD_DOWN] = (report_buf[3 + (isLeft ? 2 : 0)] & (isLeft ? 0x01 : 0x04)) != 0;
- buttons[(int)Button.DPAD_RIGHT] = (report_buf[3 + (isLeft ? 2 : 0)] & (isLeft ? 0x04 : 0x08)) != 0;
- buttons[(int)Button.DPAD_UP] = (report_buf[3 + (isLeft ? 2 : 0)] & (isLeft ? 0x02 : 0x02)) != 0;
- buttons[(int)Button.DPAD_LEFT] = (report_buf[3 + (isLeft ? 2 : 0)] & (isLeft ? 0x08 : 0x01)) != 0;
- buttons[(int)Button.HOME] = ((report_buf[4] & 0x10) != 0);
- buttons[(int)Button.CAPTURE] = ((report_buf[4] & 0x20) != 0);
- buttons[(int)Button.MINUS] = ((report_buf[4] & 0x01) != 0);
- buttons[(int)Button.PLUS] = ((report_buf[4] & 0x02) != 0);
- buttons[(int)Button.STICK] = ((report_buf[4] & (isLeft ? 0x08 : 0x04)) != 0);
- buttons[(int)Button.SHOULDER_1] = (report_buf[3 + (isLeft ? 2 : 0)] & 0x40) != 0;
- buttons[(int)Button.SHOULDER_2] = (report_buf[3 + (isLeft ? 2 : 0)] & 0x80) != 0;
- buttons[(int)Button.SR] = (report_buf[3 + (isLeft ? 2 : 0)] & 0x10) != 0;
- buttons[(int)Button.SL] = (report_buf[3 + (isLeft ? 2 : 0)] & 0x20) != 0;
-
- if (isPro) {
- buttons[(int)Button.B] = (report_buf[3 + (!isLeft ? 2 : 0)] & (!isLeft ? 0x01 : 0x04)) != 0;
- buttons[(int)Button.A] = (report_buf[3 + (!isLeft ? 2 : 0)] & (!isLeft ? 0x04 : 0x08)) != 0;
- buttons[(int)Button.X] = (report_buf[3 + (!isLeft ? 2 : 0)] & (!isLeft ? 0x02 : 0x02)) != 0;
- buttons[(int)Button.Y] = (report_buf[3 + (!isLeft ? 2 : 0)] & (!isLeft ? 0x08 : 0x01)) != 0;
-
- buttons[(int)Button.STICK2] = ((report_buf[4] & (!isLeft ? 0x08 : 0x04)) != 0);
- buttons[(int)Button.SHOULDER2_1] = (report_buf[3 + (!isLeft ? 2 : 0)] & 0x40) != 0;
- buttons[(int)Button.SHOULDER2_2] = (report_buf[3 + (!isLeft ? 2 : 0)] & 0x80) != 0;
- }
-
- if (other != null && other != this) {
- 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];
- }
-
- if (isLeft && other != null && other != this) {
- buttons[(int)Button.HOME] = other.buttons[(int)Button.HOME];
- buttons[(int)Button.PLUS] = other.buttons[(int)Button.PLUS];
- }
-
- if (!isLeft && other != null && other != this) {
- buttons[(int)Button.MINUS] = other.buttons[(int)Button.MINUS];
- }
-
- long timestamp = Stopwatch.GetTimestamp();
-
- lock (buttons_up) {
- lock (buttons_down) {
- for (int i = 0; i < buttons.Length; ++i) {
- buttons_up[i] = (down_[i] & !buttons[i]);
- buttons_down[i] = (!down_[i] & buttons[i]);
- if (down_[i] != buttons[i])
- buttons_down_timestamp[i] = (buttons[i] ? timestamp : -1);
- }
- }
- }
- }
-
- return 0;
- }
-
- // Get Gyro/Accel data
- private void ExtractIMUValues(byte[] report_buf, int n = 0) {
- if (!isSnes) {
- 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));
- acc_r[0] = (Int16)(report_buf[13 + n * 12] | ((report_buf[14 + n * 12] << 8) & 0xff00));
- acc_r[1] = (Int16)(report_buf[15 + n * 12] | ((report_buf[16 + n * 12] << 8) & 0xff00));
- acc_r[2] = (Int16)(report_buf[17 + n * 12] | ((report_buf[18 + n * 12] << 8) & 0xff00));
-
- if (form.nonOriginal) {
- for (int i = 0; i < 3; ++i) {
- switch (i) {
- case 0:
- acc_g.X = (acc_r[i] - activeData[3]) * (1.0f / acc_sen[i]) * 4.0f;
- gyr_g.X = (gyr_r[i] - activeData[0]) * (816.0f / gyr_sen[i]);
- if (form.calibrate) {
- form.xA.Add(acc_r[i]);
- form.xG.Add(gyr_r[i]);
- }
- break;
- case 1:
- acc_g.Y = (!isLeft ? -1 : 1) * (acc_r[i] - activeData[4]) * (1.0f / acc_sen[i]) * 4.0f;
- gyr_g.Y = -(!isLeft ? -1 : 1) * (gyr_r[i] - activeData[1]) * (816.0f / gyr_sen[i]);
- if (form.calibrate) {
- form.yA.Add(acc_r[i]);
- form.yG.Add(gyr_r[i]);
- }
- break;
- case 2:
- acc_g.Z = (!isLeft ? -1 : 1) * (acc_r[i] - activeData[5]) * (1.0f / acc_sen[i]) * 4.0f;
- gyr_g.Z = -(!isLeft ? -1 : 1) * (gyr_r[i] - activeData[2]) * (816.0f / gyr_sen[i]);
- if (form.calibrate) {
- form.zA.Add(acc_r[i]);
- form.zG.Add(gyr_r[i]);
- }
- break;
- }
- }
- } else {
- Int16[] offset;
- if (isPro)
- offset = pro_hor_offset;
- else if (isLeft)
- offset = left_hor_offset;
- else
- offset = right_hor_offset;
-
- for (int i = 0; i < 3; ++i) {
- switch (i) {
- case 0:
- acc_g.X = (acc_r[i] - offset[i]) * (1.0f / (acc_sensiti[i] - acc_neutral[i])) * 4.0f;
- gyr_g.X = (gyr_r[i] - gyr_neutral[i]) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
-
- break;
- case 1:
- 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]) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
- break;
- case 2:
- 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]) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
- break;
- }
- }
- }
-
-
- if (other == null && !isPro) { // single joycon mode; Z do not swap, rest do
- if (isLeft) {
- acc_g.X = -acc_g.X;
- gyr_g.X = -gyr_g.X;
- } else {
- gyr_g.Y = -gyr_g.Y;
- }
-
- float temp = acc_g.X; acc_g.X = acc_g.Y; acc_g.Y = temp;
- temp = gyr_g.X; gyr_g.X = gyr_g.Y; gyr_g.Y = temp;
- }
- }
- }
-
- public void Begin() {
- if (PollThreadObj == null) {
- PollThreadObj = new Thread(new ThreadStart(Poll));
- PollThreadObj.IsBackground = true;
- PollThreadObj.Start();
-
- form.AppendTextBox("Starting poll thread.\r\n");
- } else {
- form.AppendTextBox("Poll cannot start.\r\n");
- }
- }
-
- public void Recenter() {
- first_imu_packet = true;
- }
-
- // Should really be called calculating stick data
- private float[] CenterSticks(UInt16[] vals, ushort[] cal, ushort dz) {
- ushort[] t = cal;
-
- float[] s = { 0, 0 };
- float dx = vals[0] - t[2], dy = vals[1] - t[3];
- if (Math.Abs(dx * dx + dy * dy) < dz * dz)
- return s;
-
- s[0] = dx / (dx > 0 ? t[0] : t[4]);
- s[1] = dy / (dy > 0 ? t[1] : t[5]);
- return s;
- }
-
- private static short CastStickValue(float stick_value) {
- return (short)Math.Max(Int16.MinValue, Math.Min(Int16.MaxValue, stick_value * (stick_value > 0 ? Int16.MaxValue : -Int16.MinValue)));
- }
-
- private static byte CastStickValueByte(float stick_value) {
- return (byte)Math.Max(Byte.MinValue, Math.Min(Byte.MaxValue, 127 - stick_value * Byte.MaxValue));
- }
-
- public void SetRumble(float low_freq, float high_freq, float amp, int time = 0) {
- if (state <= Joycon.state_.ATTACHED) return;
- rumble_obj.set_vals(low_freq, high_freq, amp, time);
- }
-
- private void SendRumble(byte[] buf) {
- byte[] buf_ = new byte[report_len];
- buf_[0] = 0x10;
- buf_[1] = global_count;
- if (global_count == 0xf) global_count = 0;
- else ++global_count;
- Array.Copy(buf, 0, buf_, 2, 8);
- PrintArray(buf_, DebugType.RUMBLE, format: "Rumble data sent: {0:S}");
- HIDapi.hid_write(handle, buf_, new UIntPtr(report_len));
- }
-
- private byte[] Subcommand(byte sc, byte[] buf, uint len, bool print = true) {
- byte[] buf_ = new byte[report_len];
- byte[] response = new byte[report_len];
- Array.Copy(default_buf, 0, buf_, 2, 8);
- Array.Copy(buf, 0, buf_, 11, len);
- buf_[10] = sc;
- buf_[1] = global_count;
- buf_[0] = 0x1;
- if (global_count == 0xf) global_count = 0;
- else ++global_count;
- if (print) { PrintArray(buf_, DebugType.COMMS, len, 11, "Subcommand 0x" + string.Format("{0:X2}", sc) + " sent. Data: 0x{0:S}"); };
- HIDapi.hid_write(handle, buf_, new UIntPtr(len + 11));
- int res = HIDapi.hid_read_timeout(handle, response, new UIntPtr(report_len), 50);
- 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}"); }
- return response;
- }
-
- private void dump_calibration_data() {
- if (!isSnes) {
- byte[] buf_ = ReadSPI(0x80, (isLeft ? (byte)0x12 : (byte)0x1d), 9); // get user calibration data if possible
- bool found = false;
- for (int i = 0; i < 9; ++i) {
- if (buf_[i] != 0xff) {
- form.AppendTextBox("Using user stick calibration data.\r\n");
- found = true;
- break;
- }
- }
- if (!found) {
- form.AppendTextBox("Using factory stick calibration data.\r\n");
- buf_ = ReadSPI(0x60, (isLeft ? (byte)0x3d : (byte)0x46), 9); // get user calibration data if possible
- }
- stick_cal[isLeft ? 0 : 2] = (UInt16)((buf_[1] << 8) & 0xF00 | buf_[0]); // X Axis Max above center
- stick_cal[isLeft ? 1 : 3] = (UInt16)((buf_[2] << 4) | (buf_[1] >> 4)); // Y Axis Max above center
- stick_cal[isLeft ? 2 : 4] = (UInt16)((buf_[4] << 8) & 0xF00 | buf_[3]); // X Axis Center
- stick_cal[isLeft ? 3 : 5] = (UInt16)((buf_[5] << 4) | (buf_[4] >> 4)); // Y Axis Center
- stick_cal[isLeft ? 4 : 0] = (UInt16)((buf_[7] << 8) & 0xF00 | buf_[6]); // X Axis Min below center
- stick_cal[isLeft ? 5 : 1] = (UInt16)((buf_[8] << 4) | (buf_[7] >> 4)); // Y Axis Min below center
-
- PrintArray(stick_cal, len: 6, start: 0, format: "Stick calibration data: {0:S}");
-
- if (isPro) {
- buf_ = ReadSPI(0x80, (!isLeft ? (byte)0x12 : (byte)0x1d), 9); // get user calibration data if possible
- found = false;
- for (int i = 0; i < 9; ++i) {
- if (buf_[i] != 0xff) {
- form.AppendTextBox("Using user stick calibration data.\r\n");
- found = true;
- break;
- }
- }
- if (!found) {
- form.AppendTextBox("Using factory stick calibration data.\r\n");
- buf_ = ReadSPI(0x60, (!isLeft ? (byte)0x3d : (byte)0x46), 9); // get user calibration data if possible
- }
- stick2_cal[!isLeft ? 0 : 2] = (UInt16)((buf_[1] << 8) & 0xF00 | buf_[0]); // X Axis Max above center
- stick2_cal[!isLeft ? 1 : 3] = (UInt16)((buf_[2] << 4) | (buf_[1] >> 4)); // Y Axis Max above center
- stick2_cal[!isLeft ? 2 : 4] = (UInt16)((buf_[4] << 8) & 0xF00 | buf_[3]); // X Axis Center
- stick2_cal[!isLeft ? 3 : 5] = (UInt16)((buf_[5] << 4) | (buf_[4] >> 4)); // Y Axis Center
- stick2_cal[!isLeft ? 4 : 0] = (UInt16)((buf_[7] << 8) & 0xF00 | buf_[6]); // X Axis Min below center
- stick2_cal[!isLeft ? 5 : 1] = (UInt16)((buf_[8] << 4) | (buf_[7] >> 4)); // Y Axis Min below center
-
- PrintArray(stick2_cal, len: 6, start: 0, format: "Stick calibration data: {0:S}");
-
- buf_ = ReadSPI(0x60, (!isLeft ? (byte)0x86 : (byte)0x98), 16);
- deadzone2 = (UInt16)((buf_[4] << 8) & 0xF00 | buf_[3]);
- }
-
- buf_ = ReadSPI(0x60, (isLeft ? (byte)0x86 : (byte)0x98), 16);
- deadzone = (UInt16)((buf_[4] << 8) & 0xF00 | buf_[3]);
-
- buf_ = ReadSPI(0x80, 0x28, 10);
- acc_neutral[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
- acc_neutral[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
- acc_neutral[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
-
- buf_ = ReadSPI(0x80, 0x2E, 10);
- acc_sensiti[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
- acc_sensiti[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
- acc_sensiti[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
-
- buf_ = ReadSPI(0x80, 0x34, 10);
- gyr_neutral[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
- gyr_neutral[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
- gyr_neutral[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
-
- buf_ = ReadSPI(0x80, 0x3A, 10);
- gyr_sensiti[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
- gyr_sensiti[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
- gyr_sensiti[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
-
- PrintArray(gyr_neutral, len: 3, d: DebugType.IMU, format: "User gyro neutral position: {0:S}");
-
- // This is an extremely messy way of checking to see whether there is user stick calibration data present, but I've seen conflicting user calibration data on blank Joy-Cons. Worth another look eventually.
- if (gyr_neutral[0] + gyr_neutral[1] + gyr_neutral[2] == -3 || Math.Abs(gyr_neutral[0]) > 100 || Math.Abs(gyr_neutral[1]) > 100 || Math.Abs(gyr_neutral[2]) > 100) {
- buf_ = ReadSPI(0x60, 0x20, 10);
- acc_neutral[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
- acc_neutral[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
- acc_neutral[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
-
- buf_ = ReadSPI(0x60, 0x26, 10);
- acc_sensiti[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
- acc_sensiti[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
- acc_sensiti[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
-
- buf_ = ReadSPI(0x60, 0x2C, 10);
- gyr_neutral[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
- gyr_neutral[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
- gyr_neutral[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
-
- buf_ = ReadSPI(0x60, 0x32, 10);
- gyr_sensiti[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
- gyr_sensiti[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
- gyr_sensiti[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
-
- PrintArray(gyr_neutral, len: 3, d: DebugType.IMU, format: "Factory gyro neutral position: {0:S}");
- }
- }
- }
-
- private byte[] ReadSPI(byte addr1, byte addr2, uint len, bool print = false) {
- byte[] buf = { addr2, addr1, 0x00, 0x00, (byte)len };
- byte[] read_buf = new byte[len];
- byte[] buf_ = new byte[len + 20];
-
- for (int i = 0; i < 100; ++i) {
- buf_ = Subcommand(0x10, buf, 5, false);
- if (buf_[15] == addr2 && buf_[16] == addr1) {
- break;
- }
- }
- Array.Copy(buf_, 20, read_buf, 0, len);
- if (print) PrintArray(read_buf, DebugType.COMMS, len);
- return read_buf;
- }
-
- private void PrintArray(T[] arr, DebugType d = DebugType.NONE, uint len = 0, uint start = 0, string format = "{0:S}") {
- if (d != debug_type && debug_type != DebugType.ALL) return;
- if (len == 0) len = (uint)arr.Length;
- string tostr = "";
- for (int i = 0; i < len; ++i) {
- tostr += string.Format((arr[0] is byte) ? "{0:X2} " : ((arr[0] is float) ? "{0:F} " : "{0:D} "), arr[i + start]);
- }
- DebugPrint(string.Format(format, tostr), d);
- }
-
- private static OutputControllerXbox360InputState MapToXbox360Input(Joycon input) {
- var output = new OutputControllerXbox360InputState();
-
- var swapAB = input.swapAB;
- var swapXY = input.swapXY;
-
- var isPro = input.isPro;
- var isLeft = input.isLeft;
- var isSnes = input.isSnes;
- var other = input.other;
- var GyroAnalogSliders = input.GyroAnalogSliders;
-
- var buttons = input.buttons;
- var stick = input.stick;
- var stick2 = input.stick2;
- var sliderVal = input.sliderVal;
-
- if (isPro) {
- output.a = buttons[(int)(!swapAB ? Button.B : Button.A)];
- output.b = buttons[(int)(!swapAB ? Button.A : Button.B)];
- output.y = buttons[(int)(!swapXY ? Button.X : Button.Y)];
- output.x = buttons[(int)(!swapXY ? Button.Y : Button.X)];
-
- output.dpad_up = buttons[(int)Button.DPAD_UP];
- output.dpad_down = buttons[(int)Button.DPAD_DOWN];
- output.dpad_left = buttons[(int)Button.DPAD_LEFT];
- output.dpad_right = buttons[(int)Button.DPAD_RIGHT];
-
- output.back = buttons[(int)Button.MINUS];
- output.start = buttons[(int)Button.PLUS];
- output.guide = buttons[(int)Button.HOME];
-
- output.shoulder_left = buttons[(int)Button.SHOULDER_1];
- output.shoulder_right = buttons[(int)Button.SHOULDER2_1];
-
- output.thumb_stick_left = buttons[(int)Button.STICK];
- output.thumb_stick_right = buttons[(int)Button.STICK2];
- } else {
- if (other != null) { // no need for && other != this
- output.a = buttons[(int)(!swapAB ? isLeft ? Button.B : Button.DPAD_DOWN : isLeft ? Button.A : Button.DPAD_RIGHT)];
- output.b = buttons[(int)(swapAB ? isLeft ? Button.B : Button.DPAD_DOWN : isLeft ? Button.A : Button.DPAD_RIGHT)];
- output.y = buttons[(int)(!swapXY ? isLeft ? Button.X : Button.DPAD_UP : isLeft ? Button.Y : Button.DPAD_LEFT)];
- output.x = buttons[(int)(swapXY ? isLeft ? Button.X : Button.DPAD_UP : isLeft ? Button.Y : Button.DPAD_LEFT)];
-
- output.dpad_up = buttons[(int)(isLeft ? Button.DPAD_UP : Button.X)];
- output.dpad_down = buttons[(int)(isLeft ? Button.DPAD_DOWN : Button.B)];
- output.dpad_left = buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.Y)];
- output.dpad_right = buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.A)];
-
- output.back = buttons[(int)Button.MINUS];
- output.start = buttons[(int)Button.PLUS];
- output.guide = buttons[(int)Button.HOME];
-
- output.shoulder_left = buttons[(int)(isLeft ? Button.SHOULDER_1 : Button.SHOULDER2_1)];
- output.shoulder_right = buttons[(int)(isLeft ? Button.SHOULDER2_1 : Button.SHOULDER_1)];
-
- output.thumb_stick_left = buttons[(int)(isLeft ? Button.STICK : Button.STICK2)];
- output.thumb_stick_right = buttons[(int)(isLeft ? Button.STICK2 : Button.STICK)];
- } else { // single joycon mode
- output.a = buttons[(int)(!swapAB ? isLeft ? Button.DPAD_LEFT : Button.DPAD_RIGHT : isLeft ? Button.DPAD_DOWN : Button.DPAD_UP)];
- output.b = buttons[(int)(swapAB ? isLeft ? Button.DPAD_LEFT : Button.DPAD_RIGHT : isLeft ? Button.DPAD_DOWN : Button.DPAD_UP)];
- output.y = buttons[(int)(!swapXY ? isLeft ? Button.DPAD_RIGHT : Button.DPAD_LEFT : isLeft ? Button.DPAD_UP : Button.DPAD_DOWN)];
- output.x = buttons[(int)(swapXY ? isLeft ? Button.DPAD_RIGHT : Button.DPAD_LEFT : isLeft ? Button.DPAD_UP : Button.DPAD_DOWN)];
-
- output.back = buttons[(int)Button.MINUS] | buttons[(int)Button.HOME];
- output.start = buttons[(int)Button.PLUS] | buttons[(int)Button.CAPTURE];
-
- output.shoulder_left = buttons[(int)Button.SL];
- output.shoulder_right = buttons[(int)Button.SR];
-
- output.thumb_stick_left = buttons[(int)Button.STICK];
- }
- }
-
- // overwrite guide button if it's custom-mapped
- if (Config.Value("home") != "0")
- output.guide = false;
-
- if (!isSnes) {
- 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]);
-
- output.axis_right_x = CastStickValue((other == input && !isLeft) ? stick[0] : stick2[0]);
- output.axis_right_y = CastStickValue((other == input && !isLeft) ? stick[1] : stick2[1]);
- } else { // single joycon mode
- output.axis_left_y = CastStickValue((isLeft ? 1 : -1) * stick[0]);
- output.axis_left_x = CastStickValue((isLeft ? -1 : 1) * stick[1]);
- }
- }
-
- if (other != null || isPro) {
- byte lval = GyroAnalogSliders ? sliderVal[0] : Byte.MaxValue;
- byte rval = GyroAnalogSliders ? sliderVal[1] : Byte.MaxValue;
- output.trigger_left = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_2 : Button.SHOULDER2_2)] ? lval : 0);
- output.trigger_right = (byte)(buttons[(int)(isLeft ? Button.SHOULDER2_2 : Button.SHOULDER_2)] ? rval : 0);
- } else {
- output.trigger_left = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_2 : Button.SHOULDER_1)] ? Byte.MaxValue : 0);
- output.trigger_right = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_1 : Button.SHOULDER_2)] ? Byte.MaxValue : 0);
- }
-
- return output;
- }
-
- private static OutputControllerDualShock4InputState MapToDualShock4Input(Joycon input) {
- var output = new OutputControllerDualShock4InputState();
-
- var swapAB = input.swapAB;
- var swapXY = input.swapXY;
-
- var isPro = input.isPro;
- var isLeft = input.isLeft;
- var isSnes = input.isSnes;
- var other = input.other;
- var GyroAnalogSliders = input.GyroAnalogSliders;
-
- var buttons = input.buttons;
- var stick = input.stick;
- var stick2 = input.stick2;
- var sliderVal = input.sliderVal;
-
- if (isPro) {
- output.cross = buttons[(int)(!swapAB ? Button.B : Button.A)];
- output.circle = buttons[(int)(!swapAB ? Button.A : Button.B)];
- output.triangle = buttons[(int)(!swapXY ? Button.X : Button.Y)];
- output.square = buttons[(int)(!swapXY ? Button.Y : Button.X)];
-
-
- if (buttons[(int)Button.DPAD_UP]) {
- if (buttons[(int)Button.DPAD_LEFT])
- output.dPad = DpadDirection.Northwest;
- else if (buttons[(int)Button.DPAD_RIGHT])
- output.dPad = DpadDirection.Northeast;
- else
- output.dPad = DpadDirection.North;
- } else if (buttons[(int)Button.DPAD_DOWN]) {
- if (buttons[(int)Button.DPAD_LEFT])
- output.dPad = DpadDirection.Southwest;
- else if (buttons[(int)Button.DPAD_RIGHT])
- output.dPad = DpadDirection.Southeast;
- else
- output.dPad = DpadDirection.South;
- } else if (buttons[(int)Button.DPAD_LEFT])
- output.dPad = DpadDirection.West;
- else if (buttons[(int)Button.DPAD_RIGHT])
- output.dPad = DpadDirection.East;
-
- output.share = buttons[(int)Button.MINUS];
- output.options = buttons[(int)Button.PLUS];
- output.ps = buttons[(int)Button.HOME];
- output.touchpad = buttons[(int)Button.CAPTURE];
- output.shoulder_left = buttons[(int)Button.SHOULDER_1];
- output.shoulder_right = buttons[(int)Button.SHOULDER2_1];
- output.thumb_left = buttons[(int)Button.STICK];
- output.thumb_right = buttons[(int)Button.STICK2];
- } else {
- if (other != null) { // no need for && other != this
- output.circle = !swapAB ? buttons[(int)(isLeft ? Button.B : Button.DPAD_DOWN)] : buttons[(int)(isLeft ? Button.A : Button.DPAD_RIGHT)];
- output.cross = swapAB ? buttons[(int)(isLeft ? Button.B : Button.DPAD_DOWN)] : buttons[(int)(isLeft ? Button.A : Button.DPAD_RIGHT)];
- output.triangle = !swapXY ? buttons[(int)(isLeft ? Button.X : Button.DPAD_UP)] : buttons[(int)(isLeft ? Button.Y : Button.DPAD_LEFT)];
- output.triangle = swapXY ? buttons[(int)(isLeft ? Button.X : Button.DPAD_UP)] : buttons[(int)(isLeft ? Button.Y : Button.DPAD_LEFT)];
-
- if (buttons[(int)(isLeft ? Button.DPAD_UP : Button.X)])
- if (buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.Y)])
- output.dPad = DpadDirection.Northwest;
- else if (buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.A)])
- output.dPad = DpadDirection.Northeast;
- else
- output.dPad = DpadDirection.North;
- else if (buttons[(int)(isLeft ? Button.DPAD_DOWN : Button.B)])
- if (buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.Y)])
- output.dPad = DpadDirection.Southwest;
- else if (buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.A)])
- output.dPad = DpadDirection.Southeast;
- else
- output.dPad = DpadDirection.South;
- else if (buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.Y)])
- output.dPad = DpadDirection.West;
- else if (buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.A)])
- output.dPad = DpadDirection.East;
-
- output.share = buttons[(int)Button.MINUS];
- output.options = buttons[(int)Button.PLUS];
- output.ps = buttons[(int)Button.HOME];
- output.touchpad = buttons[(int)Button.CAPTURE];
- output.shoulder_left = buttons[(int)(isLeft ? Button.SHOULDER_1 : Button.SHOULDER2_1)];
- output.shoulder_right = buttons[(int)(isLeft ? Button.SHOULDER2_1 : Button.SHOULDER_1)];
- output.thumb_left = buttons[(int)(isLeft ? Button.STICK : Button.STICK2)];
- output.thumb_right = buttons[(int)(isLeft ? Button.STICK2 : Button.STICK)];
- } else { // single joycon mode
- output.cross = !swapAB ? buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.DPAD_RIGHT)] : buttons[(int)(isLeft ? Button.DPAD_DOWN : Button.DPAD_UP)];
- output.circle = swapAB ? buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.DPAD_RIGHT)] : buttons[(int)(isLeft ? Button.DPAD_DOWN : Button.DPAD_UP)];
- output.triangle = !swapXY ? buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.DPAD_LEFT)] : buttons[(int)(isLeft ? Button.DPAD_UP : Button.DPAD_DOWN)];
- output.square = swapXY ? buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.DPAD_LEFT)] : buttons[(int)(isLeft ? Button.DPAD_UP : Button.DPAD_DOWN)];
-
- output.square = buttons[(int)Button.MINUS] | buttons[(int)Button.HOME];
- output.options = buttons[(int)Button.PLUS] | buttons[(int)Button.CAPTURE];
-
- output.shoulder_left = buttons[(int)Button.SL];
- output.shoulder_right = buttons[(int)Button.SR];
-
- output.thumb_left = buttons[(int)Button.STICK];
- }
- }
-
- // overwrite guide button if it's custom-mapped
- if (Config.Value("home") != "0")
- output.ps = false;
-
- if (!isSnes) {
- 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]);
- output.thumb_right_x = CastStickValueByte((other == input && !isLeft) ? -stick[0] : -stick2[0]);
- output.thumb_right_y = CastStickValueByte((other == input && !isLeft) ? stick[1] : stick2[1]);
- } else { // single joycon mode
- output.thumb_left_y = CastStickValueByte((isLeft ? 1 : -1) * stick[0]);
- output.thumb_left_x = CastStickValueByte((isLeft ? 1 : -1) * stick[1]);
- }
- }
-
- if (other != null || isPro) {
- byte lval = GyroAnalogSliders ? sliderVal[0] : Byte.MaxValue;
- byte rval = GyroAnalogSliders ? sliderVal[1] : Byte.MaxValue;
- output.trigger_left_value = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_2 : Button.SHOULDER2_2)] ? lval : 0);
- output.trigger_right_value = (byte)(buttons[(int)(isLeft ? Button.SHOULDER2_2 : Button.SHOULDER_2)] ? rval : 0);
- } else {
- output.trigger_left_value = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_2 : Button.SHOULDER_1)] ? Byte.MaxValue : 0);
- output.trigger_right_value = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_1 : Button.SHOULDER_2)] ? Byte.MaxValue : 0);
- }
-
- return output;
- }
- }
+ public class Joycon {
+ float timing = 120.0f;
+
+ public string path = String.Empty;
+ public bool isPro = false;
+ public bool isSnes = false;
+ bool isUSB = false;
+ public Joycon other;
+ public bool active_gyro = false;
+
+ public bool send = true;
+
+ public enum DebugType : int {
+ NONE,
+ ALL,
+ COMMS,
+ THREADING,
+ IMU,
+ RUMBLE,
+ };
+ public DebugType debug_type = DebugType.NONE;
+ public bool isLeft;
+ public enum state_ : uint {
+ NOT_ATTACHED,
+ DROPPED,
+ NO_JOYCONS,
+ ATTACHED,
+ INPUT_MODE_0x30,
+ IMU_DATA_OK,
+ };
+ public state_ state;
+ public enum Button : int {
+ DPAD_DOWN = 0,
+ DPAD_RIGHT = 1,
+ DPAD_LEFT = 2,
+ DPAD_UP = 3,
+ SL = 4,
+ SR = 5,
+ MINUS = 6,
+ HOME = 7,
+ PLUS = 8,
+ CAPTURE = 9,
+ STICK = 10,
+ SHOULDER_1 = 11,
+ SHOULDER_2 = 12,
+
+ // For pro controller
+ B = 13,
+ A = 14,
+ Y = 15,
+ X = 16,
+ STICK2 = 17,
+ SHOULDER2_1 = 18,
+ SHOULDER2_2 = 19,
+ };
+ private bool[] buttons_down = new bool[20];
+ private bool[] buttons_up = new bool[20];
+ private bool[] buttons = new bool[20];
+ private bool[] down_ = new bool[20];
+ private long[] buttons_down_timestamp = new long[20];
+
+ private float[] stick = { 0, 0 };
+ private float[] stick2 = { 0, 0 };
+
+ private IntPtr handle;
+
+ byte[] default_buf = { 0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40 };
+
+ private byte[] stick_raw = { 0, 0, 0 };
+ private UInt16[] stick_cal = { 0, 0, 0, 0, 0, 0 };
+ private UInt16 deadzone;
+ private UInt16[] stick_precal = { 0, 0 };
+
+ private byte[] stick2_raw = { 0, 0, 0 };
+ private UInt16[] stick2_cal = { 0, 0, 0, 0, 0, 0 };
+ private UInt16 deadzone2;
+ private UInt16[] stick2_precal = { 0, 0 };
+
+ private bool stop_polling = false;
+ private int timestamp;
+ private bool first_imu_packet = true;
+ private bool imu_enabled = false;
+ private Int16[] acc_r = { 0, 0, 0 };
+ private Int16[] acc_neutral = { 0, 0, 0 };
+ private Int16[] acc_sensiti = { 0, 0, 0 };
+ private Vector3 acc_g;
+
+ private Int16[] gyr_r = { 0, 0, 0 };
+ private Int16[] gyr_neutral = { 0, 0, 0 };
+ private Int16[] gyr_sensiti = { 0, 0, 0 };
+ private Vector3 gyr_g;
+
+ private short[] acc_sen = new short[3]{
+ 16384,
+ 16384,
+ 16384
+ };
+ private short[] gyr_sen = new short[3]{
+ 18642,
+ 18642,
+ 18642
+ };
+
+ private Int16[] pro_hor_offset = { -710, 0, 0 };
+ private Int16[] left_hor_offset = { 0, 0, 0 };
+ private Int16[] right_hor_offset = { 0, 0, 0 };
+
+ private bool do_localize;
+ private float filterweight;
+ private const uint report_len = 49;
+
+ private struct Rumble {
+ private float h_f, l_f;
+ public float t, amp, fullamp;
+ public bool timed_rumble;
+
+ public void set_vals(float low_freq, float high_freq, float amplitude, int time = 0) {
+ h_f = high_freq;
+ amp = amplitude;
+ fullamp = amplitude;
+ l_f = low_freq;
+ timed_rumble = false;
+ t = 0;
+ if (time != 0) {
+ t = time / 1000f;
+ timed_rumble = true;
+ }
+ }
+ public Rumble(float low_freq, float high_freq, float amplitude, int time = 0) {
+ h_f = high_freq;
+ amp = amplitude;
+ fullamp = amplitude;
+ l_f = low_freq;
+ timed_rumble = false;
+ t = 0;
+ if (time != 0) {
+ t = time / 1000f;
+ timed_rumble = true;
+ }
+ }
+ private float clamp(float x, float min, float max) {
+ if (x < min) return min;
+ if (x > max) return max;
+ return x;
+ }
+ public byte[] GetData() {
+ byte[] rumble_data = new byte[8];
+ if (amp == 0.0f) {
+ rumble_data[0] = 0x0;
+ rumble_data[1] = 0x1;
+ rumble_data[2] = 0x40;
+ rumble_data[3] = 0x40;
+ } else {
+ l_f = clamp(l_f, 40.875885f, 626.286133f);
+ amp = clamp(amp, 0.0f, 1.0f);
+ h_f = clamp(h_f, 81.75177f, 1252.572266f);
+ UInt16 hf = (UInt16)((Math.Round(32f * Math.Log(h_f * 0.1f, 2)) - 0x60) * 4);
+ byte lf = (byte)(Math.Round(32f * Math.Log(l_f * 0.1f, 2)) - 0x40);
+ byte hf_amp;
+ if (amp == 0) hf_amp = 0;
+ else if (amp < 0.117) hf_amp = (byte)(((Math.Log(amp * 1000, 2) * 32) - 0x60) / (5 - Math.Pow(amp, 2)) - 1);
+ else if (amp < 0.23) hf_amp = (byte)(((Math.Log(amp * 1000, 2) * 32) - 0x60) - 0x5c);
+ else hf_amp = (byte)((((Math.Log(amp * 1000, 2) * 32) - 0x60) * 2) - 0xf6);
+
+ UInt16 lf_amp = (UInt16)(Math.Round((double)hf_amp) * .5);
+ byte parity = (byte)(lf_amp % 2);
+ if (parity > 0) {
+ --lf_amp;
+ }
+
+ lf_amp = (UInt16)(lf_amp >> 1);
+ lf_amp += 0x40;
+ if (parity > 0) lf_amp |= 0x8000;
+ rumble_data = new byte[8];
+ rumble_data[0] = (byte)(hf & 0xff);
+ rumble_data[1] = (byte)((hf >> 8) & 0xff);
+ rumble_data[2] = lf;
+ rumble_data[1] += hf_amp;
+ rumble_data[2] += (byte)((lf_amp >> 8) & 0xff);
+ rumble_data[3] += (byte)(lf_amp & 0xff);
+ }
+ for (int i = 0; i < 4; ++i) {
+ rumble_data[4 + i] = rumble_data[i];
+ }
+
+ return rumble_data;
+ }
+ }
+
+ private Rumble rumble_obj;
+
+ private byte global_count = 0;
+ private string debug_str;
+
+ // For UdpServer
+ public int PadId = 0;
+ public int battery = -1;
+ public int model = 2;
+ public int constate = 2;
+ public int connection = 3;
+
+ public PhysicalAddress PadMacAddress = new PhysicalAddress(new byte[] { 01, 02, 03, 04, 05, 06 });
+ public ulong Timestamp = 0;
+ public int packetCounter = 0;
+
+ public OutputControllerXbox360 out_xbox;
+ public OutputControllerDualShock4 out_ds4;
+ ushort ds4_ts = 0;
+ ulong lag;
+
+ int rumblePeriod = Int32.Parse(ConfigurationManager.AppSettings["RumblePeriod"]);
+ int lowFreq = Int32.Parse(ConfigurationManager.AppSettings["LowFreqRumble"]);
+ int highFreq = Int32.Parse(ConfigurationManager.AppSettings["HighFreqRumble"]);
+
+ bool toRumble = Boolean.Parse(ConfigurationManager.AppSettings["EnableRumble"]);
+
+ bool showAsXInput = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsXInput"]);
+ bool showAsDS4 = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsDS4"]);
+
+ public MainForm form;
+
+ public byte LED = 0x0;
+
+ public string serial_number;
+
+ private float[] activeData;
+
+ public Joycon(IntPtr handle_, bool imu, bool localize, float alpha, bool left, string path, string serialNum, int id = 0, bool isPro = false, bool isSnes = false) {
+ serial_number = serialNum;
+ activeData = new float[6];
+ handle = handle_;
+ imu_enabled = imu;
+ do_localize = localize;
+ rumble_obj = new Rumble(160, 320, 0);
+ for (int i = 0; i < buttons_down_timestamp.Length; i++)
+ buttons_down_timestamp[i] = -1;
+ filterweight = alpha;
+ isLeft = left;
+
+ PadId = id;
+ LED = (byte)(0x1 << PadId);
+ this.isPro = isPro || isSnes;
+ this.isSnes = isSnes;
+ isUSB = serialNum == "000000000001";
+
+ this.path = path;
+
+ connection = isUSB ? 0x01 : 0x02;
+
+ if (showAsXInput) {
+ out_xbox = new OutputControllerXbox360();
+ out_xbox.FeedbackReceived += ReceiveRumble;
+ }
+
+ if (showAsDS4) {
+ out_ds4 = new OutputControllerDualShock4();
+
+ if (toRumble)
+ out_ds4.FeedbackReceived += Ds4_FeedbackReceived;
+ }
+ }
+
+ public void getActiveData() {
+ this.activeData = form.activeCaliData(serial_number);
+ }
+
+ public void ReceiveRumble(Xbox360FeedbackReceivedEventArgs e) {
+ SetRumble(lowFreq, highFreq, (float)(e.LargeMotor + e.SmallMotor) / (float)255, rumblePeriod);
+
+ if (other != null && other != this)
+ other.SetRumble(lowFreq, highFreq, (float)(e.LargeMotor + e.SmallMotor) / (float)255, rumblePeriod);
+ }
+
+ public void Ds4_FeedbackReceived(DualShock4FeedbackReceivedEventArgs e) {
+ SetRumble(lowFreq, highFreq, (float)(e.LargeMotor + e.SmallMotor) / (float)255, rumblePeriod);
+
+ if (other != null && other != this)
+ other.SetRumble(lowFreq, highFreq, (float)(e.LargeMotor + e.SmallMotor) / (float)255, rumblePeriod);
+ }
+
+ public void DebugPrint(String s, DebugType d) {
+ if (debug_type == DebugType.NONE) return;
+ if (d == DebugType.ALL || d == debug_type || debug_type == DebugType.ALL) {
+ form.AppendTextBox(s + "\r\n");
+ }
+ }
+ public bool GetButtonDown(Button b) {
+ return buttons_down[(int)b];
+ }
+ public bool GetButton(Button b) {
+ return buttons[(int)b];
+ }
+ public bool GetButtonUp(Button b) {
+ return buttons_up[(int)b];
+ }
+ public float[] GetStick() {
+ return stick;
+ }
+ public float[] GetStick2() {
+ return stick2;
+ }
+ public Vector3 GetGyro() {
+ return gyr_g;
+ }
+ public Vector3 GetAccel() {
+ return acc_g;
+ }
+ public int Attach(byte leds_ = 0x0) {
+ state = state_.ATTACHED;
+
+ // Make sure command is received
+ HIDapi.hid_set_nonblocking(handle, 0);
+
+ byte[] a = { 0x0 };
+
+ // Connect
+ if (!isUSB) {
+ // Input report mode
+ Subcommand(0x03, new byte[] { 0x30 }, 1, false);
+
+ a[0] = 0x1;
+ dump_calibration_data();
+ } else {
+ Subcommand(0x03, new byte[] { 0x3f }, 1, false);
+
+ a = Enumerable.Repeat((byte)0, 64).ToArray();
+ form.AppendTextBox("Using USB.\r\n");
+
+ a[0] = 0x80;
+ a[1] = 0x1;
+ HIDapi.hid_write(handle, a, new UIntPtr(2));
+ HIDapi.hid_read(handle, a, new UIntPtr(64));
+
+ if (a[2] != 0x3) {
+ PadMacAddress = new PhysicalAddress(new byte[] { a[9], a[8], a[7], a[6], a[5], a[4] });
+ }
+
+ // USB Pairing
+ a = Enumerable.Repeat((byte)0, 64).ToArray();
+ a[0] = 0x80; a[1] = 0x2; // Handshake
+ HIDapi.hid_write(handle, a, new UIntPtr(2));
+
+ a[0] = 0x80; a[1] = 0x3; // 3Mbit baud rate
+ HIDapi.hid_write(handle, a, new UIntPtr(2));
+
+ a[0] = 0x80; a[1] = 0x2; // Handshake at new baud rate
+ HIDapi.hid_write(handle, a, new UIntPtr(2));
+
+ a[0] = 0x80; a[1] = 0x4; // Prevent HID timeout
+ HIDapi.hid_write(handle, a, new UIntPtr(2)); // doesn't actually prevent timout...
+
+ dump_calibration_data();
+ }
+
+ // Bluetooth manual pairing
+ byte[] btmac_host = Program.btMAC.GetAddressBytes();
+ // send host MAC and acquire Joycon MAC
+ //byte[] reply = Subcommand(0x01, new byte[] { 0x01, btmac_host[5], btmac_host[4], btmac_host[3], btmac_host[2], btmac_host[1], btmac_host[0] }, 7, true);
+ //byte[] LTKhash = Subcommand(0x01, new byte[] { 0x02 }, 1, true);
+ // save pairing info
+ //Subcommand(0x01, new byte[] { 0x03 }, 1, true);
+
+ BlinkHomeLight();
+
+ a[0] = leds_;
+ Subcommand(0x30, a, 1);
+ Subcommand(0x40, new byte[] { (imu_enabled ? (byte)0x1 : (byte)0x0) }, 1, true);
+ Subcommand(0x3, new byte[] { 0x30 }, 1, true);
+ Subcommand(0x48, new byte[] { 0x01 }, 1, true);
+
+ Subcommand(0x41, new byte[] { 0x03, 0x00, 0x00, 0x01 }, 4, false); // higher gyro performance rate
+
+ DebugPrint("Done with init.", DebugType.COMMS);
+
+ HIDapi.hid_set_nonblocking(handle, 1);
+
+ // send ping to USB to not time out instantly
+ if (isUSB)
+ SendRumble(rumble_obj.GetData());
+
+ return 0;
+ }
+
+ public void SetPlayerLED(byte leds_ = 0x0) {
+ Subcommand(0x30, new byte[] { leds_ }, 1);
+ }
+
+ public void BlinkHomeLight() { // do not call after initial setup
+ byte[] a = Enumerable.Repeat((byte)0xFF, 25).ToArray();
+ a[0] = 0x18;
+ a[1] = 0x01;
+ Subcommand(0x38, a, 25, false);
+ }
+
+ public void SetHomeLight(bool on) {
+ byte[] a = Enumerable.Repeat((byte)0xFF, 25).ToArray();
+ if (on) {
+ a[0] = 0x1F;
+ a[1] = 0xF0;
+ } else {
+ a[0] = 0x10;
+ a[1] = 0x01;
+ }
+ Subcommand(0x38, a, 25, false);
+ }
+
+ private void SetHCIState(byte state) {
+ byte[] a = { state };
+ Subcommand(0x06, a, 1, false);
+ }
+
+ public void PowerOff() {
+ if (state > state_.DROPPED) {
+ HIDapi.hid_set_nonblocking(handle, 0);
+ SetHCIState(0x00);
+ state = state_.DROPPED;
+ }
+ }
+
+ private void BatteryChanged() { // battery changed level
+ foreach (var v in form.con) {
+ if (v.Tag == this) {
+ switch (battery) {
+ case 4:
+ v.BackColor = System.Drawing.Color.FromArgb(0xAA, System.Drawing.Color.Green);
+ break;
+ case 3:
+ v.BackColor = System.Drawing.Color.FromArgb(0xAA, System.Drawing.Color.Green);
+ break;
+ case 2:
+ v.BackColor = System.Drawing.Color.FromArgb(0xAA, System.Drawing.Color.GreenYellow);
+ break;
+ case 1:
+ v.BackColor = System.Drawing.Color.FromArgb(0xAA, System.Drawing.Color.Orange);
+ break;
+ default:
+ v.BackColor = System.Drawing.Color.FromArgb(0xAA, System.Drawing.Color.Red);
+ break;
+ }
+ }
+ }
+ }
+
+ public void SetFilterCoeff(float a) {
+ filterweight = a;
+ }
+
+ public void Detach(bool close = false) {
+ stop_polling = true;
+
+ if (out_xbox != null) {
+ out_xbox.Disconnect();
+ }
+
+ if (out_ds4 != null) {
+ out_ds4.Disconnect();
+ }
+
+ if (state > state_.NO_JOYCONS) {
+ HIDapi.hid_set_nonblocking(handle, 0);
+
+ Subcommand(0x40, new byte[] { 0x0 }, 1); // disable IMU sensor
+ //Subcommand(0x48, new byte[] { 0x0 }, 1); // Would turn off rumble?
+
+ if (isUSB) {
+ byte[] a = Enumerable.Repeat((byte)0, 64).ToArray();
+ a[0] = 0x80; a[1] = 0x5; // Allow device to talk to BT again
+ HIDapi.hid_write(handle, a, new UIntPtr(2));
+ a[0] = 0x80; a[1] = 0x6; // Allow device to talk to BT again
+ HIDapi.hid_write(handle, a, new UIntPtr(2));
+ }
+ }
+ if (close || state > state_.DROPPED) {
+ HIDapi.hid_close(handle);
+ }
+ state = state_.NOT_ATTACHED;
+ }
+
+ // TODO: Improve this loop, make USB not laggy
+ private byte ts_en;
+ private int ReceiveRaw() {
+ if (handle == IntPtr.Zero) return -2;
+ HIDapi.hid_set_nonblocking(handle, 1);
+ byte[] raw_buf = new byte[report_len];
+ int ret = HIDapi.hid_read_timeout(handle, raw_buf, new UIntPtr(report_len), 5);
+ if (ret > 0) {
+ // Process packets as soon as they come
+ for (int n = 0; n < 3; n++) {
+ ExtractIMUValues(raw_buf, n);
+
+ byte lag = (byte)Math.Max(0, raw_buf[1] - ts_en - 3);
+ if (n == 0) {
+ Timestamp += (ulong)lag * 5000; // add lag once
+ ProcessButtonsAndStick(raw_buf);
+
+ // process buttons here to have them affect DS4
+ DoThingsWithButtons();
+
+ int newbat = battery;
+ battery = (raw_buf[2] >> 4) / 2;
+ if (newbat != battery)
+ BatteryChanged();
+ }
+ Timestamp += 5000; // 5ms difference
+
+ packetCounter++;
+ if (Program.server != null)
+ Program.server.NewReportIncoming(this);
+
+ if (out_ds4 != null) {
+ try {
+ out_ds4.UpdateInput(MapToDualShock4Input(this));
+ } catch (Exception e) {
+ // ignore /shrug
+ }
+ }
+ }
+
+ // no reason to send XInput reports so often
+ if (out_xbox != null) {
+ try {
+ out_xbox.UpdateInput(MapToXbox360Input(this));
+ } catch (Exception e) {
+ // ignore /shrug
+ }
+ }
+
+
+ if (ts_en == raw_buf[1] && !isSnes) {
+ form.AppendTextBox("Duplicate timestamp enqueued.\r\n");
+ DebugPrint(string.Format("Duplicate timestamp enqueued. TS: {0:X2}", ts_en), DebugType.THREADING);
+ }
+ ts_en = raw_buf[1];
+ DebugPrint(string.Format("Enqueue. Bytes read: {0:D}. Timestamp: {1:X2}", ret, raw_buf[1]), DebugType.THREADING);
+ }
+ return ret;
+ }
+
+ bool dragToggle = Boolean.Parse(ConfigurationManager.AppSettings["DragToggle"]);
+ Dictionary mouse_toggle_btn = new Dictionary();
+ private void Simulate(string s, bool click = true, bool up = false) {
+ if (s.StartsWith("key_")) {
+ WindowsInput.Events.KeyCode key = (WindowsInput.Events.KeyCode)Int32.Parse(s.Substring(4));
+ if (click) {
+ WindowsInput.Simulate.Events().Click(key).Invoke();
+ } else {
+ if (up) {
+ WindowsInput.Simulate.Events().Release(key).Invoke();
+ } else {
+ WindowsInput.Simulate.Events().Hold(key).Invoke();
+ }
+ }
+ } else if (s.StartsWith("mse_")) {
+ WindowsInput.Events.ButtonCode button = (WindowsInput.Events.ButtonCode)Int32.Parse(s.Substring(4));
+ if (click) {
+ WindowsInput.Simulate.Events().Click(button).Invoke();
+ } else {
+ if (dragToggle) {
+ if (!up) {
+ bool release;
+ mouse_toggle_btn.TryGetValue((int)button, out release);
+ if (release)
+ WindowsInput.Simulate.Events().Release(button).Invoke();
+ else
+ WindowsInput.Simulate.Events().Hold(button).Invoke();
+ mouse_toggle_btn[(int)button] = !release;
+ }
+ } else {
+ if (up) {
+ WindowsInput.Simulate.Events().Release(button).Invoke();
+ } else {
+ WindowsInput.Simulate.Events().Hold(button).Invoke();
+ }
+ }
+ }
+ }
+ }
+
+ // For Joystick->Joystick inputs
+ private void SimulateContinous(int origin, string s) {
+ if (s.StartsWith("joy_")) {
+ int button = Int32.Parse(s.Substring(4));
+ buttons[button] |= buttons[origin];
+ }
+ }
+
+ string extraGyroFeature = ConfigurationManager.AppSettings["GyroToJoyOrMouse"];
+ int GyroMouseSensitivity = Int32.Parse(ConfigurationManager.AppSettings["GyroMouseSensitivity"]);
+ bool HomeLongPowerOff = Boolean.Parse(ConfigurationManager.AppSettings["HomeLongPowerOff"]);
+
+ bool GyroAnalogSliders = Boolean.Parse(ConfigurationManager.AppSettings["GyroAnalogSliders"]);
+ int GyroAnalogSensitivity = Int32.Parse(ConfigurationManager.AppSettings["GyroAnalogSensitivity"]);
+ byte[] sliderVal = new byte[] { 0, 0 };
+ private void DoThingsWithButtons() {
+ int powerOffButton = (int)((isPro || !isLeft || other != null) ? Button.HOME : Button.CAPTURE);
+
+ if (HomeLongPowerOff && buttons[powerOffButton]) {
+ long timestamp = Stopwatch.GetTimestamp();
+ if ((timestamp - buttons_down_timestamp[powerOffButton]) / 10000 > 2000.0) {
+ if (other != null)
+ other.PowerOff();
+
+ PowerOff();
+ return;
+ }
+ }
+
+ if (buttons_down[(int)Button.CAPTURE])
+ Simulate(Config.Value("capture"));
+ if (buttons_down[(int)Button.HOME])
+ Simulate(Config.Value("home"));
+ SimulateContinous((int)Button.CAPTURE, Config.Value("capture"));
+ SimulateContinous((int)Button.HOME, Config.Value("home"));
+
+ if (isLeft) {
+ if (buttons_down[(int)Button.SL])
+ Simulate(Config.Value("sl_l"), false, false);
+ if (buttons_up[(int)Button.SL])
+ Simulate(Config.Value("sl_l"), false, true);
+ if (buttons_down[(int)Button.SR])
+ Simulate(Config.Value("sr_l"), false, false);
+ if (buttons_up[(int)Button.SR])
+ Simulate(Config.Value("sr_l"), false, true);
+
+ SimulateContinous((int)Button.SL, Config.Value("sl_l"));
+ SimulateContinous((int)Button.SR, Config.Value("sr_l"));
+ } else {
+ if (buttons_down[(int)Button.SL])
+ Simulate(Config.Value("sl_r"), false, false);
+ if (buttons_up[(int)Button.SL])
+ Simulate(Config.Value("sl_r"), false, true);
+ if (buttons_down[(int)Button.SR])
+ Simulate(Config.Value("sr_r"), false, false);
+ if (buttons_up[(int)Button.SR])
+ Simulate(Config.Value("sr_r"), false, true);
+
+ SimulateContinous((int)Button.SL, Config.Value("sl_r"));
+ SimulateContinous((int)Button.SR, Config.Value("sr_r"));
+ }
+
+ if (GyroAnalogSliders && (other != null || isPro)) {
+ Button leftT = isLeft ? Button.SHOULDER_2 : Button.SHOULDER2_2;
+ Button rightT = isLeft ? Button.SHOULDER2_2 : Button.SHOULDER_2;
+ float dt = 0.015f; // 15ms
+ Joycon left = isLeft ? this : (isPro ? this : this.other); Joycon right = !isLeft ? this : (isPro ? this : this.other);
+ int ldy = (int)(GyroAnalogSensitivity * (left.gyr_g.Y * dt) * (Math.Abs(left.gyr_g.Y) < 1 ? 0 : 1));
+ int rdy = (int)(GyroAnalogSensitivity * (right.gyr_g.Y * dt) * (Math.Abs(right.gyr_g.Y) < 1 ? 0 : 1));
+
+ if (buttons[(int)leftT]) {
+ sliderVal[0] = (byte)Math.Min(Byte.MaxValue, Math.Max(0, (int)sliderVal[0] + ldy));
+ } else {
+ sliderVal[0] = 0;
+ }
+
+ if (buttons[(int)rightT]) {
+ sliderVal[1] = (byte)Math.Min(Byte.MaxValue, Math.Max(0, (int)sliderVal[1] + rdy));
+ } else {
+ sliderVal[1] = 0;
+ }
+ }
+
+ if (extraGyroFeature == "joy") {
+ // TODO
+ } else if (extraGyroFeature == "mouse" && (isPro || (other == null) || (other != null && (Boolean.Parse(ConfigurationManager.AppSettings["GyroMouseLeftHanded"]) ? isLeft : !isLeft)))) {
+ string res_val = Config.Value("active_gyro");
+ if (res_val.StartsWith("joy_")) {
+ int i = Int32.Parse(res_val.Substring(4));
+ if (buttons_down[i] || (other != null && other.buttons_down[i]))
+ active_gyro = true;
+ else if (buttons_up[i] || (other != null && other.buttons_up[i]))
+ active_gyro = false;
+ }
+
+ float dt = 0.015f; // 15ms
+
+ // gyro data is in degrees/s
+ if (Config.Value("active_gyro") == "0" || active_gyro) {
+ int dx = (int)(GyroMouseSensitivity * (gyr_g.Z * dt) * (Math.Abs(gyr_g.Z) < 1 ? 0 : 1));
+ int dy = (int)-(GyroMouseSensitivity * (gyr_g.Y * dt) * (Math.Abs(gyr_g.Y) < 1 ? 0 : 1));
+
+ WindowsInput.Simulate.Events().MoveBy(dx, dy).Invoke();
+ }
+
+ // reset mouse position to centre of primary monitor
+ res_val = Config.Value("reset_mouse");
+ if (res_val.StartsWith("joy_")) {
+ int i = Int32.Parse(res_val.Substring(4));
+ if (buttons_down[i] || (other != null && other.buttons_down[i]))
+ WindowsInput.Simulate.Events().MoveTo(Screen.PrimaryScreen.Bounds.Width / 2, Screen.PrimaryScreen.Bounds.Height / 2).Invoke();
+ }
+ }
+ }
+
+ // TODO: Fix?
+ private Thread PollThreadObj; // pro times out over time randomly if it was USB and then bluetooth??
+ private void Poll() {
+ int attempts = 0;
+ Stopwatch watch = new Stopwatch();
+ watch.Start();
+ while (!stop_polling & state > state_.NO_JOYCONS) {
+ if (!isSnes && (rumble_obj.t > 0))
+ SendRumble(rumble_obj.GetData());
+
+ int a = ReceiveRaw();
+
+ if (a > 0 && state > state_.DROPPED) {
+ state = state_.IMU_DATA_OK;
+ attempts = 0;
+
+ // Needed for USB to not time out; I think USB requires a reply message after every packet sent
+ if (isUSB)
+ SendRumble(rumble_obj.GetData());
+ } else if (attempts > 240) {
+ state = state_.DROPPED;
+ form.AppendTextBox("Dropped.\r\n");
+
+ DebugPrint("Connection lost. Is the Joy-Con connected?", DebugType.ALL);
+ break;
+ } else if (a < 0) {
+ // An error on read.
+ //form.AppendTextBox("Pause 5ms");
+ Thread.Sleep((Int32)5);
+ ++attempts;
+ } else if (a == 0) {
+ // The non-blocking read timed out. No need to sleep.
+ // No need to increase attempts because it's not an error.
+ }
+ }
+ }
+
+ public void Update() {
+ if (state > state_.NO_JOYCONS) {
+ if (rumble_obj.timed_rumble) {
+ if (rumble_obj.t < 0) {
+ rumble_obj.set_vals(lowFreq, highFreq, 0, 0);
+ } else {
+ rumble_obj.t -= (1 / timing);
+ //rumble_obj.amp = (float) Math.Sin(((timing - rumble_obj.t * 1000f) / timing) * Math.PI) * rumble_obj.fullamp;
+ }
+ }
+ }
+ }
+
+ public float[] otherStick = { 0, 0 };
+
+ bool swapAB = Boolean.Parse(ConfigurationManager.AppSettings["SwapAB"]);
+ bool swapXY = Boolean.Parse(ConfigurationManager.AppSettings["SwapXY"]);
+ private int ProcessButtonsAndStick(byte[] report_buf) {
+ if (report_buf[0] == 0x00) return -1;
+ if (!isSnes) {
+ 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)];
+
+ if (isPro) {
+ stick2_raw[0] = report_buf[6 + (!isLeft ? 0 : 3)];
+ stick2_raw[1] = report_buf[7 + (!isLeft ? 0 : 3)];
+ stick2_raw[2] = report_buf[8 + (!isLeft ? 0 : 3)];
+ }
+
+ stick_precal[0] = (UInt16)(stick_raw[0] | ((stick_raw[1] & 0xf) << 8));
+ stick_precal[1] = (UInt16)((stick_raw[1] >> 4) | (stick_raw[2] << 4));
+ ushort[] cal = form.nonOriginal ? new ushort[6] { 2048, 2048, 2048, 2048, 2048, 2048 } : stick_cal;
+ ushort dz = form.nonOriginal ? (ushort)200 : deadzone;
+ stick = CenterSticks(stick_precal, cal, dz);
+
+ 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, form.nonOriginal ? cal : stick2_cal, deadzone2);
+ }
+
+ // Read other Joycon's sticks
+ if (isLeft && other != null && other != this) {
+ stick2 = otherStick;
+ other.otherStick = stick;
+ }
+
+ if (!isLeft && other != null && other != this) {
+ Array.Copy(stick, stick2, 2);
+ stick = otherStick;
+ other.otherStick = stick2;
+ }
+ }
+ //
+
+ // Set button states both for server and ViGEm
+ lock (buttons) {
+ lock (down_) {
+ for (int i = 0; i < buttons.Length; ++i) {
+ down_[i] = buttons[i];
+ }
+ }
+ buttons = new bool[20];
+
+ buttons[(int)Button.DPAD_DOWN] = (report_buf[3 + (isLeft ? 2 : 0)] & (isLeft ? 0x01 : 0x04)) != 0;
+ buttons[(int)Button.DPAD_RIGHT] = (report_buf[3 + (isLeft ? 2 : 0)] & (isLeft ? 0x04 : 0x08)) != 0;
+ buttons[(int)Button.DPAD_UP] = (report_buf[3 + (isLeft ? 2 : 0)] & (isLeft ? 0x02 : 0x02)) != 0;
+ buttons[(int)Button.DPAD_LEFT] = (report_buf[3 + (isLeft ? 2 : 0)] & (isLeft ? 0x08 : 0x01)) != 0;
+ buttons[(int)Button.HOME] = ((report_buf[4] & 0x10) != 0);
+ buttons[(int)Button.CAPTURE] = ((report_buf[4] & 0x20) != 0);
+ buttons[(int)Button.MINUS] = ((report_buf[4] & 0x01) != 0);
+ buttons[(int)Button.PLUS] = ((report_buf[4] & 0x02) != 0);
+ buttons[(int)Button.STICK] = ((report_buf[4] & (isLeft ? 0x08 : 0x04)) != 0);
+ buttons[(int)Button.SHOULDER_1] = (report_buf[3 + (isLeft ? 2 : 0)] & 0x40) != 0;
+ buttons[(int)Button.SHOULDER_2] = (report_buf[3 + (isLeft ? 2 : 0)] & 0x80) != 0;
+ buttons[(int)Button.SR] = (report_buf[3 + (isLeft ? 2 : 0)] & 0x10) != 0;
+ buttons[(int)Button.SL] = (report_buf[3 + (isLeft ? 2 : 0)] & 0x20) != 0;
+
+ if (isPro) {
+ buttons[(int)Button.B] = (report_buf[3 + (!isLeft ? 2 : 0)] & (!isLeft ? 0x01 : 0x04)) != 0;
+ buttons[(int)Button.A] = (report_buf[3 + (!isLeft ? 2 : 0)] & (!isLeft ? 0x04 : 0x08)) != 0;
+ buttons[(int)Button.X] = (report_buf[3 + (!isLeft ? 2 : 0)] & (!isLeft ? 0x02 : 0x02)) != 0;
+ buttons[(int)Button.Y] = (report_buf[3 + (!isLeft ? 2 : 0)] & (!isLeft ? 0x08 : 0x01)) != 0;
+
+ buttons[(int)Button.STICK2] = ((report_buf[4] & (!isLeft ? 0x08 : 0x04)) != 0);
+ buttons[(int)Button.SHOULDER2_1] = (report_buf[3 + (!isLeft ? 2 : 0)] & 0x40) != 0;
+ buttons[(int)Button.SHOULDER2_2] = (report_buf[3 + (!isLeft ? 2 : 0)] & 0x80) != 0;
+ }
+
+ if (other != null && other != this) {
+ 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];
+ }
+
+ if (isLeft && other != null && other != this) {
+ buttons[(int)Button.HOME] = other.buttons[(int)Button.HOME];
+ buttons[(int)Button.PLUS] = other.buttons[(int)Button.PLUS];
+ }
+
+ if (!isLeft && other != null && other != this) {
+ buttons[(int)Button.MINUS] = other.buttons[(int)Button.MINUS];
+ }
+
+ long timestamp = Stopwatch.GetTimestamp();
+
+ lock (buttons_up) {
+ lock (buttons_down) {
+ for (int i = 0; i < buttons.Length; ++i) {
+ buttons_up[i] = (down_[i] & !buttons[i]);
+ buttons_down[i] = (!down_[i] & buttons[i]);
+ if (down_[i] != buttons[i])
+ buttons_down_timestamp[i] = (buttons[i] ? timestamp : -1);
+ }
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ // Get Gyro/Accel data
+ private void ExtractIMUValues(byte[] report_buf, int n = 0) {
+ if (!isSnes) {
+ 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));
+ acc_r[0] = (Int16)(report_buf[13 + n * 12] | ((report_buf[14 + n * 12] << 8) & 0xff00));
+ acc_r[1] = (Int16)(report_buf[15 + n * 12] | ((report_buf[16 + n * 12] << 8) & 0xff00));
+ acc_r[2] = (Int16)(report_buf[17 + n * 12] | ((report_buf[18 + n * 12] << 8) & 0xff00));
+
+ if (form.nonOriginal) {
+ for (int i = 0; i < 3; ++i) {
+ switch (i) {
+ case 0:
+ acc_g.X = (acc_r[i] - activeData[3]) * (1.0f / acc_sen[i]) * 4.0f;
+ gyr_g.X = (gyr_r[i] - activeData[0]) * (816.0f / gyr_sen[i]);
+ if (form.calibrate) {
+ form.xA.Add(acc_r[i]);
+ form.xG.Add(gyr_r[i]);
+ }
+ break;
+ case 1:
+ acc_g.Y = (!isLeft ? -1 : 1) * (acc_r[i] - activeData[4]) * (1.0f / acc_sen[i]) * 4.0f;
+ gyr_g.Y = -(!isLeft ? -1 : 1) * (gyr_r[i] - activeData[1]) * (816.0f / gyr_sen[i]);
+ if (form.calibrate) {
+ form.yA.Add(acc_r[i]);
+ form.yG.Add(gyr_r[i]);
+ }
+ break;
+ case 2:
+ acc_g.Z = (!isLeft ? -1 : 1) * (acc_r[i] - activeData[5]) * (1.0f / acc_sen[i]) * 4.0f;
+ gyr_g.Z = -(!isLeft ? -1 : 1) * (gyr_r[i] - activeData[2]) * (816.0f / gyr_sen[i]);
+ if (form.calibrate) {
+ form.zA.Add(acc_r[i]);
+ form.zG.Add(gyr_r[i]);
+ }
+ break;
+ }
+ }
+ } else {
+ Int16[] offset;
+ if (isPro)
+ offset = pro_hor_offset;
+ else if (isLeft)
+ offset = left_hor_offset;
+ else
+ offset = right_hor_offset;
+
+ for (int i = 0; i < 3; ++i) {
+ switch (i) {
+ case 0:
+ acc_g.X = (acc_r[i] - offset[i]) * (1.0f / (acc_sensiti[i] - acc_neutral[i])) * 4.0f;
+ gyr_g.X = (gyr_r[i] - gyr_neutral[i]) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
+
+ break;
+ case 1:
+ 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]) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
+ break;
+ case 2:
+ 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]) * (816.0f / (gyr_sensiti[i] - gyr_neutral[i]));
+ break;
+ }
+ }
+ }
+
+
+ if (other == null && !isPro) { // single joycon mode; Z do not swap, rest do
+ if (isLeft) {
+ acc_g.X = -acc_g.X;
+ gyr_g.X = -gyr_g.X;
+ } else {
+ gyr_g.Y = -gyr_g.Y;
+ }
+
+ float temp = acc_g.X; acc_g.X = acc_g.Y; acc_g.Y = temp;
+ temp = gyr_g.X; gyr_g.X = gyr_g.Y; gyr_g.Y = temp;
+ }
+ }
+ }
+
+ public void Begin() {
+ if (PollThreadObj == null) {
+ PollThreadObj = new Thread(new ThreadStart(Poll));
+ PollThreadObj.IsBackground = true;
+ PollThreadObj.Start();
+
+ form.AppendTextBox("Starting poll thread.\r\n");
+ } else {
+ form.AppendTextBox("Poll cannot start.\r\n");
+ }
+ }
+
+ public void Recenter() {
+ first_imu_packet = true;
+ }
+
+ // Should really be called calculating stick data
+ private float[] CenterSticks(UInt16[] vals, ushort[] cal, ushort dz) {
+ ushort[] t = cal;
+
+ float[] s = { 0, 0 };
+ float dx = vals[0] - t[2], dy = vals[1] - t[3];
+ if (Math.Abs(dx * dx + dy * dy) < dz * dz)
+ return s;
+
+ s[0] = dx / (dx > 0 ? t[0] : t[4]);
+ s[1] = dy / (dy > 0 ? t[1] : t[5]);
+ return s;
+ }
+
+ private static short CastStickValue(float stick_value) {
+ return (short)Math.Max(Int16.MinValue, Math.Min(Int16.MaxValue, stick_value * (stick_value > 0 ? Int16.MaxValue : -Int16.MinValue)));
+ }
+
+ private static byte CastStickValueByte(float stick_value) {
+ return (byte)Math.Max(Byte.MinValue, Math.Min(Byte.MaxValue, 127 - stick_value * Byte.MaxValue));
+ }
+
+ public void SetRumble(float low_freq, float high_freq, float amp, int time = 0) {
+ if (state <= Joycon.state_.ATTACHED) return;
+ rumble_obj.set_vals(low_freq, high_freq, amp, time);
+ }
+
+ private void SendRumble(byte[] buf) {
+ byte[] buf_ = new byte[report_len];
+ buf_[0] = 0x10;
+ buf_[1] = global_count;
+ if (global_count == 0xf) global_count = 0;
+ else ++global_count;
+ Array.Copy(buf, 0, buf_, 2, 8);
+ PrintArray(buf_, DebugType.RUMBLE, format: "Rumble data sent: {0:S}");
+ HIDapi.hid_write(handle, buf_, new UIntPtr(report_len));
+ }
+
+ private byte[] Subcommand(byte sc, byte[] buf, uint len, bool print = true) {
+ byte[] buf_ = new byte[report_len];
+ byte[] response = new byte[report_len];
+ Array.Copy(default_buf, 0, buf_, 2, 8);
+ Array.Copy(buf, 0, buf_, 11, len);
+ buf_[10] = sc;
+ buf_[1] = global_count;
+ buf_[0] = 0x1;
+ if (global_count == 0xf) global_count = 0;
+ else ++global_count;
+ if (print) { PrintArray(buf_, DebugType.COMMS, len, 11, "Subcommand 0x" + string.Format("{0:X2}", sc) + " sent. Data: 0x{0:S}"); };
+ HIDapi.hid_write(handle, buf_, new UIntPtr(len + 11));
+ int res = HIDapi.hid_read_timeout(handle, response, new UIntPtr(report_len), 50);
+ 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}"); }
+ return response;
+ }
+
+ private void dump_calibration_data() {
+ if (!isSnes) {
+ byte[] buf_ = ReadSPI(0x80, (isLeft ? (byte)0x12 : (byte)0x1d), 9); // get user calibration data if possible
+ bool found = false;
+ for (int i = 0; i < 9; ++i) {
+ if (buf_[i] != 0xff) {
+ form.AppendTextBox("Using user stick calibration data.\r\n");
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ form.AppendTextBox("Using factory stick calibration data.\r\n");
+ buf_ = ReadSPI(0x60, (isLeft ? (byte)0x3d : (byte)0x46), 9); // get user calibration data if possible
+ }
+ stick_cal[isLeft ? 0 : 2] = (UInt16)((buf_[1] << 8) & 0xF00 | buf_[0]); // X Axis Max above center
+ stick_cal[isLeft ? 1 : 3] = (UInt16)((buf_[2] << 4) | (buf_[1] >> 4)); // Y Axis Max above center
+ stick_cal[isLeft ? 2 : 4] = (UInt16)((buf_[4] << 8) & 0xF00 | buf_[3]); // X Axis Center
+ stick_cal[isLeft ? 3 : 5] = (UInt16)((buf_[5] << 4) | (buf_[4] >> 4)); // Y Axis Center
+ stick_cal[isLeft ? 4 : 0] = (UInt16)((buf_[7] << 8) & 0xF00 | buf_[6]); // X Axis Min below center
+ stick_cal[isLeft ? 5 : 1] = (UInt16)((buf_[8] << 4) | (buf_[7] >> 4)); // Y Axis Min below center
+
+ PrintArray(stick_cal, len: 6, start: 0, format: "Stick calibration data: {0:S}");
+
+ if (isPro) {
+ buf_ = ReadSPI(0x80, (!isLeft ? (byte)0x12 : (byte)0x1d), 9); // get user calibration data if possible
+ found = false;
+ for (int i = 0; i < 9; ++i) {
+ if (buf_[i] != 0xff) {
+ form.AppendTextBox("Using user stick calibration data.\r\n");
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ form.AppendTextBox("Using factory stick calibration data.\r\n");
+ buf_ = ReadSPI(0x60, (!isLeft ? (byte)0x3d : (byte)0x46), 9); // get user calibration data if possible
+ }
+ stick2_cal[!isLeft ? 0 : 2] = (UInt16)((buf_[1] << 8) & 0xF00 | buf_[0]); // X Axis Max above center
+ stick2_cal[!isLeft ? 1 : 3] = (UInt16)((buf_[2] << 4) | (buf_[1] >> 4)); // Y Axis Max above center
+ stick2_cal[!isLeft ? 2 : 4] = (UInt16)((buf_[4] << 8) & 0xF00 | buf_[3]); // X Axis Center
+ stick2_cal[!isLeft ? 3 : 5] = (UInt16)((buf_[5] << 4) | (buf_[4] >> 4)); // Y Axis Center
+ stick2_cal[!isLeft ? 4 : 0] = (UInt16)((buf_[7] << 8) & 0xF00 | buf_[6]); // X Axis Min below center
+ stick2_cal[!isLeft ? 5 : 1] = (UInt16)((buf_[8] << 4) | (buf_[7] >> 4)); // Y Axis Min below center
+
+ PrintArray(stick2_cal, len: 6, start: 0, format: "Stick calibration data: {0:S}");
+
+ buf_ = ReadSPI(0x60, (!isLeft ? (byte)0x86 : (byte)0x98), 16);
+ deadzone2 = (UInt16)((buf_[4] << 8) & 0xF00 | buf_[3]);
+ }
+
+ buf_ = ReadSPI(0x60, (isLeft ? (byte)0x86 : (byte)0x98), 16);
+ deadzone = (UInt16)((buf_[4] << 8) & 0xF00 | buf_[3]);
+
+ buf_ = ReadSPI(0x80, 0x28, 10);
+ acc_neutral[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
+ acc_neutral[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
+ acc_neutral[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
+
+ buf_ = ReadSPI(0x80, 0x2E, 10);
+ acc_sensiti[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
+ acc_sensiti[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
+ acc_sensiti[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
+
+ buf_ = ReadSPI(0x80, 0x34, 10);
+ gyr_neutral[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
+ gyr_neutral[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
+ gyr_neutral[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
+
+ buf_ = ReadSPI(0x80, 0x3A, 10);
+ gyr_sensiti[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
+ gyr_sensiti[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
+ gyr_sensiti[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
+
+ PrintArray(gyr_neutral, len: 3, d: DebugType.IMU, format: "User gyro neutral position: {0:S}");
+
+ // This is an extremely messy way of checking to see whether there is user stick calibration data present, but I've seen conflicting user calibration data on blank Joy-Cons. Worth another look eventually.
+ if (gyr_neutral[0] + gyr_neutral[1] + gyr_neutral[2] == -3 || Math.Abs(gyr_neutral[0]) > 100 || Math.Abs(gyr_neutral[1]) > 100 || Math.Abs(gyr_neutral[2]) > 100) {
+ buf_ = ReadSPI(0x60, 0x20, 10);
+ acc_neutral[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
+ acc_neutral[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
+ acc_neutral[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
+
+ buf_ = ReadSPI(0x60, 0x26, 10);
+ acc_sensiti[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
+ acc_sensiti[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
+ acc_sensiti[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
+
+ buf_ = ReadSPI(0x60, 0x2C, 10);
+ gyr_neutral[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
+ gyr_neutral[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
+ gyr_neutral[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
+
+ buf_ = ReadSPI(0x60, 0x32, 10);
+ gyr_sensiti[0] = (Int16)(buf_[0] | ((buf_[1] << 8) & 0xff00));
+ gyr_sensiti[1] = (Int16)(buf_[2] | ((buf_[3] << 8) & 0xff00));
+ gyr_sensiti[2] = (Int16)(buf_[4] | ((buf_[5] << 8) & 0xff00));
+
+ PrintArray(gyr_neutral, len: 3, d: DebugType.IMU, format: "Factory gyro neutral position: {0:S}");
+ }
+ }
+ }
+
+ private byte[] ReadSPI(byte addr1, byte addr2, uint len, bool print = false) {
+ byte[] buf = { addr2, addr1, 0x00, 0x00, (byte)len };
+ byte[] read_buf = new byte[len];
+ byte[] buf_ = new byte[len + 20];
+
+ for (int i = 0; i < 100; ++i) {
+ buf_ = Subcommand(0x10, buf, 5, false);
+ if (buf_[15] == addr2 && buf_[16] == addr1) {
+ break;
+ }
+ }
+ Array.Copy(buf_, 20, read_buf, 0, len);
+ if (print) PrintArray(read_buf, DebugType.COMMS, len);
+ return read_buf;
+ }
+
+ private void PrintArray(T[] arr, DebugType d = DebugType.NONE, uint len = 0, uint start = 0, string format = "{0:S}") {
+ if (d != debug_type && debug_type != DebugType.ALL) return;
+ if (len == 0) len = (uint)arr.Length;
+ string tostr = "";
+ for (int i = 0; i < len; ++i) {
+ tostr += string.Format((arr[0] is byte) ? "{0:X2} " : ((arr[0] is float) ? "{0:F} " : "{0:D} "), arr[i + start]);
+ }
+ DebugPrint(string.Format(format, tostr), d);
+ }
+
+ private static OutputControllerXbox360InputState MapToXbox360Input(Joycon input) {
+ var output = new OutputControllerXbox360InputState();
+
+ var swapAB = input.swapAB;
+ var swapXY = input.swapXY;
+
+ var isPro = input.isPro;
+ var isLeft = input.isLeft;
+ var isSnes = input.isSnes;
+ var other = input.other;
+ var GyroAnalogSliders = input.GyroAnalogSliders;
+
+ var buttons = input.buttons;
+ var stick = input.stick;
+ var stick2 = input.stick2;
+ var sliderVal = input.sliderVal;
+
+ if (isPro) {
+ output.a = buttons[(int)(!swapAB ? Button.B : Button.A)];
+ output.b = buttons[(int)(!swapAB ? Button.A : Button.B)];
+ output.y = buttons[(int)(!swapXY ? Button.X : Button.Y)];
+ output.x = buttons[(int)(!swapXY ? Button.Y : Button.X)];
+
+ output.dpad_up = buttons[(int)Button.DPAD_UP];
+ output.dpad_down = buttons[(int)Button.DPAD_DOWN];
+ output.dpad_left = buttons[(int)Button.DPAD_LEFT];
+ output.dpad_right = buttons[(int)Button.DPAD_RIGHT];
+
+ output.back = buttons[(int)Button.MINUS];
+ output.start = buttons[(int)Button.PLUS];
+ output.guide = buttons[(int)Button.HOME];
+
+ output.shoulder_left = buttons[(int)Button.SHOULDER_1];
+ output.shoulder_right = buttons[(int)Button.SHOULDER2_1];
+
+ output.thumb_stick_left = buttons[(int)Button.STICK];
+ output.thumb_stick_right = buttons[(int)Button.STICK2];
+ } else {
+ if (other != null) { // no need for && other != this
+ output.a = buttons[(int)(!swapAB ? isLeft ? Button.B : Button.DPAD_DOWN : isLeft ? Button.A : Button.DPAD_RIGHT)];
+ output.b = buttons[(int)(swapAB ? isLeft ? Button.B : Button.DPAD_DOWN : isLeft ? Button.A : Button.DPAD_RIGHT)];
+ output.y = buttons[(int)(!swapXY ? isLeft ? Button.X : Button.DPAD_UP : isLeft ? Button.Y : Button.DPAD_LEFT)];
+ output.x = buttons[(int)(swapXY ? isLeft ? Button.X : Button.DPAD_UP : isLeft ? Button.Y : Button.DPAD_LEFT)];
+
+ output.dpad_up = buttons[(int)(isLeft ? Button.DPAD_UP : Button.X)];
+ output.dpad_down = buttons[(int)(isLeft ? Button.DPAD_DOWN : Button.B)];
+ output.dpad_left = buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.Y)];
+ output.dpad_right = buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.A)];
+
+ output.back = buttons[(int)Button.MINUS];
+ output.start = buttons[(int)Button.PLUS];
+ output.guide = buttons[(int)Button.HOME];
+
+ output.shoulder_left = buttons[(int)(isLeft ? Button.SHOULDER_1 : Button.SHOULDER2_1)];
+ output.shoulder_right = buttons[(int)(isLeft ? Button.SHOULDER2_1 : Button.SHOULDER_1)];
+
+ output.thumb_stick_left = buttons[(int)(isLeft ? Button.STICK : Button.STICK2)];
+ output.thumb_stick_right = buttons[(int)(isLeft ? Button.STICK2 : Button.STICK)];
+ } else { // single joycon mode
+ output.a = buttons[(int)(!swapAB ? isLeft ? Button.DPAD_LEFT : Button.DPAD_RIGHT : isLeft ? Button.DPAD_DOWN : Button.DPAD_UP)];
+ output.b = buttons[(int)(swapAB ? isLeft ? Button.DPAD_LEFT : Button.DPAD_RIGHT : isLeft ? Button.DPAD_DOWN : Button.DPAD_UP)];
+ output.y = buttons[(int)(!swapXY ? isLeft ? Button.DPAD_RIGHT : Button.DPAD_LEFT : isLeft ? Button.DPAD_UP : Button.DPAD_DOWN)];
+ output.x = buttons[(int)(swapXY ? isLeft ? Button.DPAD_RIGHT : Button.DPAD_LEFT : isLeft ? Button.DPAD_UP : Button.DPAD_DOWN)];
+
+ output.back = buttons[(int)Button.MINUS] | buttons[(int)Button.HOME];
+ output.start = buttons[(int)Button.PLUS] | buttons[(int)Button.CAPTURE];
+
+ output.shoulder_left = buttons[(int)Button.SL];
+ output.shoulder_right = buttons[(int)Button.SR];
+
+ output.thumb_stick_left = buttons[(int)Button.STICK];
+ }
+ }
+
+ // overwrite guide button if it's custom-mapped
+ if (Config.Value("home") != "0")
+ output.guide = false;
+
+ if (!isSnes) {
+ 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]);
+
+ output.axis_right_x = CastStickValue((other == input && !isLeft) ? stick[0] : stick2[0]);
+ output.axis_right_y = CastStickValue((other == input && !isLeft) ? stick[1] : stick2[1]);
+ } else { // single joycon mode
+ output.axis_left_y = CastStickValue((isLeft ? 1 : -1) * stick[0]);
+ output.axis_left_x = CastStickValue((isLeft ? -1 : 1) * stick[1]);
+ }
+ }
+
+ if (other != null || isPro) {
+ byte lval = GyroAnalogSliders ? sliderVal[0] : Byte.MaxValue;
+ byte rval = GyroAnalogSliders ? sliderVal[1] : Byte.MaxValue;
+ output.trigger_left = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_2 : Button.SHOULDER2_2)] ? lval : 0);
+ output.trigger_right = (byte)(buttons[(int)(isLeft ? Button.SHOULDER2_2 : Button.SHOULDER_2)] ? rval : 0);
+ } else {
+ output.trigger_left = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_2 : Button.SHOULDER_1)] ? Byte.MaxValue : 0);
+ output.trigger_right = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_1 : Button.SHOULDER_2)] ? Byte.MaxValue : 0);
+ }
+
+ return output;
+ }
+
+ private static OutputControllerDualShock4InputState MapToDualShock4Input(Joycon input) {
+ var output = new OutputControllerDualShock4InputState();
+
+ var swapAB = input.swapAB;
+ var swapXY = input.swapXY;
+
+ var isPro = input.isPro;
+ var isLeft = input.isLeft;
+ var isSnes = input.isSnes;
+ var other = input.other;
+ var GyroAnalogSliders = input.GyroAnalogSliders;
+
+ var buttons = input.buttons;
+ var stick = input.stick;
+ var stick2 = input.stick2;
+ var sliderVal = input.sliderVal;
+
+ if (isPro) {
+ output.cross = buttons[(int)(!swapAB ? Button.B : Button.A)];
+ output.circle = buttons[(int)(!swapAB ? Button.A : Button.B)];
+ output.triangle = buttons[(int)(!swapXY ? Button.X : Button.Y)];
+ output.square = buttons[(int)(!swapXY ? Button.Y : Button.X)];
+
+
+ if (buttons[(int)Button.DPAD_UP]) {
+ if (buttons[(int)Button.DPAD_LEFT])
+ output.dPad = DpadDirection.Northwest;
+ else if (buttons[(int)Button.DPAD_RIGHT])
+ output.dPad = DpadDirection.Northeast;
+ else
+ output.dPad = DpadDirection.North;
+ } else if (buttons[(int)Button.DPAD_DOWN]) {
+ if (buttons[(int)Button.DPAD_LEFT])
+ output.dPad = DpadDirection.Southwest;
+ else if (buttons[(int)Button.DPAD_RIGHT])
+ output.dPad = DpadDirection.Southeast;
+ else
+ output.dPad = DpadDirection.South;
+ } else if (buttons[(int)Button.DPAD_LEFT])
+ output.dPad = DpadDirection.West;
+ else if (buttons[(int)Button.DPAD_RIGHT])
+ output.dPad = DpadDirection.East;
+
+ output.share = buttons[(int)Button.MINUS];
+ output.options = buttons[(int)Button.PLUS];
+ output.ps = buttons[(int)Button.HOME];
+ output.touchpad = buttons[(int)Button.CAPTURE];
+ output.shoulder_left = buttons[(int)Button.SHOULDER_1];
+ output.shoulder_right = buttons[(int)Button.SHOULDER2_1];
+ output.thumb_left = buttons[(int)Button.STICK];
+ output.thumb_right = buttons[(int)Button.STICK2];
+ } else {
+ if (other != null) { // no need for && other != this
+ output.circle = !swapAB ? buttons[(int)(isLeft ? Button.B : Button.DPAD_DOWN)] : buttons[(int)(isLeft ? Button.A : Button.DPAD_RIGHT)];
+ output.cross = swapAB ? buttons[(int)(isLeft ? Button.B : Button.DPAD_DOWN)] : buttons[(int)(isLeft ? Button.A : Button.DPAD_RIGHT)];
+ output.triangle = !swapXY ? buttons[(int)(isLeft ? Button.X : Button.DPAD_UP)] : buttons[(int)(isLeft ? Button.Y : Button.DPAD_LEFT)];
+ output.triangle = swapXY ? buttons[(int)(isLeft ? Button.X : Button.DPAD_UP)] : buttons[(int)(isLeft ? Button.Y : Button.DPAD_LEFT)];
+
+ if (buttons[(int)(isLeft ? Button.DPAD_UP : Button.X)])
+ if (buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.Y)])
+ output.dPad = DpadDirection.Northwest;
+ else if (buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.A)])
+ output.dPad = DpadDirection.Northeast;
+ else
+ output.dPad = DpadDirection.North;
+ else if (buttons[(int)(isLeft ? Button.DPAD_DOWN : Button.B)])
+ if (buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.Y)])
+ output.dPad = DpadDirection.Southwest;
+ else if (buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.A)])
+ output.dPad = DpadDirection.Southeast;
+ else
+ output.dPad = DpadDirection.South;
+ else if (buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.Y)])
+ output.dPad = DpadDirection.West;
+ else if (buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.A)])
+ output.dPad = DpadDirection.East;
+
+ output.share = buttons[(int)Button.MINUS];
+ output.options = buttons[(int)Button.PLUS];
+ output.ps = buttons[(int)Button.HOME];
+ output.touchpad = buttons[(int)Button.CAPTURE];
+ output.shoulder_left = buttons[(int)(isLeft ? Button.SHOULDER_1 : Button.SHOULDER2_1)];
+ output.shoulder_right = buttons[(int)(isLeft ? Button.SHOULDER2_1 : Button.SHOULDER_1)];
+ output.thumb_left = buttons[(int)(isLeft ? Button.STICK : Button.STICK2)];
+ output.thumb_right = buttons[(int)(isLeft ? Button.STICK2 : Button.STICK)];
+ } else { // single joycon mode
+ output.cross = !swapAB ? buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.DPAD_RIGHT)] : buttons[(int)(isLeft ? Button.DPAD_DOWN : Button.DPAD_UP)];
+ output.circle = swapAB ? buttons[(int)(isLeft ? Button.DPAD_LEFT : Button.DPAD_RIGHT)] : buttons[(int)(isLeft ? Button.DPAD_DOWN : Button.DPAD_UP)];
+ output.triangle = !swapXY ? buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.DPAD_LEFT)] : buttons[(int)(isLeft ? Button.DPAD_UP : Button.DPAD_DOWN)];
+ output.square = swapXY ? buttons[(int)(isLeft ? Button.DPAD_RIGHT : Button.DPAD_LEFT)] : buttons[(int)(isLeft ? Button.DPAD_UP : Button.DPAD_DOWN)];
+
+ output.square = buttons[(int)Button.MINUS] | buttons[(int)Button.HOME];
+ output.options = buttons[(int)Button.PLUS] | buttons[(int)Button.CAPTURE];
+
+ output.shoulder_left = buttons[(int)Button.SL];
+ output.shoulder_right = buttons[(int)Button.SR];
+
+ output.thumb_left = buttons[(int)Button.STICK];
+ }
+ }
+
+ // overwrite guide button if it's custom-mapped
+ if (Config.Value("home") != "0")
+ output.ps = false;
+
+ if (!isSnes) {
+ 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]);
+ output.thumb_right_x = CastStickValueByte((other == input && !isLeft) ? -stick[0] : -stick2[0]);
+ output.thumb_right_y = CastStickValueByte((other == input && !isLeft) ? stick[1] : stick2[1]);
+ } else { // single joycon mode
+ output.thumb_left_y = CastStickValueByte((isLeft ? 1 : -1) * stick[0]);
+ output.thumb_left_x = CastStickValueByte((isLeft ? 1 : -1) * stick[1]);
+ }
+ }
+
+ if (other != null || isPro) {
+ byte lval = GyroAnalogSliders ? sliderVal[0] : Byte.MaxValue;
+ byte rval = GyroAnalogSliders ? sliderVal[1] : Byte.MaxValue;
+ output.trigger_left_value = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_2 : Button.SHOULDER2_2)] ? lval : 0);
+ output.trigger_right_value = (byte)(buttons[(int)(isLeft ? Button.SHOULDER2_2 : Button.SHOULDER_2)] ? rval : 0);
+ } else {
+ output.trigger_left_value = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_2 : Button.SHOULDER_1)] ? Byte.MaxValue : 0);
+ output.trigger_right_value = (byte)(buttons[(int)(isLeft ? Button.SHOULDER_1 : Button.SHOULDER_2)] ? Byte.MaxValue : 0);
+ }
+
+ return output;
+ }
+ }
}
diff --git a/BetterJoyForCemu/MainForm.cs b/BetterJoyForCemu/MainForm.cs
index acbf311..a8975ef 100644
--- a/BetterJoyForCemu/MainForm.cs
+++ b/BetterJoyForCemu/MainForm.cs
@@ -171,12 +171,12 @@ namespace BetterJoyForCemu {
v.other = jc;
jc.other = v;
- //Set both Joycon LEDs to the one with the lowest ID
- byte led = jc.LED <= v.LED ? jc.LED : v.LED;
- jc.LED = led;
- v.LED = led;
- jc.SetPlayerLED(led);
- v.SetPlayerLED(led);
+ //Set both Joycon LEDs to the one with the lowest ID
+ byte led = jc.LED <= v.LED ? jc.LED : v.LED;
+ jc.LED = led;
+ v.LED = led;
+ jc.SetPlayerLED(led);
+ v.SetPlayerLED(led);
if (v.out_xbox != null) {
v.out_xbox.Disconnect();
diff --git a/BetterJoyForCemu/Program.cs b/BetterJoyForCemu/Program.cs
index 5de6a5d..8771cbf 100644
--- a/BetterJoyForCemu/Program.cs
+++ b/BetterJoyForCemu/Program.cs
@@ -17,522 +17,522 @@ using Nefarius.ViGEm.Client;
using static BetterJoyForCemu.HIDapi;
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;
- private const ushort product_snes = 0x2017;
-
- public List j; // Array of all connected Joy-Cons
- static JoyconManager instance;
-
- public MainForm form;
-
- System.Timers.Timer controllerCheck;
-
- public static JoyconManager Instance {
- get { return instance; }
- }
-
- public void Awake() {
- instance = this;
- j = new List();
- HIDapi.hid_init();
- }
-
- public void Start() {
- controllerCheck = new System.Timers.Timer(2000); // check for new controllers every 2 seconds
- controllerCheck.Elapsed += CheckForNewControllersTime;
- controllerCheck.Start();
- }
-
- bool ControllerAlreadyAdded(string path) {
- foreach (Joycon v in j)
- if (v.path == path)
- return true;
- return false;
- }
-
- void CleanUp() { // removes dropped controllers from list
- List rem = new List();
- for (int i = 0; i < j.Count; i++) {
- Joycon v = j[i];
- if (v.state == Joycon.state_.DROPPED) {
- if (v.other != null)
- v.other.other = null; // The other of the other is the joycon itself
-
- v.Detach(true); rem.Add(v);
-
- foreach (Button b in form.con) {
- if (b.Enabled & b.Tag == v) {
- b.Invoke(new MethodInvoker(delegate {
- b.BackColor = System.Drawing.Color.FromArgb(0x00, System.Drawing.SystemColors.Control);
- b.Enabled = false;
- b.BackgroundImage = Properties.Resources.cross;
- }));
- break;
- }
- }
-
- form.AppendTextBox("Removed dropped controller. Can be reconnected.\r\n");
- }
- }
-
- foreach (Joycon v in rem)
- j.Remove(v);
- }
-
- void CheckForNewControllersTime(Object source, ElapsedEventArgs e) {
- if (Config.IntValue("ProgressiveScan") == 1) {
- CheckForNewControllers();
- }
- }
-
- public void CheckForNewControllers() {
- CleanUp();
-
- // move all code for initializing devices here and well as the initial code from Start()
- bool isLeft = false;
- IntPtr ptr = HIDapi.hid_enumerate(vendor_id, 0x0);
- IntPtr top_ptr = ptr;
-
- hid_device_info enumerate; // Add device to list
- bool foundNew = false;
- while (ptr != IntPtr.Zero) {
- enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info));
-
- if (enumerate.serial_number == null) {
- ptr = enumerate.next; // can't believe it took me this long to figure out why USB connections used up so much CPU.
- // it was getting stuck in an inf loop here!
- continue;
- }
-
- if (form.nonOriginal) {
- enumerate.product_id = product_pro;
- }
-
- bool validController = (enumerate.product_id == product_l || enumerate.product_id == product_r ||
- enumerate.product_id == product_pro || enumerate.product_id == product_snes);
- if (validController && !ControllerAlreadyAdded(enumerate.path)) {
- switch (enumerate.product_id) {
- case product_l:
- isLeft = true;
- form.AppendTextBox("Left Joy-Con connected.\r\n"); break;
- case product_r:
- isLeft = false;
- form.AppendTextBox("Right Joy-Con connected.\r\n"); break;
- case product_pro:
- isLeft = true;
- form.AppendTextBox("Pro controller connected.\r\n"); break;
- case product_snes:
- isLeft = true;
- form.AppendTextBox("SNES controller connected.\r\n"); break;
- default:
- form.AppendTextBox("Non Joy-Con Nintendo input device skipped.\r\n"); break;
- }
-
- // Add controller to block-list for HidGuardian
- if (Program.useHIDG) {
- 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 {
- form.AppendTextBox("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 {
- form.AppendTextBox("Unable to open path to device - are you using the correct (64 vs 32-bit) version for your PC?\r\n");
- break;
- }
-
- bool isPro = enumerate.product_id == product_pro;
- bool isSnes = enumerate.product_id == product_snes;
- j.Add(new Joycon(handle, EnableIMU, EnableLocalize & EnableIMU, 0.05f, isLeft, enumerate.path, enumerate.serial_number, j.Count, isPro, isSnes));
-
- foundNew = true;
- j.Last().form = form;
-
- if (j.Count < 5) {
- int ii = -1;
- foreach (Button v in form.con) {
- ii++;
- if (!v.Enabled) {
- System.Drawing.Bitmap temp;
- switch (enumerate.product_id) {
- case (product_l):
- temp = Properties.Resources.jc_left_s; break;
- case (product_r):
- temp = Properties.Resources.jc_right_s; break;
- case (product_pro):
- temp = Properties.Resources.pro; break;
- case (product_snes):
- temp = Properties.Resources.snes; break;
- default:
- temp = Properties.Resources.cross; break;
- }
-
- v.Invoke(new MethodInvoker(delegate {
- v.Tag = j.Last(); // assign controller to button
- v.Enabled = true;
- v.Click += new EventHandler(form.conBtnClick);
- v.BackgroundImage = temp;
- }));
-
- form.loc[ii].Invoke(new MethodInvoker(delegate {
- form.loc[ii].Tag = v;
- form.loc[ii].Click += new EventHandler(form.locBtnClick);
- }));
-
- break;
- }
- }
- }
-
- 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);
- }
-
- ptr = enumerate.next;
- }
-
- if (foundNew) { // attempt to auto join-up joycons on connection
- Joycon temp = null;
- foreach (Joycon v in j) {
- if (!v.isPro) {
- if (temp == null)
- temp = v;
- else if (temp.isLeft != v.isLeft && v.other == null) {
- temp.other = v;
- v.other = temp;
-
- //Set both Joycon LEDs to the one with the lowest ID
- byte led = temp.LED <= v.LED ? temp.LED : v.LED;
- temp.LED = led;
- v.LED = led;
- temp.SetPlayerLED(led);
- v.SetPlayerLED(led);
-
- if (temp.out_xbox != null) {
- try {
- temp.out_xbox.Disconnect();
- } catch (Exception e) {
- // it wasn't connected in the first place, go figure
- }
- }
- if (temp.out_ds4 != null) {
- try {
- temp.out_ds4.Disconnect();
- } catch (Exception e) {
- // it wasn't connected in the first place, go figure
- }
- }
- temp.out_xbox = null;
- temp.out_ds4 = null;
-
- foreach (Button b in form.con)
- if (b.Tag == v || b.Tag == temp) {
- Joycon tt = (b.Tag == v) ? v : (b.Tag == temp) ? temp : v;
- b.BackgroundImage = tt.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
- }
-
- temp = null; // repeat
- }
- }
- }
- }
-
- HIDapi.hid_free_enumeration(top_ptr);
-
- 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();
-
- jc.Attach(leds_: jc.LED);
-
- bool on = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings.Settings["HomeLEDOn"].Value.ToLower() == "true";
- foreach (Joycon j in Program.mgr.j) {
- j.SetHomeLight(on);
- }
-
- jc.Begin();
- if (form.nonOriginal) {
- jc.getActiveData();
- }
-
- }
- }
- }
-
- public void Update() {
- for (int i = 0; i < j.Count; ++i)
- j[i].Update();
- }
-
- public void OnApplicationQuit() {
- foreach (Joycon v in j) {
- if (Boolean.Parse(ConfigurationManager.AppSettings["AutoPowerOff"]))
- v.PowerOff();
- else
- v.Detach();
-
- if (v.out_xbox != null) {
- v.out_xbox.Disconnect();
- }
-
- if (v.out_ds4 != null) {
- v.out_ds4.Disconnect();
- }
- }
-
- controllerCheck.Stop();
- HIDapi.hid_exit();
- }
- }
-
- // 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.IsBackground = true;
- 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();
-
- public static JoyconManager mgr;
- static HighResTimer timer;
- static string pid;
-
- static MainForm form;
-
- static public bool useHIDG = Boolean.Parse(ConfigurationManager.AppSettings["UseHIDG"]);
-
- private static WindowsInput.Events.Sources.IKeyboardEventSource keyboard;
- private static WindowsInput.Events.Sources.IMouseEventSource mouse;
-
- public static void Start() {
- pid = Process.GetCurrentProcess().Id.ToString(); // get current process id for HidCerberus.Srv
-
- if (useHIDG) {
- form.console.AppendText("HidGuardian is enabled.\r\n");
- try {
- var HidCerberusService = new ServiceController("HidCerberus Service");
- if (HidCerberusService.Status == ServiceControllerStatus.Stopped) {
- form.console.AppendText("HidGuardian was stopped. Starting...\r\n");
-
- try {
- HidCerberusService.Start();
- } catch (Exception e) {
- form.console.AppendText("Unable to start HidGuardian - everything should work fine without it, but if you need it, run the app again as an admin.\r\n");
- useHIDG = false;
- }
- }
- } catch (Exception e) {
- form.console.AppendText("Unable to start HidGuardian - everything should work fine without it, but if you need it, install it properly as admin.\r\n");
- useHIDG = false;
- }
-
- HttpWebResponse response;
- if (Boolean.Parse(ConfigurationManager.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.AppendText("Unable to purge whitelist.\r\n");
- useHIDG = false;
- }
- }
-
- 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.AppendText("Unable to add program to whitelist.\r\n");
- useHIDG = false;
- }
- }
-
- if (Boolean.Parse(ConfigurationManager.AppSettings["ShowAsXInput"]) || Boolean.Parse(ConfigurationManager.AppSettings["ShowAsDS4"])) {
- try {
- emClient = new ViGEmClient(); // Manages emulated XInput
- } catch (Nefarius.ViGEm.Client.Exceptions.VigemBusNotFoundException) {
- form.console.AppendText("Could not start VigemBus. Make sure drivers are installed correctly.\r\n");
- }
- }
-
- 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.CheckForNewControllers();
- mgr.Start();
-
- server = new UdpServer(mgr.j);
- server.form = form;
-
- server.Start(IPAddress.Parse(ConfigurationManager.AppSettings["IP"]), Int32.Parse(ConfigurationManager.AppSettings["Port"]));
- timer = new HighResTimer(pollsPerSecond, new HighResTimer.ActionDelegate(mgr.Update));
- timer.Start();
-
- // Capture keyboard + mouse events for binding's sake
- keyboard = WindowsInput.Capture.Global.KeyboardAsync();
- keyboard.KeyEvent += Keyboard_KeyEvent;
- mouse = WindowsInput.Capture.Global.MouseAsync();
- mouse.MouseEvent += Mouse_MouseEvent;
-
- form.console.AppendText("All systems go\r\n");
- }
-
- private static void Mouse_MouseEvent(object sender, WindowsInput.Events.Sources.EventSourceEventArgs e) {
- if (e.Data.ButtonDown != null) {
- string res_val = Config.Value("reset_mouse");
- if (res_val.StartsWith("mse_"))
- if ((int)e.Data.ButtonDown.Button == Int32.Parse(res_val.Substring(4)))
- WindowsInput.Simulate.Events().MoveTo(Screen.PrimaryScreen.Bounds.Width / 2, Screen.PrimaryScreen.Bounds.Height / 2).Invoke();
-
- res_val = Config.Value("active_gyro");
- if (res_val.StartsWith("mse_"))
- if ((int)e.Data.ButtonDown.Button == Int32.Parse(res_val.Substring(4)))
- foreach (var i in mgr.j)
- i.active_gyro = true;
- }
-
- if (e.Data.ButtonUp != null) {
- string res_val = Config.Value("active_gyro");
- if (res_val.StartsWith("mse_"))
- if ((int)e.Data.ButtonUp.Button == Int32.Parse(res_val.Substring(4)))
- foreach (var i in mgr.j)
- i.active_gyro = false;
- }
- }
-
- private static void Keyboard_KeyEvent(object sender, WindowsInput.Events.Sources.EventSourceEventArgs e) {
- if (e.Data.KeyDown != null) {
- string res_val = Config.Value("reset_mouse");
- if (res_val.StartsWith("key_"))
- if ((int)e.Data.KeyDown.Key == Int32.Parse(res_val.Substring(4)))
- WindowsInput.Simulate.Events().MoveTo(Screen.PrimaryScreen.Bounds.Width / 2, Screen.PrimaryScreen.Bounds.Height / 2).Invoke();
-
- res_val = Config.Value("active_gyro");
- if (res_val.StartsWith("key_"))
- if ((int)e.Data.KeyDown.Key == Int32.Parse(res_val.Substring(4)))
- foreach (var i in mgr.j)
- i.active_gyro = true;
- }
-
- if (e.Data.KeyUp != null) {
- string res_val = Config.Value("active_gyro");
- if (res_val.StartsWith("key_"))
- if ((int)e.Data.KeyUp.Key == Int32.Parse(res_val.Substring(4)))
- foreach (var i in mgr.j)
- i.active_gyro = false;
- }
- }
-
- public static void Stop() {
- if (Program.useHIDG) {
- try {
- HttpWebResponse response = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/whitelist/remove/" + pid).GetResponse();
- } catch (Exception e) {
- form.console.AppendText("Unable to remove program from whitelist.\r\n");
- }
- }
-
- if (Boolean.Parse(ConfigurationManager.AppSettings["PurgeAffectedDevices"]) && Program.useHIDG) {
- try {
- HttpWebResponse r1 = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/affected/purge/").GetResponse();
- } catch { }
- }
-
- keyboard.Dispose(); mouse.Dispose();
- server.Stop();
- timer.Stop();
- mgr.OnApplicationQuit();
- }
-
- private static string appGuid = "04450797-3520-462e-a563-107677a483d8"; // randomly-generated
- static void Main(string[] args) {
- using (Mutex mutex = new Mutex(false, "Global\\" + appGuid)) {
- if (!mutex.WaitOne(0, false)) {
- MessageBox.Show("Instance already running.", "BetterJoy");
- return;
- }
-
- Application.EnableVisualStyles();
- Application.SetCompatibleTextRenderingDefault(false);
- form = new MainForm();
- Application.Run(form);
- }
- }
- }
+ 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;
+ private const ushort product_snes = 0x2017;
+
+ public List j; // Array of all connected Joy-Cons
+ static JoyconManager instance;
+
+ public MainForm form;
+
+ System.Timers.Timer controllerCheck;
+
+ public static JoyconManager Instance {
+ get { return instance; }
+ }
+
+ public void Awake() {
+ instance = this;
+ j = new List();
+ HIDapi.hid_init();
+ }
+
+ public void Start() {
+ controllerCheck = new System.Timers.Timer(2000); // check for new controllers every 2 seconds
+ controllerCheck.Elapsed += CheckForNewControllersTime;
+ controllerCheck.Start();
+ }
+
+ bool ControllerAlreadyAdded(string path) {
+ foreach (Joycon v in j)
+ if (v.path == path)
+ return true;
+ return false;
+ }
+
+ void CleanUp() { // removes dropped controllers from list
+ List rem = new List();
+ for (int i = 0; i < j.Count; i++) {
+ Joycon v = j[i];
+ if (v.state == Joycon.state_.DROPPED) {
+ if (v.other != null)
+ v.other.other = null; // The other of the other is the joycon itself
+
+ v.Detach(true); rem.Add(v);
+
+ foreach (Button b in form.con) {
+ if (b.Enabled & b.Tag == v) {
+ b.Invoke(new MethodInvoker(delegate {
+ b.BackColor = System.Drawing.Color.FromArgb(0x00, System.Drawing.SystemColors.Control);
+ b.Enabled = false;
+ b.BackgroundImage = Properties.Resources.cross;
+ }));
+ break;
+ }
+ }
+
+ form.AppendTextBox("Removed dropped controller. Can be reconnected.\r\n");
+ }
+ }
+
+ foreach (Joycon v in rem)
+ j.Remove(v);
+ }
+
+ void CheckForNewControllersTime(Object source, ElapsedEventArgs e) {
+ if (Config.IntValue("ProgressiveScan") == 1) {
+ CheckForNewControllers();
+ }
+ }
+
+ public void CheckForNewControllers() {
+ CleanUp();
+
+ // move all code for initializing devices here and well as the initial code from Start()
+ bool isLeft = false;
+ IntPtr ptr = HIDapi.hid_enumerate(vendor_id, 0x0);
+ IntPtr top_ptr = ptr;
+
+ hid_device_info enumerate; // Add device to list
+ bool foundNew = false;
+ while (ptr != IntPtr.Zero) {
+ enumerate = (hid_device_info)Marshal.PtrToStructure(ptr, typeof(hid_device_info));
+
+ if (enumerate.serial_number == null) {
+ ptr = enumerate.next; // can't believe it took me this long to figure out why USB connections used up so much CPU.
+ // it was getting stuck in an inf loop here!
+ continue;
+ }
+
+ if (form.nonOriginal) {
+ enumerate.product_id = product_pro;
+ }
+
+ bool validController = (enumerate.product_id == product_l || enumerate.product_id == product_r ||
+ enumerate.product_id == product_pro || enumerate.product_id == product_snes);
+ if (validController && !ControllerAlreadyAdded(enumerate.path)) {
+ switch (enumerate.product_id) {
+ case product_l:
+ isLeft = true;
+ form.AppendTextBox("Left Joy-Con connected.\r\n"); break;
+ case product_r:
+ isLeft = false;
+ form.AppendTextBox("Right Joy-Con connected.\r\n"); break;
+ case product_pro:
+ isLeft = true;
+ form.AppendTextBox("Pro controller connected.\r\n"); break;
+ case product_snes:
+ isLeft = true;
+ form.AppendTextBox("SNES controller connected.\r\n"); break;
+ default:
+ form.AppendTextBox("Non Joy-Con Nintendo input device skipped.\r\n"); break;
+ }
+
+ // Add controller to block-list for HidGuardian
+ if (Program.useHIDG) {
+ 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 {
+ form.AppendTextBox("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 {
+ form.AppendTextBox("Unable to open path to device - are you using the correct (64 vs 32-bit) version for your PC?\r\n");
+ break;
+ }
+
+ bool isPro = enumerate.product_id == product_pro;
+ bool isSnes = enumerate.product_id == product_snes;
+ j.Add(new Joycon(handle, EnableIMU, EnableLocalize & EnableIMU, 0.05f, isLeft, enumerate.path, enumerate.serial_number, j.Count, isPro, isSnes));
+
+ foundNew = true;
+ j.Last().form = form;
+
+ if (j.Count < 5) {
+ int ii = -1;
+ foreach (Button v in form.con) {
+ ii++;
+ if (!v.Enabled) {
+ System.Drawing.Bitmap temp;
+ switch (enumerate.product_id) {
+ case (product_l):
+ temp = Properties.Resources.jc_left_s; break;
+ case (product_r):
+ temp = Properties.Resources.jc_right_s; break;
+ case (product_pro):
+ temp = Properties.Resources.pro; break;
+ case (product_snes):
+ temp = Properties.Resources.snes; break;
+ default:
+ temp = Properties.Resources.cross; break;
+ }
+
+ v.Invoke(new MethodInvoker(delegate {
+ v.Tag = j.Last(); // assign controller to button
+ v.Enabled = true;
+ v.Click += new EventHandler(form.conBtnClick);
+ v.BackgroundImage = temp;
+ }));
+
+ form.loc[ii].Invoke(new MethodInvoker(delegate {
+ form.loc[ii].Tag = v;
+ form.loc[ii].Click += new EventHandler(form.locBtnClick);
+ }));
+
+ break;
+ }
+ }
+ }
+
+ 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);
+ }
+
+ ptr = enumerate.next;
+ }
+
+ if (foundNew) { // attempt to auto join-up joycons on connection
+ Joycon temp = null;
+ foreach (Joycon v in j) {
+ if (!v.isPro) {
+ if (temp == null)
+ temp = v;
+ else if (temp.isLeft != v.isLeft && v.other == null) {
+ temp.other = v;
+ v.other = temp;
+
+ //Set both Joycon LEDs to the one with the lowest ID
+ byte led = temp.LED <= v.LED ? temp.LED : v.LED;
+ temp.LED = led;
+ v.LED = led;
+ temp.SetPlayerLED(led);
+ v.SetPlayerLED(led);
+
+ if (temp.out_xbox != null) {
+ try {
+ temp.out_xbox.Disconnect();
+ } catch (Exception e) {
+ // it wasn't connected in the first place, go figure
+ }
+ }
+ if (temp.out_ds4 != null) {
+ try {
+ temp.out_ds4.Disconnect();
+ } catch (Exception e) {
+ // it wasn't connected in the first place, go figure
+ }
+ }
+ temp.out_xbox = null;
+ temp.out_ds4 = null;
+
+ foreach (Button b in form.con)
+ if (b.Tag == v || b.Tag == temp) {
+ Joycon tt = (b.Tag == v) ? v : (b.Tag == temp) ? temp : v;
+ b.BackgroundImage = tt.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
+ }
+
+ temp = null; // repeat
+ }
+ }
+ }
+ }
+
+ HIDapi.hid_free_enumeration(top_ptr);
+
+ 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();
+
+ jc.Attach(leds_: jc.LED);
+
+ bool on = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings.Settings["HomeLEDOn"].Value.ToLower() == "true";
+ foreach (Joycon j in Program.mgr.j) {
+ j.SetHomeLight(on);
+ }
+
+ jc.Begin();
+ if (form.nonOriginal) {
+ jc.getActiveData();
+ }
+
+ }
+ }
+ }
+
+ public void Update() {
+ for (int i = 0; i < j.Count; ++i)
+ j[i].Update();
+ }
+
+ public void OnApplicationQuit() {
+ foreach (Joycon v in j) {
+ if (Boolean.Parse(ConfigurationManager.AppSettings["AutoPowerOff"]))
+ v.PowerOff();
+ else
+ v.Detach();
+
+ if (v.out_xbox != null) {
+ v.out_xbox.Disconnect();
+ }
+
+ if (v.out_ds4 != null) {
+ v.out_ds4.Disconnect();
+ }
+ }
+
+ controllerCheck.Stop();
+ HIDapi.hid_exit();
+ }
+ }
+
+ // 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.IsBackground = true;
+ 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();
+
+ public static JoyconManager mgr;
+ static HighResTimer timer;
+ static string pid;
+
+ static MainForm form;
+
+ static public bool useHIDG = Boolean.Parse(ConfigurationManager.AppSettings["UseHIDG"]);
+
+ private static WindowsInput.Events.Sources.IKeyboardEventSource keyboard;
+ private static WindowsInput.Events.Sources.IMouseEventSource mouse;
+
+ public static void Start() {
+ pid = Process.GetCurrentProcess().Id.ToString(); // get current process id for HidCerberus.Srv
+
+ if (useHIDG) {
+ form.console.AppendText("HidGuardian is enabled.\r\n");
+ try {
+ var HidCerberusService = new ServiceController("HidCerberus Service");
+ if (HidCerberusService.Status == ServiceControllerStatus.Stopped) {
+ form.console.AppendText("HidGuardian was stopped. Starting...\r\n");
+
+ try {
+ HidCerberusService.Start();
+ } catch (Exception e) {
+ form.console.AppendText("Unable to start HidGuardian - everything should work fine without it, but if you need it, run the app again as an admin.\r\n");
+ useHIDG = false;
+ }
+ }
+ } catch (Exception e) {
+ form.console.AppendText("Unable to start HidGuardian - everything should work fine without it, but if you need it, install it properly as admin.\r\n");
+ useHIDG = false;
+ }
+
+ HttpWebResponse response;
+ if (Boolean.Parse(ConfigurationManager.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.AppendText("Unable to purge whitelist.\r\n");
+ useHIDG = false;
+ }
+ }
+
+ 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.AppendText("Unable to add program to whitelist.\r\n");
+ useHIDG = false;
+ }
+ }
+
+ if (Boolean.Parse(ConfigurationManager.AppSettings["ShowAsXInput"]) || Boolean.Parse(ConfigurationManager.AppSettings["ShowAsDS4"])) {
+ try {
+ emClient = new ViGEmClient(); // Manages emulated XInput
+ } catch (Nefarius.ViGEm.Client.Exceptions.VigemBusNotFoundException) {
+ form.console.AppendText("Could not start VigemBus. Make sure drivers are installed correctly.\r\n");
+ }
+ }
+
+ 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.CheckForNewControllers();
+ mgr.Start();
+
+ server = new UdpServer(mgr.j);
+ server.form = form;
+
+ server.Start(IPAddress.Parse(ConfigurationManager.AppSettings["IP"]), Int32.Parse(ConfigurationManager.AppSettings["Port"]));
+ timer = new HighResTimer(pollsPerSecond, new HighResTimer.ActionDelegate(mgr.Update));
+ timer.Start();
+
+ // Capture keyboard + mouse events for binding's sake
+ keyboard = WindowsInput.Capture.Global.KeyboardAsync();
+ keyboard.KeyEvent += Keyboard_KeyEvent;
+ mouse = WindowsInput.Capture.Global.MouseAsync();
+ mouse.MouseEvent += Mouse_MouseEvent;
+
+ form.console.AppendText("All systems go\r\n");
+ }
+
+ private static void Mouse_MouseEvent(object sender, WindowsInput.Events.Sources.EventSourceEventArgs e) {
+ if (e.Data.ButtonDown != null) {
+ string res_val = Config.Value("reset_mouse");
+ if (res_val.StartsWith("mse_"))
+ if ((int)e.Data.ButtonDown.Button == Int32.Parse(res_val.Substring(4)))
+ WindowsInput.Simulate.Events().MoveTo(Screen.PrimaryScreen.Bounds.Width / 2, Screen.PrimaryScreen.Bounds.Height / 2).Invoke();
+
+ res_val = Config.Value("active_gyro");
+ if (res_val.StartsWith("mse_"))
+ if ((int)e.Data.ButtonDown.Button == Int32.Parse(res_val.Substring(4)))
+ foreach (var i in mgr.j)
+ i.active_gyro = true;
+ }
+
+ if (e.Data.ButtonUp != null) {
+ string res_val = Config.Value("active_gyro");
+ if (res_val.StartsWith("mse_"))
+ if ((int)e.Data.ButtonUp.Button == Int32.Parse(res_val.Substring(4)))
+ foreach (var i in mgr.j)
+ i.active_gyro = false;
+ }
+ }
+
+ private static void Keyboard_KeyEvent(object sender, WindowsInput.Events.Sources.EventSourceEventArgs e) {
+ if (e.Data.KeyDown != null) {
+ string res_val = Config.Value("reset_mouse");
+ if (res_val.StartsWith("key_"))
+ if ((int)e.Data.KeyDown.Key == Int32.Parse(res_val.Substring(4)))
+ WindowsInput.Simulate.Events().MoveTo(Screen.PrimaryScreen.Bounds.Width / 2, Screen.PrimaryScreen.Bounds.Height / 2).Invoke();
+
+ res_val = Config.Value("active_gyro");
+ if (res_val.StartsWith("key_"))
+ if ((int)e.Data.KeyDown.Key == Int32.Parse(res_val.Substring(4)))
+ foreach (var i in mgr.j)
+ i.active_gyro = true;
+ }
+
+ if (e.Data.KeyUp != null) {
+ string res_val = Config.Value("active_gyro");
+ if (res_val.StartsWith("key_"))
+ if ((int)e.Data.KeyUp.Key == Int32.Parse(res_val.Substring(4)))
+ foreach (var i in mgr.j)
+ i.active_gyro = false;
+ }
+ }
+
+ public static void Stop() {
+ if (Program.useHIDG) {
+ try {
+ HttpWebResponse response = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/whitelist/remove/" + pid).GetResponse();
+ } catch (Exception e) {
+ form.console.AppendText("Unable to remove program from whitelist.\r\n");
+ }
+ }
+
+ if (Boolean.Parse(ConfigurationManager.AppSettings["PurgeAffectedDevices"]) && Program.useHIDG) {
+ try {
+ HttpWebResponse r1 = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/affected/purge/").GetResponse();
+ } catch { }
+ }
+
+ keyboard.Dispose(); mouse.Dispose();
+ server.Stop();
+ timer.Stop();
+ mgr.OnApplicationQuit();
+ }
+
+ private static string appGuid = "04450797-3520-462e-a563-107677a483d8"; // randomly-generated
+ static void Main(string[] args) {
+ using (Mutex mutex = new Mutex(false, "Global\\" + appGuid)) {
+ if (!mutex.WaitOne(0, false)) {
+ MessageBox.Show("Instance already running.", "BetterJoy");
+ return;
+ }
+
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ form = new MainForm();
+ Application.Run(form);
+ }
+ }
+ }
}
diff --git a/BetterJoyForCemu/Properties/Resources.Designer.cs b/BetterJoyForCemu/Properties/Resources.Designer.cs
index d58a192..cd691cc 100644
--- a/BetterJoyForCemu/Properties/Resources.Designer.cs
+++ b/BetterJoyForCemu/Properties/Resources.Designer.cs
@@ -9,135 +9,135 @@
//------------------------------------------------------------------------------
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 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));
+ }
+ }
+ }
}
diff --git a/BetterJoyForCemu/Reassign.cs b/BetterJoyForCemu/Reassign.cs
index 122e3d8..df60f53 100644
--- a/BetterJoyForCemu/Reassign.cs
+++ b/BetterJoyForCemu/Reassign.cs
@@ -29,7 +29,7 @@ namespace BetterJoyForCemu {
menu_joy_buttons.ItemClicked += Menu_joy_buttons_ItemClicked;
- foreach (SplitButton c in new SplitButton[] { btn_capture, btn_home, btn_sl_l, btn_sl_r, btn_sr_l, btn_sr_r, btn_reset_mouse , btn_active_gyro}) {
+ foreach (SplitButton c in new SplitButton[] { btn_capture, btn_home, btn_sl_l, btn_sl_r, btn_sr_l, btn_sr_r, btn_reset_mouse, btn_active_gyro }) {
c.Tag = c.Name.Substring(4);
GetPrettyName(c);
@@ -58,7 +58,7 @@ namespace BetterJoyForCemu {
curAssignment = c;
break;
case MouseButtons.Middle:
- Config.SetValue((string) c.Tag, Config.GetDefaultValue((string) c.Tag));
+ Config.SetValue((string)c.Tag, Config.GetDefaultValue((string)c.Tag));
GetPrettyName(c);
break;
case MouseButtons.Right:
diff --git a/BetterJoyForCemu/UpdServer.cs b/BetterJoyForCemu/UpdServer.cs
index 6c571dc..e5ea4c4 100644
--- a/BetterJoyForCemu/UpdServer.cs
+++ b/BetterJoyForCemu/UpdServer.cs
@@ -7,509 +7,509 @@ using System.Net.Sockets;
using Force.Crc32;
namespace BetterJoyForCemu {
- class UdpServer {
- private Socket udpSock;
- private uint serverId;
- private bool running;
- private byte[] recvBuffer = new byte[1024];
-
- List controllers;
-
- public MainForm form;
-
- public UdpServer(List p) {
- controllers = p;
- }
-
- enum MessageType {
- DSUC_VersionReq = 0x100000,
- DSUS_VersionRsp = 0x100000,
- DSUC_ListPorts = 0x100001,
- DSUS_PortInfo = 0x100001,
- DSUC_PadDataReq = 0x100002,
- DSUS_PadDataRsp = 0x100002,
- };
-
- private const ushort MaxProtocolVersion = 1001;
-
- class ClientRequestTimes {
- DateTime allPads;
- DateTime[] padIds;
- Dictionary padMacs;
-
- public DateTime AllPadsTime { get { return allPads; } }
- public DateTime[] PadIdsTime { get { return padIds; } }
- public Dictionary PadMacsTime { get { return padMacs; } }
-
- public ClientRequestTimes() {
- allPads = DateTime.MinValue;
- padIds = new DateTime[4];
-
- for (int i = 0; i < padIds.Length; i++)
- padIds[i] = DateTime.MinValue;
-
- padMacs = new Dictionary();
- }
-
- public void RequestPadInfo(byte regFlags, byte idToReg, PhysicalAddress macToReg) {
- if (regFlags == 0)
- allPads = DateTime.UtcNow;
- else {
- if ((regFlags & 0x01) != 0) //id valid
- {
- if (idToReg < padIds.Length)
- padIds[idToReg] = DateTime.UtcNow;
- }
- if ((regFlags & 0x02) != 0) //mac valid
- {
- padMacs[macToReg] = DateTime.UtcNow;
- }
- }
- }
- }
-
- private Dictionary clients = new Dictionary();
-
- private int BeginPacket(byte[] packetBuf, ushort reqProtocolVersion = MaxProtocolVersion) {
- int currIdx = 0;
- packetBuf[currIdx++] = (byte)'D';
- packetBuf[currIdx++] = (byte)'S';
- packetBuf[currIdx++] = (byte)'U';
- packetBuf[currIdx++] = (byte)'S';
-
- Array.Copy(BitConverter.GetBytes((ushort)reqProtocolVersion), 0, packetBuf, currIdx, 2);
- currIdx += 2;
-
- Array.Copy(BitConverter.GetBytes((ushort)packetBuf.Length - 16), 0, packetBuf, currIdx, 2);
- currIdx += 2;
-
- Array.Clear(packetBuf, currIdx, 4); //place for crc
- currIdx += 4;
-
- Array.Copy(BitConverter.GetBytes((uint)serverId), 0, packetBuf, currIdx, 4);
- currIdx += 4;
-
- return currIdx;
- }
-
- private void FinishPacket(byte[] packetBuf) {
- Array.Clear(packetBuf, 8, 4);
-
- uint crcCalc = Crc32Algorithm.Compute(packetBuf);
- Array.Copy(BitConverter.GetBytes((uint)crcCalc), 0, packetBuf, 8, 4);
- }
-
- private void SendPacket(IPEndPoint clientEP, byte[] usefulData, ushort reqProtocolVersion = MaxProtocolVersion) {
- byte[] packetData = new byte[usefulData.Length + 16];
- int currIdx = BeginPacket(packetData, reqProtocolVersion);
- Array.Copy(usefulData, 0, packetData, currIdx, usefulData.Length);
- FinishPacket(packetData);
-
- try { udpSock.SendTo(packetData, clientEP); } catch (Exception e) { }
- }
-
- private void ProcessIncoming(byte[] localMsg, IPEndPoint clientEP) {
- try {
- int currIdx = 0;
- if (localMsg[0] != 'D' || localMsg[1] != 'S' || localMsg[2] != 'U' || localMsg[3] != 'C')
- return;
- else
- currIdx += 4;
-
- uint protocolVer = BitConverter.ToUInt16(localMsg, currIdx);
- currIdx += 2;
-
- if (protocolVer > MaxProtocolVersion)
- return;
-
- uint packetSize = BitConverter.ToUInt16(localMsg, currIdx);
- currIdx += 2;
-
- if (packetSize < 0)
- return;
-
- packetSize += 16; //size of header
- if (packetSize > localMsg.Length)
- return;
- else if (packetSize < localMsg.Length) {
- byte[] newMsg = new byte[packetSize];
- Array.Copy(localMsg, newMsg, packetSize);
- localMsg = newMsg;
- }
-
- uint crcValue = BitConverter.ToUInt32(localMsg, currIdx);
- //zero out the crc32 in the packet once we got it since that's whats needed for calculation
- localMsg[currIdx++] = 0;
- localMsg[currIdx++] = 0;
- localMsg[currIdx++] = 0;
- localMsg[currIdx++] = 0;
-
- uint crcCalc = Crc32Algorithm.Compute(localMsg);
- if (crcValue != crcCalc)
- return;
-
- uint clientId = BitConverter.ToUInt32(localMsg, currIdx);
- currIdx += 4;
-
- uint messageType = BitConverter.ToUInt32(localMsg, currIdx);
- currIdx += 4;
-
- if (messageType == (uint)MessageType.DSUC_VersionReq) {
- byte[] outputData = new byte[8];
- int outIdx = 0;
- Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_VersionRsp), 0, outputData, outIdx, 4);
- outIdx += 4;
- Array.Copy(BitConverter.GetBytes((ushort)MaxProtocolVersion), 0, outputData, outIdx, 2);
- outIdx += 2;
- outputData[outIdx++] = 0;
- outputData[outIdx++] = 0;
-
- SendPacket(clientEP, outputData, 1001);
- } else if (messageType == (uint)MessageType.DSUC_ListPorts) {
- // Requested information on gamepads - return MAC address
- int numPadRequests = BitConverter.ToInt32(localMsg, currIdx);
- currIdx += 4;
- if (numPadRequests < 0 || numPadRequests > 4)
- return;
-
- int requestsIdx = currIdx;
- for (int i = 0; i < numPadRequests; i++) {
- byte currRequest = localMsg[requestsIdx + i];
- if (currRequest < 0 || currRequest > 4)
- return;
- }
-
- byte[] outputData = new byte[16];
- for (byte i = 0; i < numPadRequests; i++) {
- byte currRequest = localMsg[requestsIdx + i];
- var padData = controllers[i];//controllers[currRequest];
-
- int outIdx = 0;
- Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PortInfo), 0, outputData, outIdx, 4);
- outIdx += 4;
-
- outputData[outIdx++] = (byte)padData.PadId;
- outputData[outIdx++] = (byte)padData.constate;
- outputData[outIdx++] = (byte)padData.model;
- outputData[outIdx++] = (byte)padData.connection;
-
- var addressBytes = padData.PadMacAddress.GetAddressBytes();
- if (addressBytes.Length == 6) {
- outputData[outIdx++] = addressBytes[0];
- outputData[outIdx++] = addressBytes[1];
- outputData[outIdx++] = addressBytes[2];
- outputData[outIdx++] = addressBytes[3];
- outputData[outIdx++] = addressBytes[4];
- outputData[outIdx++] = addressBytes[5];
- } else {
- outputData[outIdx++] = 0;
- outputData[outIdx++] = 0;
- outputData[outIdx++] = 0;
- outputData[outIdx++] = 0;
- outputData[outIdx++] = 0;
- outputData[outIdx++] = 0;
- }
-
- outputData[outIdx++] = (byte)padData.battery;//(byte)padData.BatteryStatus;
- outputData[outIdx++] = 0;
-
- SendPacket(clientEP, outputData, 1001);
- }
- } else if (messageType == (uint)MessageType.DSUC_PadDataReq) {
- byte regFlags = localMsg[currIdx++];
- byte idToReg = localMsg[currIdx++];
- PhysicalAddress macToReg = null;
- {
- byte[] macBytes = new byte[6];
- Array.Copy(localMsg, currIdx, macBytes, 0, macBytes.Length);
- currIdx += macBytes.Length;
- macToReg = new PhysicalAddress(macBytes);
- }
-
- lock (clients) {
- if (clients.ContainsKey(clientEP))
- clients[clientEP].RequestPadInfo(regFlags, idToReg, macToReg);
- else {
- var clientTimes = new ClientRequestTimes();
- clientTimes.RequestPadInfo(regFlags, idToReg, macToReg);
- clients[clientEP] = clientTimes;
- }
- }
- }
- } catch (Exception e) { }
- }
-
- private void ReceiveCallback(IAsyncResult iar) {
- byte[] localMsg = null;
- EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
-
- try {
- //Get the received message.
- Socket recvSock = (Socket)iar.AsyncState;
- int msgLen = recvSock.EndReceiveFrom(iar, ref clientEP);
-
- localMsg = new byte[msgLen];
- Array.Copy(recvBuffer, localMsg, msgLen);
- } catch (Exception e) { }
-
- //Start another receive as soon as we copied the data
- StartReceive();
-
- //Process the data if its valid
- if (localMsg != null) {
- ProcessIncoming(localMsg, (IPEndPoint)clientEP);
- }
- }
- private void StartReceive() {
- try {
- if (running) {
- //Start listening for a new message.
- EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
- udpSock.BeginReceiveFrom(recvBuffer, 0, recvBuffer.Length, SocketFlags.None, ref newClientEP, ReceiveCallback, udpSock);
- }
- } catch (SocketException ex) {
- uint IOC_IN = 0x80000000;
- uint IOC_VENDOR = 0x18000000;
- uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
- udpSock.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
-
- StartReceive();
- }
- }
-
- public void Start(IPAddress ip, int port = 26760) {
- if (!Boolean.Parse(ConfigurationManager.AppSettings["MotionServer"])) {
- form.console.AppendText("Motion server is OFF.\r\n");
- return;
- }
-
- if (running) {
- if (udpSock != null) {
- udpSock.Close();
- udpSock = null;
- }
- running = false;
- }
-
- udpSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
- try { udpSock.Bind(new IPEndPoint(ip, port)); } catch (SocketException ex) {
- udpSock.Close();
- udpSock = null;
-
- form.console.AppendText("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.\r\n");
- return;
- }
-
- byte[] randomBuf = new byte[4];
- new Random().NextBytes(randomBuf);
- serverId = BitConverter.ToUInt32(randomBuf, 0);
-
- running = true;
- form.console.AppendText(String.Format("Starting server on {0}:{1}\r\n", ip.ToString(), port));
- StartReceive();
- }
-
- public void Stop() {
- running = false;
- if (udpSock != null) {
- udpSock.Close();
- udpSock = null;
- }
- }
-
- bool swapAB = Boolean.Parse(ConfigurationManager.AppSettings["SwapAB"]);
- bool swapXY = Boolean.Parse(ConfigurationManager.AppSettings["SwapXY"]);
- private bool ReportToBuffer(Joycon hidReport, byte[] outputData, ref int outIdx) {
- outputData[outIdx] = 0;
-
- bool isLeft = hidReport.isLeft;
-
- if (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_LEFT : Joycon.Button.Y)) 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.A)) 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(!swapXY ? (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT) : (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP))) outputData[outIdx] |= 0x80;
- if (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN) : (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT))) outputData[outIdx] |= 0x40;
- if (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT) : (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN))) outputData[outIdx] |= 0x20;
- if (hidReport.GetButton(!swapXY ? (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP) : (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT))) outputData[outIdx] |= 0x10;
-
- 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
-
- float[] leftStick = hidReport.GetStick(); // 127 is 0
- outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + leftStick[0] * 127)));
- outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + leftStick[1] * 127)));
-
- float[] rightStick = hidReport.GetStick2(); // 127 is 0
- outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + rightStick[0] * 127)));
- 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(isLeft ? Joycon.Button.DPAD_LEFT : Joycon.Button.Y)) ? (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.A)) ? (byte)0xFF : (byte)0;
- outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_UP : Joycon.Button.X)) ? (byte)0xFF : (byte)0;
-
- outputData[++outIdx] = (hidReport.GetButton(!swapXY ? (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT) : (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP))) ? (byte)0xFF : (byte)0;
- outputData[++outIdx] = (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN) : (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT))) ? (byte)0xFF : (byte)0;
- outputData[++outIdx] = (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT) : (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN))) ? (byte)0xFF : (byte)0;
- outputData[++outIdx] = (hidReport.GetButton(!swapXY ? (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP) : (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT))) ? (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(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++;
-
- //DS4 only: touchpad points
- for (int i = 0; i < 2; i++) {
- outIdx += 6;
- }
-
- //motion timestamp
- Array.Copy(BitConverter.GetBytes(hidReport.Timestamp), 0, outputData, outIdx, 8);
- outIdx += 8;
-
- //accelerometer
- {
- var accel = hidReport.GetAccel();
- if (accel != null) {
- Array.Copy(BitConverter.GetBytes(accel.Y), 0, outputData, outIdx, 4);
- outIdx += 4;
- Array.Copy(BitConverter.GetBytes(-accel.Z), 0, outputData, outIdx, 4);
- outIdx += 4;
- Array.Copy(BitConverter.GetBytes(accel.X), 0, outputData, outIdx, 4);
- outIdx += 4;
- } else {
- outIdx += 12;
- Console.WriteLine("No accelerometer reported.");
- }
- }
-
- //gyroscope
- {
- var gyro = hidReport.GetGyro();
- if (gyro != null) {
- Array.Copy(BitConverter.GetBytes(gyro.Y), 0, outputData, outIdx, 4);
- outIdx += 4;
- Array.Copy(BitConverter.GetBytes(gyro.Z), 0, outputData, outIdx, 4);
- outIdx += 4;
- Array.Copy(BitConverter.GetBytes(gyro.X), 0, outputData, outIdx, 4);
- outIdx += 4;
- } else {
- outIdx += 12;
- Console.WriteLine("No gyroscope reported.");
- }
- }
-
- return true;
- }
-
- public void NewReportIncoming(Joycon hidReport) {
- if (!running)
- return;
-
- var clientsList = new List();
- var now = DateTime.UtcNow;
- lock (clients) {
- var clientsToDelete = new List();
-
- foreach (var cl in clients) {
- const double TimeoutLimit = 5;
-
- if ((now - cl.Value.AllPadsTime).TotalSeconds < TimeoutLimit)
- clientsList.Add(cl.Key);
- else if ((hidReport.PadId >= 0 && hidReport.PadId <= 3) &&
- (now - cl.Value.PadIdsTime[(byte)hidReport.PadId]).TotalSeconds < TimeoutLimit)
- clientsList.Add(cl.Key);
- else if (cl.Value.PadMacsTime.ContainsKey(hidReport.PadMacAddress) &&
- (now - cl.Value.PadMacsTime[hidReport.PadMacAddress]).TotalSeconds < TimeoutLimit)
- clientsList.Add(cl.Key);
- else //check if this client is totally dead, and remove it if so
- {
- bool clientOk = false;
- for (int i = 0; i < cl.Value.PadIdsTime.Length; i++) {
- var dur = (now - cl.Value.PadIdsTime[i]).TotalSeconds;
- if (dur < TimeoutLimit) {
- clientOk = true;
- break;
- }
- }
- if (!clientOk) {
- foreach (var dict in cl.Value.PadMacsTime) {
- var dur = (now - dict.Value).TotalSeconds;
- if (dur < TimeoutLimit) {
- clientOk = true;
- break;
- }
- }
-
- if (!clientOk)
- clientsToDelete.Add(cl.Key);
- }
- }
- }
-
- foreach (var delCl in clientsToDelete) {
- clients.Remove(delCl);
- }
- clientsToDelete.Clear();
- clientsToDelete = null;
- }
-
- if (clientsList.Count <= 0)
- return;
-
- byte[] outputData = new byte[100];
- int outIdx = BeginPacket(outputData, 1001);
- Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PadDataRsp), 0, outputData, outIdx, 4);
- outIdx += 4;
-
- outputData[outIdx++] = (byte)hidReport.PadId;
- outputData[outIdx++] = (byte)hidReport.constate;
- outputData[outIdx++] = (byte)hidReport.model;
- outputData[outIdx++] = (byte)hidReport.connection;
- {
- byte[] padMac = hidReport.PadMacAddress.GetAddressBytes();
- outputData[outIdx++] = padMac[0];
- outputData[outIdx++] = padMac[1];
- outputData[outIdx++] = padMac[2];
- outputData[outIdx++] = padMac[3];
- outputData[outIdx++] = padMac[4];
- outputData[outIdx++] = padMac[5];
- }
-
- outputData[outIdx++] = (byte)hidReport.battery;
- outputData[outIdx++] = 1;
-
- Array.Copy(BitConverter.GetBytes(hidReport.packetCounter), 0, outputData, outIdx, 4);
- outIdx += 4;
-
- if (!ReportToBuffer(hidReport, outputData, ref outIdx))
- return;
- else
- FinishPacket(outputData);
-
- foreach (var cl in clientsList) {
- try { udpSock.SendTo(outputData, cl); } catch (SocketException ex) { }
- }
- clientsList.Clear();
- clientsList = null;
- }
- }
+ class UdpServer {
+ private Socket udpSock;
+ private uint serverId;
+ private bool running;
+ private byte[] recvBuffer = new byte[1024];
+
+ List controllers;
+
+ public MainForm form;
+
+ public UdpServer(List p) {
+ controllers = p;
+ }
+
+ enum MessageType {
+ DSUC_VersionReq = 0x100000,
+ DSUS_VersionRsp = 0x100000,
+ DSUC_ListPorts = 0x100001,
+ DSUS_PortInfo = 0x100001,
+ DSUC_PadDataReq = 0x100002,
+ DSUS_PadDataRsp = 0x100002,
+ };
+
+ private const ushort MaxProtocolVersion = 1001;
+
+ class ClientRequestTimes {
+ DateTime allPads;
+ DateTime[] padIds;
+ Dictionary padMacs;
+
+ public DateTime AllPadsTime { get { return allPads; } }
+ public DateTime[] PadIdsTime { get { return padIds; } }
+ public Dictionary PadMacsTime { get { return padMacs; } }
+
+ public ClientRequestTimes() {
+ allPads = DateTime.MinValue;
+ padIds = new DateTime[4];
+
+ for (int i = 0; i < padIds.Length; i++)
+ padIds[i] = DateTime.MinValue;
+
+ padMacs = new Dictionary();
+ }
+
+ public void RequestPadInfo(byte regFlags, byte idToReg, PhysicalAddress macToReg) {
+ if (regFlags == 0)
+ allPads = DateTime.UtcNow;
+ else {
+ if ((regFlags & 0x01) != 0) //id valid
+ {
+ if (idToReg < padIds.Length)
+ padIds[idToReg] = DateTime.UtcNow;
+ }
+ if ((regFlags & 0x02) != 0) //mac valid
+ {
+ padMacs[macToReg] = DateTime.UtcNow;
+ }
+ }
+ }
+ }
+
+ private Dictionary clients = new Dictionary();
+
+ private int BeginPacket(byte[] packetBuf, ushort reqProtocolVersion = MaxProtocolVersion) {
+ int currIdx = 0;
+ packetBuf[currIdx++] = (byte)'D';
+ packetBuf[currIdx++] = (byte)'S';
+ packetBuf[currIdx++] = (byte)'U';
+ packetBuf[currIdx++] = (byte)'S';
+
+ Array.Copy(BitConverter.GetBytes((ushort)reqProtocolVersion), 0, packetBuf, currIdx, 2);
+ currIdx += 2;
+
+ Array.Copy(BitConverter.GetBytes((ushort)packetBuf.Length - 16), 0, packetBuf, currIdx, 2);
+ currIdx += 2;
+
+ Array.Clear(packetBuf, currIdx, 4); //place for crc
+ currIdx += 4;
+
+ Array.Copy(BitConverter.GetBytes((uint)serverId), 0, packetBuf, currIdx, 4);
+ currIdx += 4;
+
+ return currIdx;
+ }
+
+ private void FinishPacket(byte[] packetBuf) {
+ Array.Clear(packetBuf, 8, 4);
+
+ uint crcCalc = Crc32Algorithm.Compute(packetBuf);
+ Array.Copy(BitConverter.GetBytes((uint)crcCalc), 0, packetBuf, 8, 4);
+ }
+
+ private void SendPacket(IPEndPoint clientEP, byte[] usefulData, ushort reqProtocolVersion = MaxProtocolVersion) {
+ byte[] packetData = new byte[usefulData.Length + 16];
+ int currIdx = BeginPacket(packetData, reqProtocolVersion);
+ Array.Copy(usefulData, 0, packetData, currIdx, usefulData.Length);
+ FinishPacket(packetData);
+
+ try { udpSock.SendTo(packetData, clientEP); } catch (Exception e) { }
+ }
+
+ private void ProcessIncoming(byte[] localMsg, IPEndPoint clientEP) {
+ try {
+ int currIdx = 0;
+ if (localMsg[0] != 'D' || localMsg[1] != 'S' || localMsg[2] != 'U' || localMsg[3] != 'C')
+ return;
+ else
+ currIdx += 4;
+
+ uint protocolVer = BitConverter.ToUInt16(localMsg, currIdx);
+ currIdx += 2;
+
+ if (protocolVer > MaxProtocolVersion)
+ return;
+
+ uint packetSize = BitConverter.ToUInt16(localMsg, currIdx);
+ currIdx += 2;
+
+ if (packetSize < 0)
+ return;
+
+ packetSize += 16; //size of header
+ if (packetSize > localMsg.Length)
+ return;
+ else if (packetSize < localMsg.Length) {
+ byte[] newMsg = new byte[packetSize];
+ Array.Copy(localMsg, newMsg, packetSize);
+ localMsg = newMsg;
+ }
+
+ uint crcValue = BitConverter.ToUInt32(localMsg, currIdx);
+ //zero out the crc32 in the packet once we got it since that's whats needed for calculation
+ localMsg[currIdx++] = 0;
+ localMsg[currIdx++] = 0;
+ localMsg[currIdx++] = 0;
+ localMsg[currIdx++] = 0;
+
+ uint crcCalc = Crc32Algorithm.Compute(localMsg);
+ if (crcValue != crcCalc)
+ return;
+
+ uint clientId = BitConverter.ToUInt32(localMsg, currIdx);
+ currIdx += 4;
+
+ uint messageType = BitConverter.ToUInt32(localMsg, currIdx);
+ currIdx += 4;
+
+ if (messageType == (uint)MessageType.DSUC_VersionReq) {
+ byte[] outputData = new byte[8];
+ int outIdx = 0;
+ Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_VersionRsp), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ Array.Copy(BitConverter.GetBytes((ushort)MaxProtocolVersion), 0, outputData, outIdx, 2);
+ outIdx += 2;
+ outputData[outIdx++] = 0;
+ outputData[outIdx++] = 0;
+
+ SendPacket(clientEP, outputData, 1001);
+ } else if (messageType == (uint)MessageType.DSUC_ListPorts) {
+ // Requested information on gamepads - return MAC address
+ int numPadRequests = BitConverter.ToInt32(localMsg, currIdx);
+ currIdx += 4;
+ if (numPadRequests < 0 || numPadRequests > 4)
+ return;
+
+ int requestsIdx = currIdx;
+ for (int i = 0; i < numPadRequests; i++) {
+ byte currRequest = localMsg[requestsIdx + i];
+ if (currRequest < 0 || currRequest > 4)
+ return;
+ }
+
+ byte[] outputData = new byte[16];
+ for (byte i = 0; i < numPadRequests; i++) {
+ byte currRequest = localMsg[requestsIdx + i];
+ var padData = controllers[i];//controllers[currRequest];
+
+ int outIdx = 0;
+ Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PortInfo), 0, outputData, outIdx, 4);
+ outIdx += 4;
+
+ outputData[outIdx++] = (byte)padData.PadId;
+ outputData[outIdx++] = (byte)padData.constate;
+ outputData[outIdx++] = (byte)padData.model;
+ outputData[outIdx++] = (byte)padData.connection;
+
+ var addressBytes = padData.PadMacAddress.GetAddressBytes();
+ if (addressBytes.Length == 6) {
+ outputData[outIdx++] = addressBytes[0];
+ outputData[outIdx++] = addressBytes[1];
+ outputData[outIdx++] = addressBytes[2];
+ outputData[outIdx++] = addressBytes[3];
+ outputData[outIdx++] = addressBytes[4];
+ outputData[outIdx++] = addressBytes[5];
+ } else {
+ outputData[outIdx++] = 0;
+ outputData[outIdx++] = 0;
+ outputData[outIdx++] = 0;
+ outputData[outIdx++] = 0;
+ outputData[outIdx++] = 0;
+ outputData[outIdx++] = 0;
+ }
+
+ outputData[outIdx++] = (byte)padData.battery;//(byte)padData.BatteryStatus;
+ outputData[outIdx++] = 0;
+
+ SendPacket(clientEP, outputData, 1001);
+ }
+ } else if (messageType == (uint)MessageType.DSUC_PadDataReq) {
+ byte regFlags = localMsg[currIdx++];
+ byte idToReg = localMsg[currIdx++];
+ PhysicalAddress macToReg = null;
+ {
+ byte[] macBytes = new byte[6];
+ Array.Copy(localMsg, currIdx, macBytes, 0, macBytes.Length);
+ currIdx += macBytes.Length;
+ macToReg = new PhysicalAddress(macBytes);
+ }
+
+ lock (clients) {
+ if (clients.ContainsKey(clientEP))
+ clients[clientEP].RequestPadInfo(regFlags, idToReg, macToReg);
+ else {
+ var clientTimes = new ClientRequestTimes();
+ clientTimes.RequestPadInfo(regFlags, idToReg, macToReg);
+ clients[clientEP] = clientTimes;
+ }
+ }
+ }
+ } catch (Exception e) { }
+ }
+
+ private void ReceiveCallback(IAsyncResult iar) {
+ byte[] localMsg = null;
+ EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
+
+ try {
+ //Get the received message.
+ Socket recvSock = (Socket)iar.AsyncState;
+ int msgLen = recvSock.EndReceiveFrom(iar, ref clientEP);
+
+ localMsg = new byte[msgLen];
+ Array.Copy(recvBuffer, localMsg, msgLen);
+ } catch (Exception e) { }
+
+ //Start another receive as soon as we copied the data
+ StartReceive();
+
+ //Process the data if its valid
+ if (localMsg != null) {
+ ProcessIncoming(localMsg, (IPEndPoint)clientEP);
+ }
+ }
+ private void StartReceive() {
+ try {
+ if (running) {
+ //Start listening for a new message.
+ EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
+ udpSock.BeginReceiveFrom(recvBuffer, 0, recvBuffer.Length, SocketFlags.None, ref newClientEP, ReceiveCallback, udpSock);
+ }
+ } catch (SocketException ex) {
+ uint IOC_IN = 0x80000000;
+ uint IOC_VENDOR = 0x18000000;
+ uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
+ udpSock.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
+
+ StartReceive();
+ }
+ }
+
+ public void Start(IPAddress ip, int port = 26760) {
+ if (!Boolean.Parse(ConfigurationManager.AppSettings["MotionServer"])) {
+ form.console.AppendText("Motion server is OFF.\r\n");
+ return;
+ }
+
+ if (running) {
+ if (udpSock != null) {
+ udpSock.Close();
+ udpSock = null;
+ }
+ running = false;
+ }
+
+ udpSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+ try { udpSock.Bind(new IPEndPoint(ip, port)); } catch (SocketException ex) {
+ udpSock.Close();
+ udpSock = null;
+
+ form.console.AppendText("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.\r\n");
+ return;
+ }
+
+ byte[] randomBuf = new byte[4];
+ new Random().NextBytes(randomBuf);
+ serverId = BitConverter.ToUInt32(randomBuf, 0);
+
+ running = true;
+ form.console.AppendText(String.Format("Starting server on {0}:{1}\r\n", ip.ToString(), port));
+ StartReceive();
+ }
+
+ public void Stop() {
+ running = false;
+ if (udpSock != null) {
+ udpSock.Close();
+ udpSock = null;
+ }
+ }
+
+ bool swapAB = Boolean.Parse(ConfigurationManager.AppSettings["SwapAB"]);
+ bool swapXY = Boolean.Parse(ConfigurationManager.AppSettings["SwapXY"]);
+ private bool ReportToBuffer(Joycon hidReport, byte[] outputData, ref int outIdx) {
+ outputData[outIdx] = 0;
+
+ bool isLeft = hidReport.isLeft;
+
+ if (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_LEFT : Joycon.Button.Y)) 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.A)) 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(!swapXY ? (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT) : (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP))) outputData[outIdx] |= 0x80;
+ if (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN) : (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT))) outputData[outIdx] |= 0x40;
+ if (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT) : (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN))) outputData[outIdx] |= 0x20;
+ if (hidReport.GetButton(!swapXY ? (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP) : (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT))) outputData[outIdx] |= 0x10;
+
+ 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
+
+ float[] leftStick = hidReport.GetStick(); // 127 is 0
+ outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + leftStick[0] * 127)));
+ outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + leftStick[1] * 127)));
+
+ float[] rightStick = hidReport.GetStick2(); // 127 is 0
+ outputData[++outIdx] = (byte)(Math.Max(0, Math.Min(255, 127 + rightStick[0] * 127)));
+ 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(isLeft ? Joycon.Button.DPAD_LEFT : Joycon.Button.Y)) ? (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.A)) ? (byte)0xFF : (byte)0;
+ outputData[++outIdx] = (hidReport.GetButton(isLeft ? Joycon.Button.DPAD_UP : Joycon.Button.X)) ? (byte)0xFF : (byte)0;
+
+ outputData[++outIdx] = (hidReport.GetButton(!swapXY ? (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT) : (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP))) ? (byte)0xFF : (byte)0;
+ outputData[++outIdx] = (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN) : (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT))) ? (byte)0xFF : (byte)0;
+ outputData[++outIdx] = (hidReport.GetButton(!swapAB ? (isLeft ? Joycon.Button.A : Joycon.Button.DPAD_RIGHT) : (isLeft ? Joycon.Button.B : Joycon.Button.DPAD_DOWN))) ? (byte)0xFF : (byte)0;
+ outputData[++outIdx] = (hidReport.GetButton(!swapXY ? (isLeft ? Joycon.Button.X : Joycon.Button.DPAD_UP) : (isLeft ? Joycon.Button.Y : Joycon.Button.DPAD_LEFT))) ? (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(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++;
+
+ //DS4 only: touchpad points
+ for (int i = 0; i < 2; i++) {
+ outIdx += 6;
+ }
+
+ //motion timestamp
+ Array.Copy(BitConverter.GetBytes(hidReport.Timestamp), 0, outputData, outIdx, 8);
+ outIdx += 8;
+
+ //accelerometer
+ {
+ var accel = hidReport.GetAccel();
+ if (accel != null) {
+ Array.Copy(BitConverter.GetBytes(accel.Y), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ Array.Copy(BitConverter.GetBytes(-accel.Z), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ Array.Copy(BitConverter.GetBytes(accel.X), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ } else {
+ outIdx += 12;
+ Console.WriteLine("No accelerometer reported.");
+ }
+ }
+
+ //gyroscope
+ {
+ var gyro = hidReport.GetGyro();
+ if (gyro != null) {
+ Array.Copy(BitConverter.GetBytes(gyro.Y), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ Array.Copy(BitConverter.GetBytes(gyro.Z), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ Array.Copy(BitConverter.GetBytes(gyro.X), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ } else {
+ outIdx += 12;
+ Console.WriteLine("No gyroscope reported.");
+ }
+ }
+
+ return true;
+ }
+
+ public void NewReportIncoming(Joycon hidReport) {
+ if (!running)
+ return;
+
+ var clientsList = new List();
+ var now = DateTime.UtcNow;
+ lock (clients) {
+ var clientsToDelete = new List();
+
+ foreach (var cl in clients) {
+ const double TimeoutLimit = 5;
+
+ if ((now - cl.Value.AllPadsTime).TotalSeconds < TimeoutLimit)
+ clientsList.Add(cl.Key);
+ else if ((hidReport.PadId >= 0 && hidReport.PadId <= 3) &&
+ (now - cl.Value.PadIdsTime[(byte)hidReport.PadId]).TotalSeconds < TimeoutLimit)
+ clientsList.Add(cl.Key);
+ else if (cl.Value.PadMacsTime.ContainsKey(hidReport.PadMacAddress) &&
+ (now - cl.Value.PadMacsTime[hidReport.PadMacAddress]).TotalSeconds < TimeoutLimit)
+ clientsList.Add(cl.Key);
+ else //check if this client is totally dead, and remove it if so
+ {
+ bool clientOk = false;
+ for (int i = 0; i < cl.Value.PadIdsTime.Length; i++) {
+ var dur = (now - cl.Value.PadIdsTime[i]).TotalSeconds;
+ if (dur < TimeoutLimit) {
+ clientOk = true;
+ break;
+ }
+ }
+ if (!clientOk) {
+ foreach (var dict in cl.Value.PadMacsTime) {
+ var dur = (now - dict.Value).TotalSeconds;
+ if (dur < TimeoutLimit) {
+ clientOk = true;
+ break;
+ }
+ }
+
+ if (!clientOk)
+ clientsToDelete.Add(cl.Key);
+ }
+ }
+ }
+
+ foreach (var delCl in clientsToDelete) {
+ clients.Remove(delCl);
+ }
+ clientsToDelete.Clear();
+ clientsToDelete = null;
+ }
+
+ if (clientsList.Count <= 0)
+ return;
+
+ byte[] outputData = new byte[100];
+ int outIdx = BeginPacket(outputData, 1001);
+ Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PadDataRsp), 0, outputData, outIdx, 4);
+ outIdx += 4;
+
+ outputData[outIdx++] = (byte)hidReport.PadId;
+ outputData[outIdx++] = (byte)hidReport.constate;
+ outputData[outIdx++] = (byte)hidReport.model;
+ outputData[outIdx++] = (byte)hidReport.connection;
+ {
+ byte[] padMac = hidReport.PadMacAddress.GetAddressBytes();
+ outputData[outIdx++] = padMac[0];
+ outputData[outIdx++] = padMac[1];
+ outputData[outIdx++] = padMac[2];
+ outputData[outIdx++] = padMac[3];
+ outputData[outIdx++] = padMac[4];
+ outputData[outIdx++] = padMac[5];
+ }
+
+ outputData[outIdx++] = (byte)hidReport.battery;
+ outputData[outIdx++] = 1;
+
+ Array.Copy(BitConverter.GetBytes(hidReport.packetCounter), 0, outputData, outIdx, 4);
+ outIdx += 4;
+
+ if (!ReportToBuffer(hidReport, outputData, ref outIdx))
+ return;
+ else
+ FinishPacket(outputData);
+
+ foreach (var cl in clientsList) {
+ try { udpSock.SendTo(outputData, cl); } catch (SocketException ex) { }
+ }
+ clientsList.Clear();
+ clientsList = null;
+ }
+ }
}