Files
Project64-Savestater/Project64Savestater/MainForm.cs
2022-06-07 21:56:59 +02:00

655 lines
24 KiB
C#

using SharpDX.DirectInput;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Telerik.WinControls;
using Telerik.WinControls.UI;
using Timer = System.Timers.Timer;
namespace PJ64Savestater
{
public partial class MainForm : Telerik.WinControls.UI.RadForm
{
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<DeviceInstance>();
private Joystick curPad = null;
private RadTextBox focuesTextBox = null;
private InputProfile curProfile = null;
private bool enableActionExecution = false;
private int toleranceOffset;
public MainForm()
{
// Read configs
var reader = new AppSettingsReader();
toleranceOffset = (int)reader.GetValue("toleranceOffset", typeof(int));
// Init components
InitializeComponent();
// Init Timer
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();
radDropDownList_Pads.Items.Clear();
foreach (DeviceInstance d in allDevices)
radDropDownList_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()
{
var dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.ProductName);
Directory.CreateDirectory(dir);
return Path.Combine(dir, DEFAULT_PROFILE_FILENAME);
}
private void CheckForInput()
{
if (curPad is object)
{
var inputCodes = new List<string>();
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 > toleranceOffset)
inputCodes.Add("Slider " + i + " +");
if (val < -toleranceOffset)
inputCodes.Add("Slider " + i + " -");
}
for (int i = 0, loopTo3 = state.AccelerationSliders.Length - 1; i <= loopTo3; i++)
{
int val = state.AccelerationSliders[i];
if (val > toleranceOffset)
inputCodes.Add("ASlider " + i + " +");
if (val < -toleranceOffset)
inputCodes.Add("ASlider " + i + " -");
}
for (int i = 0, loopTo4 = state.ForceSliders.Length - 1; i <= loopTo4; i++)
{
int val = state.ForceSliders[i];
if (val > toleranceOffset)
inputCodes.Add("FSlider " + i + " +");
if (val < -toleranceOffset)
inputCodes.Add("FSlider " + i + " -");
}
for (int i = 0, loopTo5 = state.VelocitySliders.Length - 1; i <= loopTo5; i++)
{
int val = state.VelocitySliders[i];
if (val > toleranceOffset)
inputCodes.Add("VSlider " + i + " +");
if (val < -toleranceOffset)
inputCodes.Add("VSlider " + i + " -");
}
if (state.X > toleranceOffset)
inputCodes.Add("X +");
if (state.X < -toleranceOffset)
inputCodes.Add("X -");
if (state.Y > toleranceOffset)
inputCodes.Add("Y +");
if (state.Y < -toleranceOffset)
inputCodes.Add("Y -");
if (state.Z > toleranceOffset)
inputCodes.Add("Z +");
if (state.Z < -toleranceOffset)
inputCodes.Add("Z -");
if (state.AccelerationX > toleranceOffset)
inputCodes.Add("AX +");
if (state.AccelerationX < -toleranceOffset)
inputCodes.Add("AX -");
if (state.AccelerationY > toleranceOffset)
inputCodes.Add("AY +");
if (state.AccelerationY < -toleranceOffset)
inputCodes.Add("AY -");
if (state.AccelerationZ > toleranceOffset)
inputCodes.Add("AZ +");
if (state.AccelerationZ < -toleranceOffset)
inputCodes.Add("AZ -");
if (state.AngularAccelerationX > toleranceOffset)
inputCodes.Add("AAX +");
if (state.AngularAccelerationX < -toleranceOffset)
inputCodes.Add("AAX -");
if (state.AngularAccelerationY > toleranceOffset)
inputCodes.Add("AAY +");
if (state.AngularAccelerationY < -toleranceOffset)
inputCodes.Add("AAY -");
if (state.AngularAccelerationZ > toleranceOffset)
inputCodes.Add("AAZ +");
if (state.AngularAccelerationZ < -toleranceOffset)
inputCodes.Add("AAZ -");
if (state.ForceX > toleranceOffset)
inputCodes.Add("FX +");
if (state.ForceX < -toleranceOffset)
inputCodes.Add("FX -");
if (state.ForceY > toleranceOffset)
inputCodes.Add("FY +");
if (state.ForceY < -toleranceOffset)
inputCodes.Add("FY -");
if (state.ForceZ > toleranceOffset)
inputCodes.Add("FZ +");
if (state.ForceZ < -toleranceOffset)
inputCodes.Add("FZ -");
if (state.RotationX > toleranceOffset)
inputCodes.Add("RX +");
if (state.RotationX < -toleranceOffset)
inputCodes.Add("RX -");
if (state.RotationY > toleranceOffset)
inputCodes.Add("RY +");
if (state.RotationY < -toleranceOffset)
inputCodes.Add("RY -");
if (state.RotationZ > toleranceOffset)
inputCodes.Add("RZ +");
if (state.RotationZ < -toleranceOffset)
inputCodes.Add("RZ -");
if (state.TorqueX > toleranceOffset)
inputCodes.Add("TX +");
if (state.TorqueX < -toleranceOffset)
inputCodes.Add("TX -");
if (state.TorqueY > toleranceOffset)
inputCodes.Add("TY +");
if (state.TorqueY < -toleranceOffset)
inputCodes.Add("TY -");
if (state.TorqueZ > toleranceOffset)
inputCodes.Add("TZ +");
if (state.TorqueZ < -toleranceOffset)
inputCodes.Add("TZ -");
if (state.VelocityX > toleranceOffset)
inputCodes.Add("VX +");
if (state.VelocityX < -toleranceOffset)
inputCodes.Add("VX -");
if (state.VelocityY > toleranceOffset)
inputCodes.Add("VY +");
if (state.VelocityY < -toleranceOffset)
inputCodes.Add("VY -");
if (state.VelocityZ > toleranceOffset)
inputCodes.Add("VZ +");
if (state.VelocityZ < -toleranceOffset)
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)
{
radTextBox_CreateSavestate.Text = GetStringFromInputControl(p[ActionCodes.CreateSavestate]);
radTextBox_LoadSavestate.Text = GetStringFromInputControl(p[ActionCodes.LoadSavestate]);
try
{
var tguid = allDevices.FirstOrDefault(n => Globals.CompareTwoByteArrays(n.InstanceGuid.ToByteArray(), p.ControllerInstanceGuid.ToByteArray()));
if (tguid is object)
radDropDownList_Pads.SelectedIndex = Array.IndexOf(allDevices, tguid);
}
catch (Exception)
{
}
curProfile = p;
}
else
RadMessageBox.Show(this, LangRes.MsgBox_WrongProfileCode, LangRes.MsgBox_WrongProfileCode_Title, MessageBoxButtons.OK, RadMessageIcon.Exclamation);
}
private void MyTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
{
CheckForInput();
}
private void MainForm_Shown(object sender, EventArgs e)
{
LoadPads();
LoadProfile(GetDefaultProfileFilePath());
}
private void radButton_StartStopListening_Click(object sender, EventArgs e)
{
if (enableActionExecution)
{
radButton_StartStopListening.Image = Properties.Resources.icons8_play_button_circled_16px;
radButton_StartStopListening.Text = LangRes.Button_StartListening;
enableActionExecution = false;
}
else
{
radButton_StartStopListening.Image = Properties.Resources.icons8_stop_squared_16px;
radButton_StartStopListening.Text = LangRes.Button_StopListening;
enableActionExecution = true;
}
}
private void radButton3_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 radButton4_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 radTextBox1_TextChanged(object sender, EventArgs e)
{
SetAction(ActionCodes.LoadSavestate, ((RadTextBox)sender).Text);
}
private void radTextBox2_TextChanged(object sender, EventArgs e)
{
SetAction(ActionCodes.CreateSavestate, ((RadTextBox)sender).Text);
}
private void radButton1_Click(object sender, EventArgs e)
{
LoadPads();
}
private void radDropDownList1_SelectedIndexChanged(object sender, Telerik.WinControls.UI.Data.PositionChangedEventArgs e)
{
int index = radDropDownList_Pads.SelectedIndex;
if (index >= 0)
SetCurrentPad(allDevices[index].InstanceGuid);
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
curProfile.Save(GetDefaultProfileFilePath());
}
private void RadTextBox_Savestate_Clicked(object sender, EventArgs e)
{
if (curPad is object && curProfile is object)
focuesTextBox = (RadTextBox)sender;
}
private void RadTextBox_Savestate_LostFocus(object sender, EventArgs e)
{
focuesTextBox = null;
}
}
}