From b3e8b1b1fbd60e8fce8be0df5310b90f493cc57c Mon Sep 17 00:00:00 2001 From: Kurt Date: Tue, 31 Mar 2020 19:22:42 -0700 Subject: [PATCH] Add sysbot logic for remote control --- NHSE.Injection/Decoder.cs | 42 ++++++++++++++ NHSE.Injection/NHSE.Injection.csproj | 9 +++ NHSE.Injection/SwitchButton.cs | 27 +++++++++ NHSE.Injection/SwitchCommand.cs | 70 +++++++++++++++++++++++ NHSE.Injection/SwitchStick.cs | 11 ++++ NHSE.Injection/SysBot.cs | 83 ++++++++++++++++++++++++++++ NHSE.sln | 6 ++ 7 files changed, 248 insertions(+) create mode 100644 NHSE.Injection/Decoder.cs create mode 100644 NHSE.Injection/NHSE.Injection.csproj create mode 100644 NHSE.Injection/SwitchButton.cs create mode 100644 NHSE.Injection/SwitchCommand.cs create mode 100644 NHSE.Injection/SwitchStick.cs create mode 100644 NHSE.Injection/SysBot.cs diff --git a/NHSE.Injection/Decoder.cs b/NHSE.Injection/Decoder.cs new file mode 100644 index 0000000..50d739d --- /dev/null +++ b/NHSE.Injection/Decoder.cs @@ -0,0 +1,42 @@ +using System; + +namespace NHSE.Injection +{ + public static class Decoder + { + private static bool IsNum(char c) => (uint)(c - '0') <= 9; + private static bool IsHexUpper(char c) => (uint)(c - 'A') <= 5; + + public static byte[] ConvertHexByteStringToBytes(byte[] bytes) + { + var dest = new byte[bytes.Length / 2]; + for (int i = 0; i < dest.Length; i++) + { + int ofs = i * 2; + var _0 = (char)bytes[ofs + 0]; + var _1 = (char)bytes[ofs + 1]; + dest[i] = DecodeTuple(_0, _1); + } + return dest; + } + + private static byte DecodeTuple(char _0, char _1) + { + byte result; + if (IsNum(_0)) + result = (byte)((_0 - '0') << 4); + else if (IsHexUpper(_0)) + result = (byte)((_0 - 'A' + 10) << 4); + else + throw new ArgumentOutOfRangeException(nameof(_0)); + + if (IsNum(_1)) + result |= (byte)(_1 - '0'); + else if (IsHexUpper(_1)) + result |= (byte)(_1 - 'A' + 10); + else + throw new ArgumentOutOfRangeException(nameof(_1)); + return result; + } + } +} \ No newline at end of file diff --git a/NHSE.Injection/NHSE.Injection.csproj b/NHSE.Injection/NHSE.Injection.csproj new file mode 100644 index 0000000..3419541 --- /dev/null +++ b/NHSE.Injection/NHSE.Injection.csproj @@ -0,0 +1,9 @@ + + + + net46;netstandard2.0 + 8 + enable + + + diff --git a/NHSE.Injection/SwitchButton.cs b/NHSE.Injection/SwitchButton.cs new file mode 100644 index 0000000..5c3be72 --- /dev/null +++ b/NHSE.Injection/SwitchButton.cs @@ -0,0 +1,27 @@ +namespace NHSE.Injection +{ + /// + /// Controller Buttons + /// + public enum SwitchButton + { + A, + B, + X, + Y, + RSTICK, + LSTICK, + L, + R, + ZL, + ZR, + PLUS, + MINUS, + DUP, + DDOWN, + DLEFT, + DRIGHT, + HOME, + CAPTURE, + } +} \ No newline at end of file diff --git a/NHSE.Injection/SwitchCommand.cs b/NHSE.Injection/SwitchCommand.cs new file mode 100644 index 0000000..ea1acdf --- /dev/null +++ b/NHSE.Injection/SwitchCommand.cs @@ -0,0 +1,70 @@ +using System.Linq; +using System.Text; + +namespace NHSE.Injection +{ + /// + /// Encodes commands for a to be sent as a array. + /// + public static class SwitchCommand + { + private static readonly Encoding Encoder = Encoding.UTF8; + private static byte[] Encode(string command) => Encoder.GetBytes(command + "\r\n"); + + /// + /// Removes the virtual controller from the bot. Allows physical controllers to control manually. + /// + /// Encoded command bytes + public static byte[] DetachController() => Encode("detachController"); + + /// + /// Presses and releases a for 50ms. + /// + /// Button to click. + /// Press & Release timing is performed by the console automatically. + /// Encoded command bytes + public static byte[] Click(SwitchButton button) => Encode($"click {button}"); + + /// + /// Presses and does NOT release a . + /// + /// Button to hold. + /// Encoded command bytes + public static byte[] Hold(SwitchButton button) => Encode($"press {button}"); + + /// + /// Releases the held . + /// + /// Button to release. + /// Encoded command bytes + public static byte[] Release(SwitchButton button) => Encode($"release {button}"); + + /// + /// Sets the specified to the desired and positions. + /// + /// Encoded command bytes + public static byte[] SetStick(SwitchStick stick, int x, int y) => Encode($"setStick {stick} {x} {y}"); + + /// + /// Resets the specified to (0,0) + /// + /// Encoded command bytes + public static byte[] ResetStick(SwitchStick stick) => SetStick(stick, 0, 0); + + /// + /// Requests the Bot to send bytes from . + /// + /// Address of the data + /// Amount of bytes + /// Encoded command bytes + public static byte[] Peek(uint offset, int count) => Encode($"peek 0x{offset:X8} {count}"); + + /// + /// Sends the Bot to be written to . + /// + /// Address of the data + /// Data to write + /// Encoded command bytes + public static byte[] Poke(uint offset, byte[] data) => Encode($"poke 0x{offset:X8} 0x{string.Concat(data.Select(z => $"{z:X2}"))}"); + } +} \ No newline at end of file diff --git a/NHSE.Injection/SwitchStick.cs b/NHSE.Injection/SwitchStick.cs new file mode 100644 index 0000000..34dcdf5 --- /dev/null +++ b/NHSE.Injection/SwitchStick.cs @@ -0,0 +1,11 @@ +namespace NHSE.Injection +{ + /// + /// Controller Stick differentiation + /// + public enum SwitchStick + { + LEFT, + RIGHT, + } +} \ No newline at end of file diff --git a/NHSE.Injection/SysBot.cs b/NHSE.Injection/SysBot.cs new file mode 100644 index 0000000..a84c63a --- /dev/null +++ b/NHSE.Injection/SysBot.cs @@ -0,0 +1,83 @@ +using System.Net.Sockets; +using System.Threading; + +namespace NHSE.Injection +{ + public class SysBot + { + public string IP = "192.168.1.65"; + public int Port = 6000; + public Socket Connection = new Socket(SocketType.Stream, ProtocolType.Tcp); + public bool Connected; + + private readonly object _sync = new object(); + + public void Connect(string ip, int port) + { + IP = ip; + Port = port; + lock (_sync) + { + Connection = new Socket(SocketType.Stream, ProtocolType.Tcp); + Connection.Connect(IP, Port); + Connected = true; + } + } + + public void Disconnect() + { + lock (_sync) + { + Connection.Disconnect(false); + Connected = false; + } + } + + private int ReadInternal(byte[] buffer) + { + int br = Connection.Receive(buffer, 0, 1, SocketFlags.None); + while (buffer[br - 1] != (byte)'\n') + br += Connection.Receive(buffer, br, 1, SocketFlags.None); + return br; + } + + private int SendInternal(byte[] buffer) => Connection.Send(buffer); + + public int Read(byte[] buffer) + { + lock (_sync) + return ReadInternal(buffer); + } + + public byte[] ReadBytes(uint offset, int length) + { + lock (_sync) + { + var cmd = SwitchCommand.Peek(offset, length); + SendInternal(cmd); + + // give it time to push data back + Thread.Sleep((length / 256) + 100); + var buffer = new byte[(length * 2) + 1]; + var _ = ReadInternal(buffer); + return Decoder.ConvertHexByteStringToBytes(buffer); + } + } + + public void WriteBytes(byte[] data, uint offset) + { + lock (_sync) + { + SendInternal(SwitchCommand.Poke(offset, data)); + + // give it time to push data back + Thread.Sleep((data.Length / 256) + 100); + } + } + } + + public enum InjectionType + { + Pouch, + } +} diff --git a/NHSE.sln b/NHSE.sln index c1f1636..83ac443 100644 --- a/NHSE.sln +++ b/NHSE.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHSE.Parsing", "NHSE.Parsin EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHSE.Tests", "NHSE.Tests\NHSE.Tests.csproj", "{AD3412A3-74EA-443D-BB07-B36E39D5166F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHSE.Injection", "NHSE.Injection\NHSE.Injection.csproj", "{319E810B-A516-480E-88BD-F648222344D3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {AD3412A3-74EA-443D-BB07-B36E39D5166F}.Debug|Any CPU.Build.0 = Debug|Any CPU {AD3412A3-74EA-443D-BB07-B36E39D5166F}.Release|Any CPU.ActiveCfg = Release|Any CPU {AD3412A3-74EA-443D-BB07-B36E39D5166F}.Release|Any CPU.Build.0 = Release|Any CPU + {319E810B-A516-480E-88BD-F648222344D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {319E810B-A516-480E-88BD-F648222344D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {319E810B-A516-480E-88BD-F648222344D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {319E810B-A516-480E-88BD-F648222344D3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE