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