Skip to content

Commit

Permalink
Inherit the ICommunicatorNX interface for UsbBotMini for pointer read…
Browse files Browse the repository at this point in the history
…ing relative to heap, streamline encoding via an optional boolean. (#105)
  • Loading branch information
Koi-3088 authored Sep 2, 2021
1 parent ecf5917 commit b1b8155
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 43 deletions.
54 changes: 40 additions & 14 deletions PKHeX.Core.Injection/BotController/Controllers/UsbBotMini.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace PKHeX.Core.Injection
{
public class UsbBotMini : ICommunicator, IPokeBlocks
public class UsbBotMini : ICommunicator, ICommunicatorNX, IPokeBlocks
{
private const int MaximumTransferSize = 468; // byte limitation of USB-Botbase over Android for ACNHMS, assumed same here.

Expand Down Expand Up @@ -39,13 +39,10 @@ public void Connect()
if (ur.Vid == 1406 && ur.Pid == 12288 && Port == (int)port)
SwDevice = ur.Device;
}
//SwDevice = UsbDevice.OpenUsbDevice(MyUsbFinder);

// If the device is open and ready
if (SwDevice == null)
{
throw new Exception("Device Not Found.");
}

if (SwDevice.IsOpen)
SwDevice.Close();
Expand Down Expand Up @@ -98,6 +95,21 @@ public void Disconnect()
}
}

public byte[] ReadBytes(uint offset, int length) => ReadBytesUSB(offset, length, RWMethod.Heap);
public void WriteBytes(byte[] data, uint offset) => WriteBytesUSB(data, offset, RWMethod.Heap);
public byte[] ReadBytesMain(ulong offset, int length) => ReadBytesUSB(offset, length, RWMethod.Main);
public void WriteBytesMain(byte[] data, uint offset) => WriteBytesUSB(data, offset, RWMethod.Main);
public byte[] ReadBytesAbsolute(ulong offset, int length) => ReadBytesUSB(offset, length, RWMethod.Absolute);
public void WriteBytesAbsolute(byte[] data, ulong offset) => WriteBytesUSB(data, offset, RWMethod.Absolute);
public ulong GetHeapBase()
{
var cmd = SwitchCommand.GetHeapBase();
SendInternal(cmd);
var buffer = new byte[(8 * 2) + 1];
var _ = ReadInternal(buffer);
return BitConverter.ToUInt64(buffer, 0);
}

private int ReadInternal(byte[] buffer)
{
byte[] sizeOfReturn = new byte[4];
Expand Down Expand Up @@ -142,50 +154,64 @@ public int Read(byte[] buffer)
}
}

public byte[] ReadBytes(uint offset, int length)
public byte[] ReadBytesUSB(ulong offset, int length, RWMethod method)
{
if (length > MaximumTransferSize)
return ReadBytesLarge(offset, length);
lock (_sync)
{
var cmd = SwitchCommand.PeekRaw(offset, length);
var cmd = method switch
{
RWMethod.Heap => SwitchCommand.Peek((uint)offset, length, false),
RWMethod.Main => SwitchCommand.PeekMain(offset, length, false),
RWMethod.Absolute => SwitchCommand.PeekAbsolute(offset, length, false),
_ => SwitchCommand.Peek((uint)offset, length, false),
};

SendInternal(cmd);

// give it time to push data back
Thread.Sleep(1);

var buffer = new byte[length];
var _ = ReadInternal(buffer);
//return Decoder.ConvertHexByteStringToBytes(buffer);
return buffer;
}
}

public void WriteBytes(byte[] data, uint offset)
public void WriteBytesUSB(byte[] data, ulong offset, RWMethod method)
{
if (data.Length > MaximumTransferSize)
WriteBytesLarge(data, offset);
lock (_sync)
{
SendInternal(SwitchCommand.PokeRaw(offset, data));
var cmd = method switch
{
RWMethod.Heap => SwitchCommand.Poke((uint)offset, data, false),
RWMethod.Main => SwitchCommand.PokeMain(offset, data, false),
RWMethod.Absolute => SwitchCommand.PokeAbsolute(offset, data, false),
_ => SwitchCommand.Poke((uint)offset, data, false),
};

SendInternal(cmd);

// give it time to push data back
Thread.Sleep(1);
}
}

