using DevComponents.DotNetBar; using DevComponents.DotNetBar.Controls; using SharpDX.DirectInput; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Timer = System.Timers.Timer; namespace PJ64Savestater { public partial class Form1 : OfficeForm { private const string FILTER_INPUTPROFILE = "Json files (*.json)|*.json"; private const string PROFILE_CODE = "Project64 Savestater"; private const string DEFAULT_PROFILE_FILENAME = "Profile.json"; private Timer myTimer; private DirectInput DInput = new DirectInput(); private DeviceInstance[] allDevices = Array.Empty(); private Joystick curPad = null; private TextBoxX focuesTextBox = null; private InputProfile curProfile; private bool enableActionExecution = false; public Form1() { InitializeComponent(); UpdateAmbientColors(); myTimer = new Timer(1) { SynchronizingObject = this, AutoReset = true, Enabled = true }; myTimer.Elapsed += MyTimer_Elapsed; } private void LoadPads() { allDevices = DInput.GetDevices(DeviceClass.GameControl, DeviceEnumerationFlags.AttachedOnly).ToArray(); ComboBoxEx_Pads.Items.Clear(); foreach (DeviceInstance d in allDevices) ComboBoxEx_Pads.Items.Add(d.InstanceName); } private void SetCurrentPad(Guid guid) { curPad = new Joystick(DInput, guid); foreach (DeviceObjectInstance doi in curPad.GetObjects(DeviceObjectTypeFlags.Axis)) curPad.GetObjectPropertiesById(doi.ObjectId).Range = new InputRange(-5000, 5000); curPad.Properties.AxisMode = DeviceAxisMode.Absolute; curPad.SetCooperativeLevel(Handle, CooperativeLevel.NonExclusive | CooperativeLevel.Background); curPad.Acquire(); if (curProfile is object) curProfile.ControllerInstanceGuid = guid; } private static string GetDefaultProfileFilePath() { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), DEFAULT_PROFILE_FILENAME); } private void CheckForInput() { if (curPad is object) { var inputCodes = new List(); var state = new JoystickState(); curPad.Poll(); curPad.GetCurrentState(ref state); for (int i = 0, loopTo = state.Buttons.Length - 1; i <= loopTo; i++) { if (state.Buttons[i]) inputCodes.Add("Button " + i); } for (int i = 0, loopTo1 = state.PointOfViewControllers.Length - 1; i <= loopTo1; i++) { int val = state.PointOfViewControllers[i]; if (val > -1) inputCodes.Add("Point " + i + " " + val); } for (int i = 0, loopTo2 = state.Sliders.Length - 1; i <= loopTo2; i++) { int val = state.Sliders[i]; if (val > 0) inputCodes.Add("Slider " + i + " +"); if (val < 0) inputCodes.Add("Slider " + i + " -"); } for (int i = 0, loopTo3 = state.AccelerationSliders.Length - 1; i <= loopTo3; i++) { int val = state.AccelerationSliders[i]; if (val > 0) inputCodes.Add("ASlider " + i + " +"); if (val < 0) inputCodes.Add("ASlider " + i + " -"); } for (int i = 0, loopTo4 = state.ForceSliders.Length - 1; i <= loopTo4; i++) { int val = state.ForceSliders[i]; if (val > 0) inputCodes.Add("FSlider " + i + " +"); if (val < 0) inputCodes.Add("FSlider " + i + " -"); } for (int i = 0, loopTo5 = state.VelocitySliders.Length - 1; i <= loopTo5; i++) { int val = state.VelocitySliders[i]; if (val > 0) inputCodes.Add("VSlider " + i + " +"); if (val < 0) inputCodes.Add("VSlider " + i + " -"); } if (state.X > 0) inputCodes.Add("X +"); if (state.X < 0) inputCodes.Add("X -"); if (state.Y > 0) inputCodes.Add("Y +"); if (state.Y < 0) inputCodes.Add("Y -"); if (state.Z > 0) inputCodes.Add("Z +"); if (state.Z < 0) inputCodes.Add("Z -"); if (state.AccelerationX > 0) inputCodes.Add("AX +"); if (state.AccelerationX < 0) inputCodes.Add("AX -"); if (state.AccelerationY > 0) inputCodes.Add("AY +"); if (state.AccelerationY < 0) inputCodes.Add("AY -"); if (state.AccelerationZ > 0) inputCodes.Add("AZ +"); if (state.AccelerationZ < 0) inputCodes.Add("AZ -"); if (state.AngularAccelerationX > 0) inputCodes.Add("AAX +"); if (state.AngularAccelerationX < 0) inputCodes.Add("AAX -"); if (state.AngularAccelerationY > 0) inputCodes.Add("AAY +"); if (state.AngularAccelerationY < 0) inputCodes.Add("AAY -"); if (state.AngularAccelerationZ > 0) inputCodes.Add("AAZ +"); if (state.AngularAccelerationZ < 0) inputCodes.Add("AAZ -"); if (state.ForceX > 0) inputCodes.Add("FX +"); if (state.ForceX < 0) inputCodes.Add("FX -"); if (state.ForceY > 0) inputCodes.Add("FY +"); if (state.ForceY < 0) inputCodes.Add("FY -"); if (state.ForceZ > 0) inputCodes.Add("FZ +"); if (state.ForceZ < 0) inputCodes.Add("FZ -"); if (state.RotationX > 0) inputCodes.Add("RX +"); if (state.RotationX < 0) inputCodes.Add("RX -"); if (state.RotationY > 0) inputCodes.Add("RY +"); if (state.RotationY < 0) inputCodes.Add("RY -"); if (state.RotationZ > 0) inputCodes.Add("RZ +"); if (state.RotationZ < 0) inputCodes.Add("RZ -"); if (state.TorqueX > 0) inputCodes.Add("TX +"); if (state.TorqueX < 0) inputCodes.Add("TX -"); if (state.TorqueY > 0) inputCodes.Add("TY +"); if (state.TorqueY < 0) inputCodes.Add("TY -"); if (state.TorqueZ > 0) inputCodes.Add("TZ +"); if (state.TorqueZ < 0) inputCodes.Add("TZ -"); if (state.VelocityX > 0) inputCodes.Add("VX +"); if (state.VelocityX < 0) inputCodes.Add("VX -"); if (state.VelocityY > 0) inputCodes.Add("VY +"); if (state.VelocityY < 0) inputCodes.Add("VY -"); if (state.VelocityZ > 0) inputCodes.Add("VZ +"); if (state.VelocityZ < 0) inputCodes.Add("VZ -"); if (inputCodes.Any()) { if (focuesTextBox is object) focuesTextBox.Text = inputCodes.Last(); else if (enableActionExecution) CheckForActions(inputCodes.ToArray()); } } } private void CheckForActions(string[] inputCodes) { foreach (var inputCode in inputCodes) { var inputControl = GetInputControlFromString(inputCode); foreach (var ctrl in curProfile.Controls) { if (ctrl.Value == inputControl) ExecuteAction(ctrl.Key); } } } private static void ExecuteAction(string actionCode) { if (actionCode == ActionCodes.CreateSavestate) SendKeys.Send("{F5}"); else if (actionCode == ActionCodes.LoadSavestate) SendKeys.Send("{F7}"); } private void SetAction(string actionCode, string inputCode) { if (curProfile is object) curProfile[actionCode] = GetInputControlFromString(inputCode); } public static InputControl GetInputControlFromString(string str) { var input = new InputControl(); var p = str.Split(' '); var switchExpr = p[0]; switch (switchExpr) { case "Button": input.InputKey = InputKeys.Buttons; input.KeyIndex = Convert.ToInt32(p[1]); break; case "Point": input.InputKey = InputKeys.PointOfViewControllers; input.KeyIndex = Convert.ToInt32(p[1]); input.Value = p[2]; break; case "Slider": input.InputKey = InputKeys.Sliders; input.KeyIndex = Convert.ToInt32(p[1]); break; case "ASlider": input.InputKey = InputKeys.AccelerationSliders; input.KeyIndex = Convert.ToInt32(p[1]); break; case "FSlider": input.InputKey = InputKeys.ForceSliders; input.KeyIndex = Convert.ToInt32(p[1]); break; case "VSlider": input.InputKey = InputKeys.VelocitySliders; input.KeyIndex = Convert.ToInt32(p[1]); break; case "X": input.InputKey = InputKeys.X; input.Value = p[1]; break; case "Y": input.InputKey = InputKeys.Y; input.Value = p[1]; break; case "Z": input.InputKey = InputKeys.Z; input.Value = p[1]; break; case "AX": input.InputKey = InputKeys.AccelerationX; input.Value = p[1]; break; case "AY": input.InputKey = InputKeys.AccelerationY; input.Value = p[1]; break; case "AZ": input.InputKey = InputKeys.AccelerationZ; input.Value = p[1]; break; case "AAX": input.InputKey = InputKeys.AngularAccelerationX; input.Value = p[1]; break; case "AAY": input.InputKey = InputKeys.AngularAccelerationY; input.Value = p[1]; break; case "AAZ": input.InputKey = InputKeys.AngularAccelerationZ; input.Value = p[1]; break; case "FX": input.InputKey = InputKeys.ForceX; input.Value = p[1]; break; case "FY": input.InputKey = InputKeys.ForceY; input.Value = p[1]; break; case "FZ": input.InputKey = InputKeys.ForceZ; input.Value = p[1]; break; case "RX": input.InputKey = InputKeys.RotationX; input.Value = p[1]; break; case "RY": input.InputKey = InputKeys.RotationY; input.Value = p[1]; break; case "RZ": input.InputKey = InputKeys.RotationZ; input.Value = p[1]; break; case "TX": input.InputKey = InputKeys.TorqueX; input.Value = p[1]; break; case "TY": input.InputKey = InputKeys.TorqueY; input.Value = p[1]; break; case "TZ": input.InputKey = InputKeys.TorqueZ; input.Value = p[1]; break; case "VX": input.InputKey = InputKeys.VelocityX; input.Value = p[1]; break; case "VY": input.InputKey = InputKeys.VelocityY; input.Value = p[1]; break; case "VZ": input.InputKey = InputKeys.VelocityZ; input.Value = p[1]; break; } return input; } public static string GetStringFromInputControl(InputControl input) { string str = string.Empty; if (input.InputKey is null && input.KeyIndex is null && input.Value is null) return string.Empty; switch (input.InputKey) { case var @case when @case == InputKeys.Buttons: { return $"Button {input.KeyIndex}"; } case var case1 when case1 == InputKeys.PointOfViewControllers: { return $"Point {input.KeyIndex} {input.Value}"; } case var case2 when case2 == InputKeys.Sliders: { return $"Slider {input.KeyIndex}"; } case var case3 when case3 == InputKeys.AccelerationSliders: { return $"ASlider {input.KeyIndex}"; } case var case4 when case4 == InputKeys.ForceSliders: { return $"FSlider {input.KeyIndex}"; } case var case5 when case5 == InputKeys.VelocitySliders: { return $"VSlider {input.KeyIndex}"; } case var case6 when case6 == InputKeys.X: { return $"X {input.Value}"; } case var case7 when case7 == InputKeys.Y: { return $"Y {input.Value}"; } case var case8 when case8 == InputKeys.Z: { return $"Z {input.Value}"; } case var case9 when case9 == InputKeys.AccelerationX: { return $"AX {input.Value}"; } case var case10 when case10 == InputKeys.AccelerationY: { return $"AY {input.Value}"; } case var case11 when case11 == InputKeys.AccelerationZ: { return $"AZ {input.Value}"; } case var case12 when case12 == InputKeys.AngularAccelerationX: { return $"AAX {input.Value}"; } case var case13 when case13 == InputKeys.AngularAccelerationY: { return $"AAY {input.Value}"; } case var case14 when case14 == InputKeys.AngularAccelerationZ: { return $"AAZ {input.Value}"; } case var case15 when case15 == InputKeys.ForceX: { return $"FX {input.Value}"; } case var case16 when case16 == InputKeys.ForceY: { return $"FY {input.Value}"; } case var case17 when case17 == InputKeys.ForceZ: { return $"FZ {input.Value}"; } case var case18 when case18 == InputKeys.RotationX: { return $"RX {input.Value}"; } case var case19 when case19 == InputKeys.RotationY: { return $"RY {input.Value}"; } case var case20 when case20 == InputKeys.RotationZ: { return $"RZ {input.Value}"; } case var case21 when case21 == InputKeys.TorqueX: { return $"TX {input.Value}"; } case var case22 when case22 == InputKeys.TorqueY: { return $"TY {input.Value}"; } case var case23 when case23 == InputKeys.TorqueZ: { return $"TZ {input.Value}"; } case var case24 when case24 == InputKeys.VelocityX: { return $"VX {input.Value}"; } case var case25 when case25 == InputKeys.VelocityY: { return $"VY {input.Value}"; } case var case26 when case26 == InputKeys.VelocityZ: { return $"VZ {input.Value}"; } } return str; } private void LoadProfile(string filePath) { var p = !string.IsNullOrEmpty(filePath) && System.IO.File.Exists(filePath) ? InputProfile.Load(filePath) : new InputProfile(PROFILE_CODE); if (p.ProfileCode == PROFILE_CODE) { TextBoxX_CreateSavestate.Text = GetStringFromInputControl(p[ActionCodes.CreateSavestate]); TextBoxX_LoadSavestate.Text = GetStringFromInputControl(p[ActionCodes.LoadSavestate]); try { var tguid = allDevices.FirstOrDefault(n => Globals.CompareTwoByteArrays(n.InstanceGuid.ToByteArray(), p.ControllerInstanceGuid.ToByteArray())); if (tguid is object) ComboBoxEx_Pads.SelectedIndex = Array.IndexOf(allDevices, tguid); } catch (Exception) { } curProfile = p; } else MessageBoxEx.Show(this, LangRes.MsgBox_WrongProfileCode, LangRes.MsgBox_WrongProfileCode_Title, MessageBoxButtons.OK, MessageBoxIcon.Warning); } private void MyTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { CheckForInput(); } private void TextBoxX_Click(object sender, EventArgs e) { if (curPad is object && curProfile is object) focuesTextBox = (TextBoxX)sender; } private void TextBoxX_LostFocus_TextChanged(object sender, EventArgs e) { focuesTextBox = null; } private void TextBoxX_LoadSavestate_TextChanged(object sender, EventArgs e) { SetAction(ActionCodes.LoadSavestate, ((TextBoxX)sender).Text); } private void TextBoxX_CreateSavestate_TextChanged(object sender, EventArgs e) { SetAction(ActionCodes.CreateSavestate, ((TextBoxX)sender).Text); } private void Form1_Shown(object sender, EventArgs e) { LoadPads(); LoadProfile(GetDefaultProfileFilePath()); } private void ButtonX_LoadPads_Click(object sender, EventArgs e) { LoadPads(); } private void ComboBoxEx_Pads_SelectedIndexChanged(object sender, EventArgs e) { int index = ComboBoxEx_Pads.SelectedIndex; if (index >= 0) SetCurrentPad(allDevices[index].InstanceGuid); } private void ButtonX_StartStopListening_Click(object sender, EventArgs e) { if (enableActionExecution) { ButtonX_StartStopListening.Image = Properties.Resources.icons8_play_button_circled_16px; ButtonX_StartStopListening.Text = LangRes.Button_StartListening; enableActionExecution = false; } else { ButtonX_StartStopListening.Image = Properties.Resources.icons8_stop_squared_16px; ButtonX_StartStopListening.Text = LangRes.Button_StopListening; enableActionExecution = true; } } private void ButtonX_SaveProfile_Click(object sender, EventArgs e) { var sfd_SaveInputProfile = new SaveFileDialog { Filter = FILTER_INPUTPROFILE }; if (sfd_SaveInputProfile.ShowDialog(this) == DialogResult.OK) curProfile.Save(sfd_SaveInputProfile.FileName); } private void ButtonX_LoadProfile_Click(object sender, EventArgs e) { var ofd_SaveInputProfile = new OpenFileDialog { Filter = FILTER_INPUTPROFILE }; if (ofd_SaveInputProfile.ShowDialog(this) == DialogResult.OK) LoadProfile(ofd_SaveInputProfile.FileName); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { curProfile.Save(GetDefaultProfileFilePath()); } } }