From d539425b7b2dde0ed65778f385576bef5eaa2642 Mon Sep 17 00:00:00 2001 From: David Khachaturov Date: Thu, 21 May 2020 15:27:28 +0100 Subject: [PATCH] - changed tabs to spaces - formatted all files in accordance to the new spec --- BetterJoyForCemu/.editorconfig | 3 +- .../3rdPartyControllers.Designer.cs | 408 +-- BetterJoyForCemu/3rdPartyControllers.cs | 213 +- BetterJoyForCemu/HIDapi.cs | 2 +- BetterJoyForCemu/Joycon.cs | 2748 ++++++++--------- BetterJoyForCemu/MainForm.cs | 12 +- BetterJoyForCemu/Program.cs | 1036 +++---- .../Properties/Resources.Designer.cs | 262 +- BetterJoyForCemu/Reassign.cs | 4 +- BetterJoyForCemu/UpdServer.cs | 1010 +++--- 10 files changed, 2849 insertions(+), 2849 deletions(-) diff --git a/BetterJoyForCemu/.editorconfig b/BetterJoyForCemu/.editorconfig index 4a55feb1..f0b4f776 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 70844e85..11e087e2 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 32278862..780ce808 100644 --- a/BetterJoyForCemu/3rdPartyControllers.cs +++ b/BetterJoyForCemu/3rdPartyControllers.cs @@ -12,111 +12,110 @@ 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"; - - RefreshControllerList(); - - 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 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)); - - 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; - } - } - - 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 chk_isPro_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_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 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_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_allControllers_MouseDown(object sender, MouseEventArgs e) { - if (e.Y > list_allControllers.ItemHeight * list_allControllers.Items.Count) - list_allControllers.SelectedItems.Clear(); - } - } + 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(); + + 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 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)); + + 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; + } + } + + 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 chk_isPro_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_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 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_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_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 119eea40..40c20480 100644 --- a/BetterJoyForCemu/HIDapi.cs +++ b/BetterJoyForCemu/HIDapi.cs @@ -4,7 +4,7 @@ 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 e5cdfa29..4dae7990 100644 --- a/BetterJoyForCemu/Joycon.cs +++ b/BetterJoyForCemu/Joycon.cs @@ -12,1378 +12,1378 @@ 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 acbf311e..a8975ef9 100644 --- a/BetterJoyForCemu/MainForm.cs +++ b/BetterJoyForCemu/MainForm.cs @@ -171,12 +171,12 @@ public void conBtnClick(object sender, EventArgs e) { 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 5de6a5dc..8771cbfd 100644 --- a/BetterJoyForCemu/Program.cs +++ b/BetterJoyForCemu/Program.cs @@ -17,522 +17,522 @@ 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 d58a1927..cd691ccc 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 122e3d8e..df60f532 100644 --- a/BetterJoyForCemu/Reassign.cs +++ b/BetterJoyForCemu/Reassign.cs @@ -29,7 +29,7 @@ public Reassign() { 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 @@ private void Remap(object sender, MouseEventArgs e) { 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 6c571dc7..e5ea4c41 100644 --- a/BetterJoyForCemu/UpdServer.cs +++ b/BetterJoyForCemu/UpdServer.cs @@ -7,509 +7,509 @@ 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; + } + } }