private void WriteBytesLarge(byte[] data, uint offset)
private void WriteBytesLarge(byte[] data, ulong offset)
{
int byteCount = data.Length;
for (int i = 0; i < byteCount; i += MaximumTransferSize)
WriteBytes(SubArray(data, i, MaximumTransferSize), offset + (uint)i);
WriteBytes(SubArray(data, i, MaximumTransferSize), (uint)offset + (uint)i);
}

private byte[] ReadBytesLarge(uint offset, int length)
private byte[] ReadBytesLarge(ulong offset, int length)
{
List<byte> read = new();
for (int i = 0; i < length; i += MaximumTransferSize)
read.AddRange(ReadBytes(offset + (uint)i, Math.Min(MaximumTransferSize, length - i)));
read.AddRange(ReadBytes((uint)offset + (uint)i, Math.Min(MaximumTransferSize, length - i)));
return read.ToArray();
}

Expand All @@ -198,4 +224,4 @@ private static T[] SubArray<T>(T[] data, int index, int length)
return result;
}
}
}
}
59 changes: 30 additions & 29 deletions PKHeX.Core.Injection/BotController/SwitchCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ public static class SwitchCommand
/// <summary>
/// Removes the virtual controller from the bot. Allows physical controllers to control manually.
/// </summary>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] DetachController() => Encode("detachController");
public static byte[] DetachController(bool addrn = true) => Encode("detachController", addrn);

