Formatting clean-up and calibration addition cleanup.

This commit is contained in:
David Khachaturov 2019-04-27 15:21:06 +01:00
parent fa926f479f
commit a12518886c
7 changed files with 1215 additions and 1259 deletions

View file

@ -44,8 +44,9 @@
<!-- Should prevent any more issues of the controller being unusable after the program (even though this can be fixed if you read the README) -->
<!-- Default: true -->
<add key="PurgeAffectedDevices" value="true" />
<!-- Determines whether or not the program will use mannually calibrate gyro data for non-original controller. -->
<!-- When "true", click "Calibrate" button once to get gyro calibrate data.-->
<!-- Determines whether or not to enable (experimental - currently default controller to pro) support for 3rd-party controllers. Adds a "Calibrate" button. -->
<!-- When "true", click "Calibrate" button once to get gyro calibrate data .-->
<!-- When enabled, can only calibrate one controller at a time. -->
<!-- Default: false -->
<add key="NonOriginalController" value="false" />
</appSettings>

View file

@ -6,85 +6,89 @@ using System.Text;
using System.Threading.Tasks;
namespace BetterJoyForCemu {
public static class Config { // stores dynamic configuration, including
const string PATH = "settings";
static Dictionary<string, bool> variables = new Dictionary<string, bool>();
public static class Config { // stores dynamic configuration, including
const string PATH = "settings";
static Dictionary<string, bool> variables = new Dictionary<string, bool>();
public static void Init(List<KeyValuePair<string, float[]>> caliData) {
variables["ProgressiveScan"] = true;
variables["StartInTray"] = false;
const int settingsNum = 2; // currently - ProgressiveScan, StartInTray
if (File.Exists(PATH)) {
using (StreamReader file = new StreamReader(PATH)) {
string line = String.Empty;
int lineNO = 0;
while ((line = file.ReadLine()) != null) {
string[] vs = line.Split();
try {
if (lineNO < 2){
variables[vs[0]] = Boolean.Parse(vs[1]);
} else {
caliData.Clear();
for (int i = 0; i < vs.Length; i++){
string[] caliArr = vs[i].Split(',');
float[] newArr = new float[6];
for (int j = 1; j < caliArr.Length; j++){
newArr[j-1] = float.Parse(caliArr[j]);
}
caliData.Add(new KeyValuePair<string, float[]>(
caliArr[0],
newArr
));
}
}
} catch { }
lineNO++;
}
}
} else {
using (StreamWriter file = new StreamWriter(PATH)) {
foreach (string k in variables.Keys)
file.WriteLine(String.Format("{0} {1}", k, variables[k]));
string caliStr = "";
for(int i = 0; i < caliData.Count; i++){
string space = " ";
if (i == 0) space = "";
caliStr += space + caliData[i].Key + "," + String.Join(",", caliData[i].Value);
}
file.WriteLine(caliStr);
}
}
}
public static void Init(List<KeyValuePair<string, float[]>> caliData) {
variables["ProgressiveScan"] = true;
variables["StartInTray"] = false;
public static bool Value(string key) {
if (!variables.ContainsKey("ProgressiveScan") && !variables.ContainsKey("StartInTray")) {
return false;
}
return variables[key];
}
if (File.Exists(PATH)) {
using (StreamReader file = new StreamReader(PATH)) {
string line = String.Empty;
int lineNO = 0;
while ((line = file.ReadLine()) != null) {
string[] vs = line.Split();
try {
if (lineNO < settingsNum) { // load in basic settings
variables[vs[0]] = Boolean.Parse(vs[1]);
} else { // load in calibration presets
caliData.Clear();
for (int i = 0; i < vs.Length; i++) {
string[] caliArr = vs[i].Split(',');
float[] newArr = new float[6];
for (int j = 1; j < caliArr.Length; j++) {
newArr[j - 1] = float.Parse(caliArr[j]);
}
caliData.Add(new KeyValuePair<string, float[]>(
caliArr[0],
newArr
));
}
}
} catch { }
lineNO++;
}
}
} else {
using (StreamWriter file = new StreamWriter(PATH)) {
foreach (string k in variables.Keys)
file.WriteLine(String.Format("{0} {1}", k, variables[k]));
string caliStr = "";
for (int i = 0; i < caliData.Count; i++) {
string space = " ";
if (i == 0) space = "";
caliStr += space + caliData[i].Key + "," + String.Join(",", caliData[i].Value);
}
file.WriteLine(caliStr);
}
}
}
public static void SaveCaliData(List<KeyValuePair<string, float[]>> caliData){
string[] txt = File.ReadAllLines(PATH);
string caliStr = "";
for (int i = 0; i < caliData.Count; i++){
string space = " ";
if (i == 0) space = "";
caliStr += space + caliData[i].Key + "," + String.Join(",", caliData[i].Value);
}
txt[2] = caliStr;
File.WriteAllLines(PATH, txt);
}
public static bool Value(string key) {
if (!variables.ContainsKey("ProgressiveScan") && !variables.ContainsKey("StartInTray")) {
return false;
}
return variables[key];
}
public static void Save(string key, bool value) {
variables[key] = value;
string[] txt = File.ReadAllLines(PATH);
int NO = 0;
foreach (string k in variables.Keys)
{
txt[NO] = String.Format("{0} {1}", k, variables[k]);
NO++;
}
File.WriteAllLines(PATH, txt);
}
}
public static void SaveCaliData(List<KeyValuePair<string, float[]>> caliData) {
string[] txt = File.ReadAllLines(PATH);
if (txt.Length < settingsNum + 1) // no custom calibrations yet
Array.Resize(ref txt, txt.Length + 1);
string caliStr = "";
for (int i = 0; i < caliData.Count; i++) {
string space = " ";
if (i == 0) space = "";
caliStr += space + caliData[i].Key + "," + String.Join(",", caliData[i].Value);
}
txt[2] = caliStr;
File.WriteAllLines(PATH, txt);
}
public static void Save(string key, bool value) {
variables[key] = value;
string[] txt = File.ReadAllLines(PATH);
int NO = 0;
foreach (string k in variables.Keys) {
txt[NO] = String.Format("{0} {1}", k, variables[k]);
NO++;
}
File.WriteAllLines(PATH, txt);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -331,10 +331,6 @@
this.AutoCalibrate.Text = "Calibrate";
this.AutoCalibrate.UseVisualStyleBackColor = true;
this.AutoCalibrate.Click += new System.EventHandler(this.StartCalibrate);
if (!this.nonOriginal)
{
this.AutoCalibrate.Hide();
}
//
// MainForm
//

View file

@ -15,391 +15,367 @@ using System.Xml.Linq;
namespace BetterJoyForCemu {
public partial class MainForm : Form {
public bool nonOriginal = Boolean.Parse(ConfigurationManager.AppSettings["NonOriginalController"]);
public List<Button> con, loc;
public bool calibrate;
public List<KeyValuePair<string,float[]>> caliData;
private Timer countDown;
private int count;
public List<int> xG;
public List<int> yG;
public List<int> zG;
public List<int> xA;
public List<int> yA;
public List<int> zA;
public bool nonOriginal = Boolean.Parse(ConfigurationManager.AppSettings["NonOriginalController"]);
public List<Button> con, loc;
public bool calibrate;
public List<KeyValuePair<string, float[]>> caliData;
private Timer countDown;
private int count;
public List<int> xG, yG, zG, xA, yA, zA;
public MainForm() {
xG = new List<int>();
yG = new List<int>();
zG = new List<int>();
xA = new List<int>();
yA = new List<int>();
zA = new List<int>();
caliData = new List<KeyValuePair<string, float[]>> {
new KeyValuePair<string, float[]>("0", new float[6] {0,0,0,-710,0,0})
};
xG = new List<int>(); yG = new List<int>(); zG = new List<int>();
xA = new List<int>(); yA = new List<int>(); zA = new List<int>();
caliData = new List<KeyValuePair<string, float[]>> {
new KeyValuePair<string, float[]>("0", new float[6] {0,0,0,-710,0,0})
};
InitializeComponent();
con = new List<Button> { con1, con2, con3, con4 };
loc = new List<Button> { loc1, loc2, loc3, loc4 };
if (!nonOriginal)
AutoCalibrate.Hide();
//list all options
string[] myConfigs = ConfigurationManager.AppSettings.AllKeys;
Size childSize = new Size(87, 20);
for (int i = 0; i != myConfigs.Length; i++)
{
tableLayoutPanel1.RowCount++;
tableLayoutPanel1.Controls.Add(new Label() { Text = myConfigs[i], TextAlign=ContentAlignment.BottomLeft, AutoEllipsis=true, Size = childSize }, 0, i);
con = new List<Button> { con1, con2, con3, con4 };
loc = new List<Button> { loc1, loc2, loc3, loc4 };
var value = ConfigurationManager.AppSettings[myConfigs[i]];
Control childControl;
if (value == "true" || value == "false")
{
childControl = new CheckBox() { Checked = Boolean.Parse(value), Size= childSize };
}
else
{
childControl = new TextBox() { Text = value, Size = childSize };
}
//list all options
string[] myConfigs = ConfigurationManager.AppSettings.AllKeys;
Size childSize = new Size(87, 20);
for (int i = 0; i != myConfigs.Length; i++) {
tableLayoutPanel1.RowCount++;
tableLayoutPanel1.Controls.Add(new Label() { Text = myConfigs[i], TextAlign = ContentAlignment.BottomLeft, AutoEllipsis = true, Size = childSize }, 0, i);
tableLayoutPanel1.Controls.Add(childControl, 1, i);
childControl.MouseClick += cbBox_Changed;
}
}
var value = ConfigurationManager.AppSettings[myConfigs[i]];
Control childControl;
if (value == "true" || value == "false") {
childControl = new CheckBox() { Checked = Boolean.Parse(value), Size = childSize };
} else {
childControl = new TextBox() { Text = value, Size = childSize };
}
private void HideToTray() {
this.WindowState = FormWindowState.Minimized;
notifyIcon.Visible = true;
notifyIcon.ShowBalloonTip(1);
this.ShowInTaskbar = false;
this.Hide();
}
tableLayoutPanel1.Controls.Add(childControl, 1, i);
childControl.MouseClick += cbBox_Changed;
}
}
private void ShowFromTray() {
this.Show();
this.WindowState = FormWindowState.Normal;
this.ShowInTaskbar = true;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.Icon = Properties.Resources.betterjoyforcemu_icon;
notifyIcon.Visible = false;
}
private void HideToTray() {
this.WindowState = FormWindowState.Minimized;
notifyIcon.Visible = true;
notifyIcon.ShowBalloonTip(1);
this.ShowInTaskbar = false;
this.Hide();
}
private void ShowFromTray() {
this.Show();
this.WindowState = FormWindowState.Normal;
this.ShowInTaskbar = true;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.Icon = Properties.Resources.betterjoyforcemu_icon;
notifyIcon.Visible = false;
}
private void MainForm_Resize(object sender, EventArgs e) {
if (this.WindowState == FormWindowState.Minimized) {
HideToTray();
HideToTray();
}
}
private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e) {
ShowFromTray();
ShowFromTray();
}
private void MainForm_Load(object sender, EventArgs e) {
Config.Init(caliData);
Config.Init(caliData);
Program.Start();
Program.Start();
passiveScanBox.Checked = Config.Value("ProgressiveScan");
startInTrayBox.Checked = Config.Value("StartInTray");
passiveScanBox.Checked = Config.Value("ProgressiveScan");
startInTrayBox.Checked = Config.Value("StartInTray");
if (Config.Value("StartInTray")) {
HideToTray();
}
else {
ShowFromTray();
}
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
try {
Program.Stop();
Environment.Exit(0);
} catch { }
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e) { // this does not work, for some reason. Fix before release
try {
Program.Stop();
Close();
Environment.Exit(0);
} catch { }
if (Config.Value("StartInTray")) {
HideToTray();
} else {
ShowFromTray();
}
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
linkLabel1.LinkVisited = true;
System.Diagnostics.Process.Start("http://paypal.me/DavidKhachaturov/5");
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
try {
Program.Stop();
Environment.Exit(0);
} catch { }
}
private void passiveScanBox_CheckedChanged(object sender, EventArgs e) {
Config.Save("ProgressiveScan", passiveScanBox.Checked);
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e) { // this does not work, for some reason. Fix before release
try {
Program.Stop();
Close();
Environment.Exit(0);
} catch { }
}
public void AppendTextBox(string value) { // https://stackoverflow.com/questions/519233/writing-to-a-textbox-from-another-thread
if (InvokeRequired) {
this.Invoke(new Action<string>(AppendTextBox), new object[] { value });
return;
}
console.AppendText(value);
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
linkLabel1.LinkVisited = true;
System.Diagnostics.Process.Start("http://paypal.me/DavidKhachaturov/5");
}
bool toRumble = Boolean.Parse(ConfigurationManager.AppSettings["EnableRumble"]);
bool showAsXInput = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsXInput"]);
private void passiveScanBox_CheckedChanged(object sender, EventArgs e) {
Config.Save("ProgressiveScan", passiveScanBox.Checked);
}
public void locBtnClick(object sender, EventArgs e) {
Button bb = sender as Button;
public void AppendTextBox(string value) { // https://stackoverflow.com/questions/519233/writing-to-a-textbox-from-another-thread
if (InvokeRequired) {
this.Invoke(new Action<string>(AppendTextBox), new object[] { value });
return;
}
console.AppendText(value);
}
if (bb.Tag.GetType() == typeof(Button)) {
Button button = bb.Tag as Button;
bool toRumble = Boolean.Parse(ConfigurationManager.AppSettings["EnableRumble"]);
bool showAsXInput = Boolean.Parse(ConfigurationManager.AppSettings["ShowAsXInput"]);
if (button.Tag.GetType() == typeof(Joycon)) {
Joycon v = (Joycon) button.Tag;
v.SetRumble(20.0f, 400.0f, 1.0f, 300);
}
}
}
public void locBtnClick(object sender, EventArgs e) {
Button bb = sender as Button;
public void conBtnClick(object sender, EventArgs e) {
Button button = sender as Button;
if (bb.Tag.GetType() == typeof(Button)) {
Button button = bb.Tag as Button;
if (button.Tag.GetType() == typeof(Joycon)) {
Joycon v = (Joycon)button.Tag;
if (button.Tag.GetType() == typeof(Joycon)) {
Joycon v = (Joycon)button.Tag;
v.SetRumble(20.0f, 400.0f, 1.0f, 300);
}
}
}
if (v.other == null && !v.isPro) { // needs connecting to other joycon (so messy omg)
bool succ = false;
foreach (Joycon jc in Program.mgr.j) {
if (!jc.isPro && jc.isLeft != v.isLeft && jc != v && jc.other == null) {
v.other = jc;
jc.other = v;
public void conBtnClick(object sender, EventArgs e) {
Button button = sender as Button;
//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.SetLED(led);
v.SetLED(led);
if (button.Tag.GetType() == typeof(Joycon)) {
Joycon v = (Joycon)button.Tag;
v.xin.Dispose();
v.xin = null;
if (v.other == null && !v.isPro) { // needs connecting to other joycon (so messy omg)
bool succ = false;
foreach (Joycon jc in Program.mgr.j) {
if (!jc.isPro && jc.isLeft != v.isLeft && jc != v && jc.other == null) {
v.other = jc;
jc.other = v;
foreach (Button b in con)
if (b.Tag == jc)
b.BackgroundImage = jc.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
//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.SetLED(led);
v.SetLED(led);
succ = true;
break;
}
}
v.xin.Dispose();
v.xin = null;
if (succ)
foreach (Button b in con)
if (b.Tag == v)
b.BackgroundImage = v.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
} else if (v.other != null && !v.isPro) { // needs disconnecting from other joycon
if (v.xin == null) {
ReenableXinput(v);
v.xin.Connect();
}
foreach (Button b in con)
if (b.Tag == jc)
b.BackgroundImage = jc.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
if (v.other.xin == null) {
ReenableXinput(v.other);
v.other.xin.Connect();
}
succ = true;
break;
}
}
button.BackgroundImage = v.isLeft ? Properties.Resources.jc_left_s : Properties.Resources.jc_right_s;
if (succ)
foreach (Button b in con)
if (b.Tag == v)
b.BackgroundImage = v.isLeft ? Properties.Resources.jc_left : Properties.Resources.jc_right;
} else if (v.other != null && !v.isPro) { // needs disconnecting from other joycon
if (v.xin == null) {
ReenableXinput(v);
v.xin.Connect();
}
foreach (Button b in con)
if (b.Tag == v.other)
b.BackgroundImage = v.other.isLeft ? Properties.Resources.jc_left_s : Properties.Resources.jc_right_s;
if (v.other.xin == null) {
ReenableXinput(v.other);
v.other.xin.Connect();
}
//Set original Joycon LEDs
v.other.LED = (byte)(0x1 << v.other.PadId);
v.LED = (byte)(0x1 << v.PadId);
v.other.SetLED(v.other.LED);
v.SetLED(v.LED);
button.BackgroundImage = v.isLeft ? Properties.Resources.jc_left_s : Properties.Resources.jc_right_s;
v.other.other = null;
v.other = null;
}
}
}
foreach (Button b in con)
if (b.Tag == v.other)
b.BackgroundImage = v.other.isLeft ? Properties.Resources.jc_left_s : Properties.Resources.jc_right_s;
private void startInTrayBox_CheckedChanged(object sender, EventArgs e)
{
Config.Save("StartInTray", startInTrayBox.Checked);
}
//Set original Joycon LEDs
v.other.LED = (byte)(0x1 << v.other.PadId);
v.LED = (byte)(0x1 << v.PadId);
v.other.SetLED(v.other.LED);
v.SetLED(v.LED);
private void btn_open3rdP_Click(object sender, EventArgs e) {
_3rdPartyControllers partyForm = new _3rdPartyControllers();
partyForm.ShowDialog();
}
v.other.other = null;
v.other = null;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Application.Restart();
Environment.Exit(0);
}
private void startInTrayBox_CheckedChanged(object sender, EventArgs e) {
Config.Save("StartInTray", startInTrayBox.Checked);
}
void ReenableXinput(Joycon v) {
if (showAsXInput) {
v.xin = new Xbox360Controller(Program.emClient);
private void btn_open3rdP_Click(object sender, EventArgs e) {
_3rdPartyControllers partyForm = new _3rdPartyControllers();
partyForm.ShowDialog();
}
if (toRumble)
v.xin.FeedbackReceived += v.ReceiveRumble;
v.report = new Xbox360Report();
}
}
private void button1_Click(object sender, EventArgs e) {
Application.Restart();
Environment.Exit(0);
}
private void label2_Click(object sender, EventArgs e)
{
rightPanel.Visible = !rightPanel.Visible;
foldLbl.Text = rightPanel.Visible ? "<" : ">";
}
void ReenableXinput(Joycon v) {
if (showAsXInput) {
v.xin = new Xbox360Controller(Program.emClient);
private void cbBox_Changed(object sender, EventArgs e)
{
var coord = tableLayoutPanel1.GetPositionFromControl(sender as Control);
if (toRumble)
v.xin.FeedbackReceived += v.ReceiveRumble;
v.report = new Xbox360Report();
}
}
var valCtl = tableLayoutPanel1.GetControlFromPosition(coord.Column, coord.Row);
var KeyCtl = tableLayoutPanel1.GetControlFromPosition(coord.Column - 1, coord.Row).Text;
private void label2_Click(object sender, EventArgs e) {
rightPanel.Visible = !rightPanel.Visible;
foldLbl.Text = rightPanel.Visible ? "<" : ">";
}
try
{
var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var settings = configFile.AppSettings.Settings;
if (sender.GetType() == typeof(CheckBox) && settings[KeyCtl] != null)
{
settings[KeyCtl].Value = ((CheckBox)valCtl).Checked.ToString().ToLower();
}
else if (sender.GetType() == typeof(TextBox) && settings[KeyCtl] != null)
{
settings[KeyCtl].Value = ((TextBox)valCtl).Text.ToLower();
}
configFile.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(configFile.AppSettings.SectionInformation.Name);
}
catch (ConfigurationErrorsException)
{
Console.WriteLine("Error writing app settings");
Trace.WriteLine(String.Format("rw {0}, column {1}, {2}, {3}", coord.Row, coord.Column, sender.GetType(), KeyCtl));
}
}
private void StartCalibrate(object sender, EventArgs e){
if(Program.mgr.j.Count == 0){
this.console.Text = "Please connect to a controller.";
return;
}
if (Program.mgr.j.Count > 1){
this.console.Text = "Please only use one controller.";
return;
}
this.AutoCalibrate.Enabled = false;
countDown = new Timer();
this.count = 4;
this.CountDown(null, null);
countDown.Tick += new EventHandler(CountDown);
countDown.Interval = 1000;
countDown.Enabled = true;
}
private void cbBox_Changed(object sender, EventArgs e) {
var coord = tableLayoutPanel1.GetPositionFromControl(sender as Control);
private void StartGetData(){
this.xG.Clear();
this.yG.Clear();
this.zG.Clear();
this.xA.Clear();
this.yA.Clear();
this.zA.Clear();
countDown = new Timer();
this.count = 3;
this.calibrate = true;
countDown.Tick += new EventHandler(CalcData);
countDown.Interval = 1000;
countDown.Enabled = true;
}
var valCtl = tableLayoutPanel1.GetControlFromPosition(coord.Column, coord.Row);
var KeyCtl = tableLayoutPanel1.GetControlFromPosition(coord.Column - 1, coord.Row).Text;
private void CountDown(object sender, EventArgs e){
if(this.count == 0){
this.console.Text = "Calibrating...";
countDown.Stop();
this.StartGetData();
} else {
this.console.Text = "Plese keep controller flat." + "\r\n";
this.console.Text += "Calibrate will start in " + this.count + " seconds.";
this.count--;
}
}
private void CalcData(object sender, EventArgs e){
if (this.count == 0){
countDown.Stop();
this.calibrate = false;
string serNum = Program.mgr.j.First().serial_number;
int serIndex = this.findSer(serNum);
float[] Arr = new float[6] { 0,0,0,0,0,0};
if(serIndex == -1){
this.caliData.Add(new KeyValuePair<string, float[]>(
serNum,
Arr
));
} else {
Arr = this.caliData[serIndex].Value;
}
Random rnd = new Random();
Arr[0] = (float)quickselect_median(this.xG, rnd.Next);
Arr[1] = (float)quickselect_median(this.yG, rnd.Next);
Arr[2] = (float)quickselect_median(this.zG, rnd.Next);
Arr[3] = (float)quickselect_median(this.xA, rnd.Next);
Arr[4] = (float)quickselect_median(this.yA, rnd.Next);
Arr[5] = (float)quickselect_median(this.zA, rnd.Next) - 4010; //Joycon.cs acc_sen 16384
this.console.Text = "Calibrate completed!!!" + "\r\n";
Config.SaveCaliData(this.caliData);
Program.mgr.j.First().getActiveData();
this.AutoCalibrate.Enabled = true;
} else {
this.count--;
}
try {
var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var settings = configFile.AppSettings.Settings;
if (sender.GetType() == typeof(CheckBox) && settings[KeyCtl] != null) {
settings[KeyCtl].Value = ((CheckBox)valCtl).Checked.ToString().ToLower();
} else if (sender.GetType() == typeof(TextBox) && settings[KeyCtl] != null) {
settings[KeyCtl].Value = ((TextBox)valCtl).Text.ToLower();
}
configFile.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(configFile.AppSettings.SectionInformation.Name);
} catch (ConfigurationErrorsException) {
Console.WriteLine("Error writing app settings");
Trace.WriteLine(String.Format("rw {0}, column {1}, {2}, {3}", coord.Row, coord.Column, sender.GetType(), KeyCtl));
}
}
private void StartCalibrate(object sender, EventArgs e) {
if (Program.mgr.j.Count == 0) {
this.console.Text = "Please connect a single pro controller.";
return;
}
if (Program.mgr.j.Count > 1) {
this.console.Text = "Please calibrate one controller at a time (disconnect others).";
return;
}
this.AutoCalibrate.Enabled = false;
countDown = new Timer();
this.count = 4;
this.CountDown(null, null);
countDown.Tick += new EventHandler(CountDown);
countDown.Interval = 1000;
countDown.Enabled = true;
}
}
private double quickselect_median(List<int> l, Func<int,int> pivot_fn){
int ll = l.Count;
if (ll % 2 == 1){
return this.quickselect(l, ll / 2, pivot_fn);
} else {
return 0.5 * (quickselect(l, ll / 2 - 1, pivot_fn) + quickselect(l, ll / 2, pivot_fn));
}
}
private void StartGetData() {
this.xG.Clear(); this.yG.Clear(); this.zG.Clear();
this.xA.Clear(); this.yA.Clear(); this.zA.Clear();
countDown = new Timer();
this.count = 3;
this.calibrate = true;
countDown.Tick += new EventHandler(CalcData);
countDown.Interval = 1000;
countDown.Enabled = true;
}
private int quickselect(List<int> l, int k, Func<int,int> pivot_fn){
if (l.Count == 1 && k == 0){
return l[0];
}
int pivot = l[pivot_fn(l.Count)];
List<int> lows = l.Where(x => x < pivot).ToList();
List<int> highs = l.Where(x => x > pivot).ToList();
List<int> pivots = l.Where(x => x == pivot).ToList();
if (k < lows.Count){
return quickselect(lows, k, pivot_fn);
} else if(k < (lows.Count + pivots.Count)){
return pivots[0];
} else {
return quickselect(highs, k - lows.Count - pivots.Count, pivot_fn);
}
}
private void CountDown(object sender, EventArgs e) {
if (this.count == 0) {
this.console.Text = "Calibrating...";
countDown.Stop();
this.StartGetData();
} else {
this.console.Text = "Plese keep the controller flat." + "\r\n";
this.console.Text += "Calibration will start in " + this.count + " seconds.";
this.count--;
}
}
private void CalcData(object sender, EventArgs e) {
if (this.count == 0) {
countDown.Stop();
this.calibrate = false;
string serNum = Program.mgr.j.First().serial_number;
int serIndex = this.findSer(serNum);
float[] Arr = new float[6] { 0, 0, 0, 0, 0, 0 };
if (serIndex == -1) {
this.caliData.Add(new KeyValuePair<string, float[]>(
serNum,
Arr
));
} else {
Arr = this.caliData[serIndex].Value;
}
Random rnd = new Random();
Arr[0] = (float)quickselect_median(this.xG, rnd.Next);
Arr[1] = (float)quickselect_median(this.yG, rnd.Next);
Arr[2] = (float)quickselect_median(this.zG, rnd.Next);
Arr[3] = (float)quickselect_median(this.xA, rnd.Next);
Arr[4] = (float)quickselect_median(this.yA, rnd.Next);
Arr[5] = (float)quickselect_median(this.zA, rnd.Next) - 4010; //Joycon.cs acc_sen 16384
this.console.Text += "Calibration completed!!!" + "\r\n";
Config.SaveCaliData(this.caliData);
Program.mgr.j.First().getActiveData();
this.AutoCalibrate.Enabled = true;
} else {
this.count--;
}
public float[] activeCaliData(string serNum){
for (int i = 0; i < this.caliData.Count; i++){
if (this.caliData[i].Key == serNum){
return this.caliData[i].Value;
}
}
return this.caliData[0].Value;
}
}
private double quickselect_median(List<int> l, Func<int, int> pivot_fn) {
int ll = l.Count;
if (ll % 2 == 1) {
return this.quickselect(l, ll / 2, pivot_fn);
} else {
return 0.5 * (quickselect(l, ll / 2 - 1, pivot_fn) + quickselect(l, ll / 2, pivot_fn));
}
}
private int findSer(string serNum){
for(int i = 0; i < this.caliData.Count; i++){
if(this.caliData[i].Key== serNum){
return i;
}
}
return -1;
}
}
private int quickselect(List<int> l, int k, Func<int, int> pivot_fn) {
if (l.Count == 1 && k == 0) {
return l[0];
}
int pivot = l[pivot_fn(l.Count)];
List<int> lows = l.Where(x => x < pivot).ToList();
List<int> highs = l.Where(x => x > pivot).ToList();
List<int> pivots = l.Where(x => x == pivot).ToList();
if (k < lows.Count) {
return quickselect(lows, k, pivot_fn);
} else if (k < (lows.Count + pivots.Count)) {
return pivots[0];
} else {
return quickselect(highs, k - lows.Count - pivots.Count, pivot_fn);
}
}
public float[] activeCaliData(string serNum) {
for (int i = 0; i < this.caliData.Count; i++) {
if (this.caliData[i].Key == serNum) {
return this.caliData[i].Value;
}
}
return this.caliData[0].Value;
}
private int findSer(string serNum) {
for (int i = 0; i < this.caliData.Count; i++) {
if (this.caliData[i].Key == serNum) {
return i;
}
}
return -1;
}
}
}

View file

@ -24,412 +24,410 @@ using System.Windows.Forms;
using System.ServiceProcess;
namespace BetterJoyForCemu {
public class JoyconManager {
public bool EnableIMU = true;
public bool EnableLocalize = false;
private const ushort vendor_id = 0x57e;
private const ushort vendor_id_ = 0x057e;
private const ushort product_l = 0x2006;
private const ushort product_r = 0x2007;
private const ushort product_pro = 0x2009;
public List<Joycon> j; // Array of all connected Joy-Cons
static JoyconManager instance;
public MainForm form;
System.Timers.Timer controllerCheck;
bool useHIDG = Boolean.Parse(ConfigurationManager.AppSettings["UseHIDG"]);
public static JoyconManager Instance {
get { return instance; }
}
public void Awake() {
instance = this;
j = new List<Joycon>();
HIDapi.hid_init();
}
public void Start() {
controllerCheck = new System.Timers.Timer(2000); // check 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<Joycon> rem = new List<Joycon>();
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(); 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 to list. Can be reconnected.\r\n");
}
}
foreach (Joycon v in rem)
j.Remove(v);
}
void CheckForNewControllersTime(Object source, ElapsedEventArgs e) {
if (Config.Value("ProgressiveScan")) {
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 (form.nonOriginal)
{
enumerate.product_id = product_pro;
}
if ((enumerate.product_id == product_l || enumerate.product_id == product_r || enumerate.product_id == product_pro) && !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;
default:
form.AppendTextBox("Non Joy-Con Nintendo input device skipped.\r\n"); break;
}
// Add controller to block-list for HidGuardian
if (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");
}
} else { // Remove affected devices from list
try {
HttpWebResponse r1 = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/affected/purge/").GetResponse();
} catch { }
}
// -------------------- //
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;
}
j.Add(new Joycon(handle, EnableIMU, EnableLocalize & EnableIMU, 0.05f, isLeft, enumerate.path, enumerate.serial_number, j.Count, enumerate.product_id == product_pro));
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;
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.SetLED(led);
v.SetLED(led);
temp.xin.Dispose();
temp.xin = 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.xin != null)
jc.xin.Connect();
jc.Attach(leds_: jc.LED);
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) {
v.Detach();
if (v.xin != null) {
v.xin.Disconnect();
v.xin.Dispose();
}
}
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 bool useHIDG = Boolean.Parse(ConfigurationManager.AppSettings["UseHIDG"]);
public static void Start() {
pid = Process.GetCurrentProcess().Id.ToString(); // get current process id for HidCerberus.Srv
if (useHIDG) {
try {
var HidCerberusService = new ServiceController("HidCerberus Service");
if (HidCerberusService.Status == ServiceControllerStatus.Stopped) {
form.console.Text += "HidGuardian was stopped. Starting...\r\n";
try {
HidCerberusService.Start();
} catch (Exception e) {
form.console.Text += "Unable to start HidGuardian - everything should work fine without it, but if you need it, run the app again as an admin.\r\n";
}
}
} catch (Exception e) {
form.console.Text += "Unable to start HidGuardian - everything should work fine without it, but if you need it, install it properly as admin.\r\n";
}
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.Text += "Unable to purge whitelist.\r\n";
}
}
try {
response = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/whitelist/add/" + pid).GetResponse(); // add BetterJoyForCemu to allowed processes
} catch (Exception e) {
form.console.Text += "Unable to add program to whitelist.\r\n";
}
} else {
form.console.Text += "HidGuardian is disabled.\r\n";
}
emClient = new ViGEmClient(); // Manages emulated XInput
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) {
// Get local BT host MAC
if (nic.NetworkInterfaceType != NetworkInterfaceType.FastEthernetFx && nic.NetworkInterfaceType != NetworkInterfaceType.Wireless80211) {
if (nic.Name.Split()[0] == "Bluetooth") {
btMAC = nic.GetPhysicalAddress();
}
}
}
mgr = new JoyconManager();
mgr.form = form;
mgr.Awake();
mgr.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();
form.console.Text += "All systems go\r\n";
}
public static void Stop() {
try {
HttpWebResponse response = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/whitelist/remove/" + pid).GetResponse();
} catch (Exception e) {
form.console.Text += "Unable to remove program from whitelist.\r\n";
}
if (Boolean.Parse(ConfigurationManager.AppSettings["PurgeAffectedDevices"])) {
try {
HttpWebResponse r1 = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/affected/purge/").GetResponse();
} catch { }
}
server.Stop();
timer.Stop();
mgr.OnApplicationQuit();
form.console.Text += "";
}
static void Main(string[] args) {
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;
public List<Joycon> j; // Array of all connected Joy-Cons
static JoyconManager instance;
public MainForm form;
System.Timers.Timer controllerCheck;
bool useHIDG = Boolean.Parse(ConfigurationManager.AppSettings["UseHIDG"]);
public static JoyconManager Instance {
get { return instance; }
}
public void Awake() {
instance = this;
j = new List<Joycon>();
HIDapi.hid_init();
}
public void Start() {
controllerCheck = new System.Timers.Timer(2000); // check 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<Joycon> rem = new List<Joycon>();
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(); 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 to list. Can be reconnected.\r\n");
}
}
foreach (Joycon v in rem)
j.Remove(v);
}
void CheckForNewControllersTime(Object source, ElapsedEventArgs e) {
if (Config.Value("ProgressiveScan")) {
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 (form.nonOriginal) {
enumerate.product_id = product_pro;
}
if ((enumerate.product_id == product_l || enumerate.product_id == product_r || enumerate.product_id == product_pro) && !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;
default:
form.AppendTextBox("Non Joy-Con Nintendo input device skipped.\r\n"); break;
}
// Add controller to block-list for HidGuardian
if (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");
}
} else { // Remove affected devices from list
try {
HttpWebResponse r1 = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/affected/purge/").GetResponse();
} catch { }
}
// -------------------- //
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;
}
j.Add(new Joycon(handle, EnableIMU, EnableLocalize & EnableIMU, 0.05f, isLeft, enumerate.path, enumerate.serial_number, j.Count, enumerate.product_id == product_pro));
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;
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.SetLED(led);
v.SetLED(led);
temp.xin.Dispose();
temp.xin = 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.xin != null)
jc.xin.Connect();
jc.Attach(leds_: jc.LED);
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) {
v.Detach();
if (v.xin != null) {
v.xin.Disconnect();
v.xin.Dispose();
}
}
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 bool useHIDG = Boolean.Parse(ConfigurationManager.AppSettings["UseHIDG"]);
public static void Start() {
pid = Process.GetCurrentProcess().Id.ToString(); // get current process id for HidCerberus.Srv
if (useHIDG) {
try {
var HidCerberusService = new ServiceController("HidCerberus Service");
if (HidCerberusService.Status == ServiceControllerStatus.Stopped) {
form.console.Text += "HidGuardian was stopped. Starting...\r\n";
try {
HidCerberusService.Start();
} catch (Exception e) {
form.console.Text += "Unable to start HidGuardian - everything should work fine without it, but if you need it, run the app again as an admin.\r\n";
}
}
} catch (Exception e) {
form.console.Text += "Unable to start HidGuardian - everything should work fine without it, but if you need it, install it properly as admin.\r\n";
}
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.Text += "Unable to purge whitelist.\r\n";
}
}
try {
response = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/whitelist/add/" + pid).GetResponse(); // add BetterJoyForCemu to allowed processes
} catch (Exception e) {
form.console.Text += "Unable to add program to whitelist.\r\n";
}
} else {
form.console.Text += "HidGuardian is disabled.\r\n";
}
emClient = new ViGEmClient(); // Manages emulated XInput
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) {
// Get local BT host MAC
if (nic.NetworkInterfaceType != NetworkInterfaceType.FastEthernetFx && nic.NetworkInterfaceType != NetworkInterfaceType.Wireless80211) {
if (nic.Name.Split()[0] == "Bluetooth") {
btMAC = nic.GetPhysicalAddress();
}
}
}
mgr = new JoyconManager();
mgr.form = form;
mgr.Awake();
mgr.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();
form.console.Text += "All systems go\r\n";
}
public static void Stop() {
try {
HttpWebResponse response = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/whitelist/remove/" + pid).GetResponse();
} catch (Exception e) {
form.console.Text += "Unable to remove program from whitelist.\r\n";
}
if (Boolean.Parse(ConfigurationManager.AppSettings["PurgeAffectedDevices"])) {
try {
HttpWebResponse r1 = (HttpWebResponse)WebRequest.Create(@"http://localhost:26762/api/v1/hidguardian/affected/purge/").GetResponse();
} catch { }
}
server.Stop();
timer.Stop();
mgr.OnApplicationQuit();
form.console.Text += "";
}
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
form = new MainForm();
Application.Run(form);
}
}
}

View file

@ -281,7 +281,7 @@ namespace BetterJoyForCemu {
}
}
public void Start(IPAddress ip, int port=26760) {
public void Start(IPAddress ip, int port = 26760) {
if (running) {
if (udpSock != null) {
udpSock.Close();
@ -316,56 +316,56 @@ namespace BetterJoyForCemu {
}
}
bool swapAB = Boolean.Parse(ConfigurationManager.AppSettings["SwapAB"]);
bool swapXY = Boolean.Parse(ConfigurationManager.AppSettings["SwapXY"]);
private bool ReportToBuffer(Joycon hidReport, byte[] outputData, ref int outIdx) {
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(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;
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(!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;
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)));
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(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(!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;