/*
*
Expand All @@ -27,23 +28,26 @@ public static class SwitchCommand
/// Presses and releases a <see cref="SwitchButton"/> for 50ms.
/// </summary>
/// <param name="button">Button to click.</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <remarks>Press &amp; Release timing is performed by the console automatically.</remarks>
/// <returns>Encoded command bytes</returns>
public static byte[] Click(SwitchButton button) => Encode($"click {button}");
public static byte[] Click(SwitchButton button, bool addrn = true) => Encode($"click {button}", addrn);

/// <summary>
/// Presses and does NOT release a <see cref="SwitchButton"/>.
/// </summary>
/// <param name="button">Button to hold.</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] Hold(SwitchButton button) => Encode($"press {button}");
public static byte[] Hold(SwitchButton button, bool addrn = true) => Encode($"press {button}", addrn);

/// <summary>
/// Releases the held <see cref="SwitchButton"/>.
/// </summary>
/// <param name="button">Button to release.</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] Release(SwitchButton button) => Encode($"release {button}");
public static byte[] Release(SwitchButton button, bool addrn = true) => Encode($"release {button}", addrn);

/*
*
Expand All @@ -54,14 +58,20 @@ public static class SwitchCommand
/// <summary>
/// Sets the specified <see cref="stick"/> to the desired <see cref="x"/> and <see cref="y"/> positions.
/// </summary>
/// <param name="stick">LEFT or RIGHT joystick enumerator.</param>
/// <param name="x">X axis magnitude.</param>
/// <param name="y">Y axis magnitude.</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] SetStick(SwitchStick stick, int x, int y) => Encode($"setStick {stick} {x} {y}");
public static byte[] SetStick(SwitchStick stick, int x, int y, bool addrn = true) => Encode($"setStick {stick} {x} {y}", addrn);

/// <summary>
/// Resets the specified <see cref="stick"/> to (0,0)
/// </summary>
/// <param name="stick">LEFT or RIGHT joystick enumerator.</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] ResetStick(SwitchStick stick) => SetStick(stick, 0, 0);
public static byte[] ResetStick(SwitchStick stick, bool addrn = true) => SetStick(stick, 0, 0, addrn);

/*
*
Expand All @@ -74,69 +84,60 @@ public static class SwitchCommand
/// </summary>
/// <param name="offset">Address of the data</param>
/// <param name="count">Amount of bytes</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] Peek(uint offset, int count) => Encode($"peek 0x{offset:X8} {count}");
public static byte[] Peek(uint offset, int count, bool addrn = true) => Encode($"peek 0x{offset:X8} {count}", addrn);

/// <summary>
/// Sends the Bot <see cref="data"/> to be written to <see cref="offset"/>.
/// </summary>
/// <param name="offset">Address of the data</param>
/// <param name="data">Data to write</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] Poke(uint offset, byte[] data) => Encode($"poke 0x{offset:X8} 0x{string.Concat(data.Select(z => $"{z:X2}"))}");

/// <summary>
/// (Without return characters for USB-Botbase) Requests the Bot to send <see cref="count"/> bytes from <see cref="offset"/>.
/// </summary>
/// <param name="offset">Address of the data</param>
/// <param name="count">Amount of bytes</param>
/// <returns>Encoded command bytes</returns>
public static byte[] PeekRaw(uint offset, int count) => Encode($"peek 0x{offset:X8} {count}", false);

/// <summary>
/// (Without return characters for USB-Botbase) Sends the Bot <see cref="data"/> to be written to <see cref="offset"/>.
/// </summary>
/// <param name="offset">Address of the data</param>
/// <param name="data">Data to write</param>
/// <returns>Encoded command bytes</returns>
public static byte[] PokeRaw(uint offset, byte[] data) => Encode($"poke 0x{offset:X8} 0x{string.Concat(data.Select(z => $"{z:X2}"))}", false);
public static byte[] Poke(uint offset, byte[] data, bool addrn = true) => Encode($"poke 0x{offset:X8} 0x{string.Concat(data.Select(z => $"{z:X2}"))}", addrn);

/// <summary>
/// Requests the Bot to send <see cref="count"/> bytes from absolute <see cref="offset"/>.
/// </summary>
/// <param name="offset">Absolute address of the data</param>
/// <param name="count">Amount of bytes</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] PeekAbsolute(ulong offset, int count) => Encode($"peekAbsolute 0x{offset:X16} {count}");
public static byte[] PeekAbsolute(ulong offset, int count, bool addrn = true) => Encode($"peekAbsolute 0x{offset:X16} {count}", addrn);

/// <summary>
/// Sends the Bot <see cref="data"/> to be written to absolute <see cref="offset"/>.
/// </summary>
/// <param name="offset">Absolute address of the data</param>
/// <param name="data">Data to write</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] PokeAbsolute(ulong offset, byte[] data) => Encode($"pokeAbsolute 0x{offset:X16} 0x{string.Concat(data.Select(z => $"{z:X2}"))}");
public static byte[] PokeAbsolute(ulong offset, byte[] data, bool addrn = true) => Encode($"pokeAbsolute 0x{offset:X16} 0x{string.Concat(data.Select(z => $"{z:X2}"))}", addrn);

/// <summary>
/// Requests the Bot to send <see cref="count"/> bytes from main <see cref="offset"/>.
/// </summary>
/// <param name="offset">Address of the data relative to main</param>
/// <param name="count">Amount of bytes</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] PeekMain(ulong offset, int count) => Encode($"peekMain 0x{offset:X16} {count}");
public static byte[] PeekMain(ulong offset, int count, bool addrn = true) => Encode($"peekMain 0x{offset:X16} {count}", addrn);

/// <summary>
/// Sends the Bot <see cref="data"/> to be written to main <see cref="offset"/>.
/// </summary>
/// <param name="offset">Address of the data relative to main</param>
/// <param name="data">Data to write</param>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] PokeMain(ulong offset, byte[] data) => Encode($"pokeMain 0x{offset:X16} 0x{string.Concat(data.Select(z => $"{z:X2}"))}");
public static byte[] PokeMain(ulong offset, byte[] data, bool addrn = true) => Encode($"pokeMain 0x{offset:X16} 0x{string.Concat(data.Select(z => $"{z:X2}"))}", addrn);

/// <summary>
/// Requests the Bot to send the address of the Heap base.
/// </summary>
/// <param name="addrn">Encoding selector. Default "true" for sys-botbase.</param>
/// <returns>Encoded command bytes</returns>
public static byte[] GetHeapBase() => Encode($"getHeapBase");
public static byte[] GetHeapBase(bool addrn = true) => Encode($"getHeapBase", addrn);
}
}

0 comments on commit b1b8155

Please sign in to comment.