using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using ksm.OpenRGB.Utils;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ksm.OpenRGB")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+1aaf8d1496829bfc0fcad0bb203a91edf02fa5ac")]
[assembly: AssemblyProduct("OpenRGB-BepInEx")]
[assembly: AssemblyTitle("ksm.OpenRGB")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class IsUnmanagedAttribute : Attribute
{
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
internal static class IsExternalInit
{
}
}
namespace ksm.OpenRGB
{
public enum ColorMode : uint
{
None,
PerLed,
ModeSpecific,
Random
}
public enum CommandId : uint
{
RequestControllerCount = 0u,
RequestControllerData = 1u,
RequestProtocolVersion = 40u,
SetClientName = 50u,
DeviceListUpdated = 100u,
RequestProfiles = 150u,
SaveProfile = 151u,
LoadProfile = 152u,
DeleteProfile = 153u,
RequestPlugins = 200u,
PluginSpecific = 201u,
ResizeZone = 1000u,
UpdateLeds = 1050u,
UpdateZoneLeds = 1051u,
UpdateSingleLed = 1052u,
SetCustomMode = 1100u,
UpdateMode = 1101u,
SaveMode = 1102u
}
public enum DeviceType : uint
{
Motherboard,
Dram,
Gpu,
Cooler,
Ledstrip,
Keyboard,
Mouse,
Mousemat,
Headset,
HeadsetStand,
Gamepad,
Light,
Speaker,
Virtual,
Unknown
}
public enum Direction : uint
{
None = uint.MaxValue,
Left = 0u,
Right = 1u,
Up = 2u,
Down = 3u,
Horizontal = 4u,
Vertical = 5u
}
[Flags]
public enum ModeFlags : uint
{
None = 0u,
HasSpeed = 1u,
HasDirectionLR = 2u,
HasDirectionUD = 4u,
HasDirectionHV = 8u,
HasBrightness = 0x10u,
HasPerLedColor = 0x20u,
HasModeSpecificColor = 0x40u,
HasRandomColor = 0x80u
}
public enum ZoneType : uint
{
Single,
Linear,
Matrix
}
public interface IOpenRgbClient
{
bool Connected { get; }
ProtocolVersion MaxSupportedProtocolVersion { get; }
ProtocolVersion ClientProtocolVersion { get; }
ProtocolVersion CommonProtocolVersion { get; }
event EventHandler<EventArgs>? DeviceListUpdated;
void Connect();
int GetControllerCount();
Device GetControllerData(int deviceId);
Device[] GetAllControllerData();
string[] GetProfiles();
void LoadProfile(string profile);
void DeleteProfile(string profile);
void SaveProfile(string profile);
Plugin[] GetPlugins();
void ResizeZone(int deviceId, int zoneId, int size);
void UpdateLeds(int deviceId, ReadOnlySpan<Color> colors);
void UpdateZoneLeds(int deviceId, int zoneId, ReadOnlySpan<Color> colors);
void UpdateSingleLed(int deviceId, int ledId, Color color);
void SetCustomMode(int deviceId);
void UpdateMode(int deviceId, int modeId, uint? speed = null, Direction? direction = null, Color[]? colors = null);
void SaveMode(int deviceId, int modeId);
void PluginSpecific(int pluginId, int pluginPacketType, ReadOnlySpan<byte> data);
}
internal interface ISpanReader<out T>
{
T ReadFrom(ref SpanReader reader, ProtocolVersion? protocolVersion = null, int? index = null, int? outerCount = null);
}
internal interface ISpanWritable
{
int Length { get; }
void WriteTo(ref SpanWriter writer);
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly record struct Color(byte R = 0, byte G = 0, byte B = 0)
{
private readonly byte UnusedAlpha = 0;
[CompilerGenerated]
private bool PrintMembers(StringBuilder builder)
{
builder.Append("R = ");
builder.Append(R.ToString());
builder.Append(", G = ");
builder.Append(G.ToString());
builder.Append(", B = ");
builder.Append(B.ToString());
return true;
}
}
public class Device
{
public int Index { get; }
public DeviceType Type { get; }
public string Name { get; }
public string? Vendor { get; }
public string Description { get; }
public string Version { get; }
public string Serial { get; }
public string Location { get; }
public int ActiveModeIndex { get; }
public Mode[] Modes { get; }
public Zone[] Zones { get; }
public Led[] Leds { get; }
public Color[] Colors { get; }
public Mode ActiveMode => Modes[ActiveModeIndex];
internal Device(int index, DeviceType type, string name, string? vendor, string description, string version, string serial, string location, int activeModeIndex, Mode[] modes, Zone[] zones, Led[] leds, Color[] colors)
{
Index = index;
Type = type;
Name = name;
Vendor = vendor;
Description = description;
Version = version;
Serial = serial;
Location = location;
ActiveModeIndex = activeModeIndex;
Modes = modes;
Zones = zones;
Leds = leds;
Colors = colors;
}
public override string ToString()
{
return $"{Type}: {Name}";
}
}
public class Led
{
public int Index { get; }
public string Name { get; }
public uint Value { get; }
internal Led(int index, string name, uint value)
{
Index = index;
Name = name;
Value = value;
}
public override string ToString()
{
return $"Name: {Name}, Value: {Value}";
}
}
public class MatrixMap
{
public uint Height { get; }
public uint Width { get; }
public uint[,] Matrix { get; }
internal MatrixMap(uint height, uint width, uint[,] matrix)
{
Height = height;
Width = width;
Matrix = matrix;
}
}
public class Mode
{
public ProtocolVersion ProtocolVersion { get; }
public int Index { get; }
public string Name { get; }
public int Value { get; }
public ModeFlags Flags { get; }
public uint SpeedMin { get; }
public uint SpeedMax { get; }
public uint BrightnessMin { get; }
public uint BrightnessMax { get; }
public uint ColorMin { get; }
public uint ColorMax { get; }
public uint Speed { get; private set; }
public uint Brightness { get; private set; }
public Direction Direction { get; private set; }
public ColorMode ColorMode { get; }
public Color[] Colors { get; private set; }
public bool SupportsSpeed => Flags.HasFlag(ModeFlags.HasSpeed);
public bool SupportsBrightness => Flags.HasFlag(ModeFlags.HasBrightness);
public bool SupportsDirection
{
get
{
if (!Flags.HasFlag(ModeFlags.HasDirectionHV) && !Flags.HasFlag(ModeFlags.HasDirectionUD))
{
return Flags.HasFlag(ModeFlags.HasDirectionLR);
}
return true;
}
}
internal Mode(ProtocolVersion protocolVersion, int index, string name, int value, ModeFlags flags, uint speedMin, uint speedMax, uint brightnessMin, uint brightnessMax, uint colorMin, uint colorMax, uint speed, uint brightness, Direction direction, ColorMode colorMode, Color[] colors)
{
ProtocolVersion = protocolVersion;
Index = index;
Name = name;
Value = value;
Flags = flags;
SpeedMin = (SupportsSpeed ? speedMin : 0u);
SpeedMax = (SupportsSpeed ? speedMax : 0u);
BrightnessMin = (SupportsBrightness ? brightnessMin : 0u);
BrightnessMax = (SupportsBrightness ? brightnessMax : 0u);
ColorMin = (Flags.HasFlag(ModeFlags.HasModeSpecificColor) ? colorMin : 0u);
ColorMax = (Flags.HasFlag(ModeFlags.HasModeSpecificColor) ? colorMax : 0u);
Speed = speed;
Brightness = brightness;
Direction = (SupportsDirection ? direction : Direction.None);
ColorMode = colorMode;
Colors = colors;
}
public void SetSpeed(uint newSpeed)
{
if (!SupportsSpeed)
{
throw new InvalidOperationException("This mode does not support speed.");
}
Speed = newSpeed;
}
public void SetBrightness(uint newBrightness)
{
if (!SupportsBrightness)
{
throw new InvalidOperationException("This mode does not support brightness.");
}
Brightness = newBrightness;
}
public void SetDirection(Direction newDirection)
{
if (!SupportsDirection)
{
throw new InvalidOperationException("This mode does not support direction.");
}
Direction = newDirection;
}
public void SetColors(Color[] newColors)
{
Colors = newColors;
}
}
public class Plugin
{
public string Name { get; }
public string Description { get; }
public string Version { get; }
public uint Index { get; }
public int SdkVersion { get; }
internal Plugin(string name, string description, string version, uint index, int sdkVersion)
{
Name = name;
Description = description;
Version = version;
Index = index;
SdkVersion = sdkVersion;
}
}
public readonly struct ProtocolVersion
{
public static readonly ProtocolVersion Invalid = new ProtocolVersion(0u, supportsVendorString: false, supportsProfileControls: false, supportsBrightnessAndSaveMode: false, supportsSegmentsAndPlugins: false);
public static readonly ProtocolVersion V0 = new ProtocolVersion(0u, supportsVendorString: false, supportsProfileControls: false, supportsBrightnessAndSaveMode: false, supportsSegmentsAndPlugins: false);
public static readonly ProtocolVersion V1 = new ProtocolVersion(1u, supportsVendorString: true, supportsProfileControls: false, supportsBrightnessAndSaveMode: false, supportsSegmentsAndPlugins: false);
public static readonly ProtocolVersion V2 = new ProtocolVersion(2u, supportsVendorString: true, supportsProfileControls: true, supportsBrightnessAndSaveMode: false, supportsSegmentsAndPlugins: false);
public static readonly ProtocolVersion V3 = new ProtocolVersion(3u, supportsVendorString: true, supportsProfileControls: true, supportsBrightnessAndSaveMode: true, supportsSegmentsAndPlugins: false);
public static readonly ProtocolVersion V4 = new ProtocolVersion(4u, supportsVendorString: true, supportsProfileControls: true, supportsBrightnessAndSaveMode: true, supportsSegmentsAndPlugins: true);
public uint Number { get; }
public bool SupportsVendorString { get; }
public bool SupportsProfileControls { get; }
public bool SupportsBrightnessAndSaveMode { get; }
public bool SupportsSegmentsAndPlugins { get; }
private ProtocolVersion(uint number, bool supportsVendorString, bool supportsProfileControls, bool supportsBrightnessAndSaveMode, bool supportsSegmentsAndPlugins)
{
Number = number;
SupportsVendorString = supportsVendorString;
SupportsProfileControls = supportsProfileControls;
SupportsBrightnessAndSaveMode = supportsBrightnessAndSaveMode;
SupportsSegmentsAndPlugins = supportsSegmentsAndPlugins;
}
public static ProtocolVersion FromNumber(uint number)
{
return number switch
{
0u => V0,
1u => V1,
2u => V2,
3u => V3,
4u => V4,
_ => throw new ArgumentOutOfRangeException("number", number, "Unknown protocol version"),
};
}
}
public class Segment
{
public int Index { get; }
public string Name { get; }
public ZoneType Type { get; }
public uint Start { get; }
public uint LedCount { get; }
internal Segment(int index, string name, ZoneType type, uint start, uint ledCount)
{
Index = index;
Name = name;
Type = type;
Start = start;
LedCount = ledCount;
}
}
public class Zone
{
public int Index { get; }
public int DeviceIndex { get; }
public string Name { get; }
public ZoneType Type { get; }
public uint LedCount { get; }
public uint LedsMin { get; }
public uint LedsMax { get; }
public MatrixMap? MatrixMap { get; }
public Segment[] Segments { get; }
internal Zone(int index, int deviceIndex, string name, ZoneType type, uint ledCount, uint ledsMin, uint ledsMax, MatrixMap? matrixMap, Segment[] segments)
{
Index = index;
DeviceIndex = deviceIndex;
Name = name;
Type = type;
LedCount = ledCount;
LedsMin = ledsMin;
LedsMax = ledsMax;
MatrixMap = matrixMap;
Segments = segments;
}
}
public sealed class OpenRgbClient : IDisposable, IOpenRgbClient
{
private const int MaxProtocolNumber = 4;
private readonly string _name;
private readonly string _ip;
private readonly int _port;
private readonly int _timeoutMs;
private readonly uint _protocolVersionNumber;
private readonly OpenRgbConnection _connection;
public bool Connected => _connection.Connected;
public ProtocolVersion MaxSupportedProtocolVersion => ProtocolVersion.FromNumber(4u);
public ProtocolVersion ClientProtocolVersion => ProtocolVersion.FromNumber(_protocolVersionNumber);
public ProtocolVersion CommonProtocolVersion => _connection.CurrentProtocolVersion;
public event EventHandler<EventArgs>? DeviceListUpdated;
public OpenRgbClient(string ip = "127.0.0.1", int port = 6742, string name = "OpenRGB.NET", bool autoConnect = true, int timeoutMs = 1000, uint protocolVersionNumber = 4u)
{
_ip = ip;
_port = port;
_name = name;
_timeoutMs = timeoutMs;
_protocolVersionNumber = protocolVersionNumber;
_connection = new OpenRgbConnection(this.DeviceListUpdated);
if (protocolVersionNumber > 4)
{
throw new ArgumentException("Client protocol version provided higher than supported.", "protocolVersionNumber");
}
if (autoConnect)
{
Connect();
}
}
public void Connect()
{
if (!Connected)
{
_connection.Connect(_name, _ip, _port, _timeoutMs, _protocolVersionNumber);
}
}
public int GetControllerCount()
{
return _connection.Request<EmptyArg, PrimitiveReader<int>, int>(CommandId.RequestControllerCount, 0u, default(EmptyArg));
}
public Device GetControllerData(int deviceId)
{
if (deviceId < 0)
{
throw new ArgumentException("Unexpected device Id", "deviceId");
}
return _connection.Request<ProtocolVersionArg, DeviceReader, Device>(CommandId.RequestControllerData, (uint)deviceId, new ProtocolVersionArg(_connection.CurrentProtocolVersion));
}
public Device[] GetAllControllerData()
{
int controllerCount = GetControllerCount();
Device[] array = new Device[controllerCount];
for (int i = 0; i < controllerCount; i++)
{
array[i] = GetControllerData(i);
}
return array;
}
public string[] GetProfiles()
{
if (!CommonProtocolVersion.SupportsProfileControls)
{
throw new NotSupportedException($"Not supported on protocol version {CommonProtocolVersion.Number}");
}
return _connection.Request<EmptyArg, ProfilesReader, string[]>(CommandId.RequestProfiles, 0u, default(EmptyArg));
}
public Plugin[] GetPlugins()
{
if (!CommonProtocolVersion.SupportsSegmentsAndPlugins)
{
throw new NotSupportedException($"Not supported on protocol version {CommonProtocolVersion.Number}");
}
return _connection.Request<EmptyArg, PluginsReader, Plugin[]>(CommandId.RequestPlugins, 0u, default(EmptyArg));
}
public void ResizeZone(int deviceId, int zoneId, int size)
{
_connection.Send(CommandId.ResizeZone, (uint)deviceId, new Args<uint, uint>((uint)zoneId, (uint)size));
}
public void UpdateLeds(int deviceId, ReadOnlySpan<Color> colors)
{
if (colors.Length == 0)
{
throw new ArgumentException("The colors span is empty.", "colors");
}
if (deviceId < 0)
{
throw new ArgumentException("Invalid deviceId", "deviceId");
}
ReadOnlySpan<byte> additionalData = MemoryMarshal.Cast<Color, byte>(colors);
_connection.Send(CommandId.UpdateLeds, (uint)deviceId, new UpdateLedsArg((ushort)colors.Length), additionalData);
}
public void UpdateZoneLeds(int deviceId, int zoneId, ReadOnlySpan<Color> colors)
{
if (colors.Length == 0)
{
throw new ArgumentException("The colors span is empty.", "colors");
}
if (deviceId < 0)
{
throw new ArgumentException("Invalid device id.", "deviceId");
}
if (zoneId < 0)
{
throw new ArgumentException("Invalid zone id", "zoneId");
}
ReadOnlySpan<byte> additionalData = MemoryMarshal.Cast<Color, byte>(colors);
_connection.Send(CommandId.UpdateZoneLeds, (uint)deviceId, new UpdateZoneLedsArg((uint)zoneId, (ushort)colors.Length), additionalData);
}
public void UpdateSingleLed(int deviceId, int ledId, Color color)
{
if (deviceId < 0)
{
throw new ArgumentException("Invalid device id.", "deviceId");
}
if (ledId < 0)
{
throw new ArgumentException("Invalid led id", "ledId");
}
_connection.Send(CommandId.UpdateSingleLed, (uint)deviceId, new Args<uint, Color>((uint)ledId, color));
}
public void SetCustomMode(int deviceId)
{
_connection.Send(CommandId.SetCustomMode, (uint)deviceId, default(EmptyArg));
}
public void LoadProfile(string profile)
{
_connection.Send(CommandId.LoadProfile, 0u, new StringArg(profile));
}
public void SaveProfile(string profile)
{
_connection.Send(CommandId.SaveProfile, 0u, new StringArg(profile));
}
public void DeleteProfile(string profile)
{
_connection.Send(CommandId.DeleteProfile, 0u, new StringArg(profile));
}
public void UpdateMode(int deviceId, int modeId, uint? speed = null, Direction? direction = null, Color[]? colors = null)
{
Device controllerData = GetControllerData(deviceId);
if (modeId > controllerData.Modes.Length)
{
throw new ArgumentException("modeId");
}
Mode mode = controllerData.Modes[modeId];
if (speed.HasValue)
{
if (!mode.SupportsSpeed)
{
throw new InvalidOperationException("Cannot set speed on a mode that doesn't use this parameter");
}
mode.SetSpeed(speed.Value);
}
if (direction.HasValue)
{
if (!mode.SupportsDirection)
{
throw new InvalidOperationException("Cannot set direction on a mode that doesn't use this parameter");
}
mode.SetDirection(direction.Value);
}
if (colors != null)
{
if (colors.Length != mode.Colors.Length)
{
throw new InvalidOperationException("Incorrect number of colors supplied");
}
mode.SetColors(colors);
}
_connection.Send(CommandId.UpdateMode, (uint)deviceId, new ModeOperationArg((uint)modeId, new ModeArg(mode)));
}
public void SaveMode(int deviceId, int modeId)
{
Device controllerData = GetControllerData(deviceId);
if (modeId > controllerData.Modes.Length)
{
throw new ArgumentException("modeId");
}
Mode mode = controllerData.Modes[modeId];
_connection.Send(CommandId.SaveMode, (uint)deviceId, new ModeOperationArg((uint)modeId, new ModeArg(mode)));
}
public void PluginSpecific(int pluginId, int pluginPacketType, ReadOnlySpan<byte> data)
{
if (!CommonProtocolVersion.SupportsSegmentsAndPlugins)
{
throw new NotSupportedException($"Not supported on protocol version {CommonProtocolVersion.Number}");
}
_connection.Send(CommandId.PluginSpecific, (uint)pluginId, new Args<uint>((uint)pluginPacketType), data);
}
public void Dispose()
{
_connection.Dispose();
}
}
internal sealed class OpenRgbConnection : IDisposable
{
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly Socket _socket;
private readonly Dictionary<CommandId, BlockingCollection<byte[]>> _pendingRequests;
private Task? _readLoopTask;
public bool Connected => _socket.Connected;
public ProtocolVersion CurrentProtocolVersion { get; private set; }
public EventHandler<EventArgs>? DeviceListUpdated { get; set; }
public OpenRgbConnection(EventHandler<EventArgs>? OnDeviceListUpdated)
{
_cancellationTokenSource = new CancellationTokenSource();
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
_socket.NoDelay = true;
_pendingRequests = Enum.GetValues(typeof(CommandId)).Cast<CommandId>().ToDictionary((CommandId c) => c, (CommandId _) => new BlockingCollection<byte[]>());
DeviceListUpdated = OnDeviceListUpdated;
}
public void Connect(string name, string ip, int port, int timeoutMs, uint protocolVersionNumber = 4u)
{
if (!Connected)
{
_socket.Connect(ip, port, timeoutMs, _cancellationTokenSource.Token);
_readLoopTask = Task.Run((Func<Task?>)ReadLoop, _cancellationTokenSource.Token);
Send(CommandId.SetClientName, 0u, new StringArg(name));
uint number = NegotiateProtocolVersion(protocolVersionNumber);
CurrentProtocolVersion = ProtocolVersion.FromNumber(number);
}
}
private async Task ReadLoop()
{
byte[] headerBuffer = new byte[16];
while (!_cancellationTokenSource.IsCancellationRequested && _socket.Connected)
{
try
{
await _socket.ReceiveAllAsync(headerBuffer, _cancellationTokenSource.Token);
PacketHeader header = PacketHeader.FromSpan(headerBuffer);
if (header.Command == CommandId.DeviceListUpdated)
{
Task.Run(delegate
{
try
{
DeviceListUpdated?.Invoke(this, EventArgs.Empty);
}
catch
{
}
});
}
else
{
byte[] dataBuffer = new byte[header.DataLength];
await _socket.ReceiveAllAsync(dataBuffer, _cancellationTokenSource.Token);
_pendingRequests[header.Command].Add(dataBuffer, _cancellationTokenSource.Token);
}
}
catch (TaskCanceledException)
{
}
}
}
public void Send<TRequest>(CommandId command, uint deviceId, TRequest requestData, ReadOnlySpan<byte> additionalData = default(ReadOnlySpan<byte>)) where TRequest : ISpanWritable
{
int num = requestData.Length + additionalData.Length;
int num2 = 16 + num;
PacketHeader packetHeader = new PacketHeader(deviceId, command, (uint)num);
byte[] array = ArrayPool<byte>.Shared.Rent(num2);
Span<byte> span = array.AsSpan(0, num2);
SpanWriter writer = new SpanWriter(span);
packetHeader.WriteTo(ref writer);
requestData.WriteTo(ref writer);
writer.Write(additionalData);
try
{
_socket.SendAll(span);
}
finally
{
ArrayPool<byte>.Shared.Return(array);
}
}
private TResult Receive<TReader, TResult>(CommandId command, uint deviceId) where TReader : struct, ISpanReader<TResult>
{
SpanReader reader = new SpanReader(_pendingRequests[command].Take(_cancellationTokenSource.Token));
return new TReader().ReadFrom(ref reader, CurrentProtocolVersion, (int)deviceId);
}
public TResult Request<TArgument, TReader, TResult>(CommandId command, uint deviceId, TArgument requestData, ReadOnlySpan<byte> additionalData = default(ReadOnlySpan<byte>)) where TArgument : ISpanWritable where TReader : struct, ISpanReader<TResult>
{
Send(command, deviceId, requestData, additionalData);
return Receive<TReader, TResult>(command, deviceId);
}
private uint NegotiateProtocolVersion(uint maxSupportedProtocolVersion)
{
_socket.ReceiveTimeout = 1000;
uint val;
try
{
val = Request<Args<uint>, PrimitiveReader<uint>, uint>(CommandId.RequestProtocolVersion, 0u, new Args<uint>(maxSupportedProtocolVersion));
}
catch (TimeoutException)
{
val = 0u;
}
_socket.ReceiveTimeout = 0;
return Math.Min(val, maxSupportedProtocolVersion);
}
public void Dispose()
{
_cancellationTokenSource.Cancel();
try
{
_readLoopTask?.Wait();
}
catch
{
}
_cancellationTokenSource.Dispose();
_socket.Dispose();
_readLoopTask?.Dispose();
}
[Conditional("DEBUG")]
private static void DebugDumpBuffer(ReadOnlySpan<byte> buffer, bool sending, CommandId command)
{
string text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "OpenRGB.NET");
if (!Directory.Exists(text))
{
Directory.CreateDirectory(text);
}
string text2 = (from f in Directory.EnumerateFiles(text)
orderby f descending
select f).FirstOrDefault();
int num = ((text2 == null) ? (-1) : int.Parse(Path.GetFileNameWithoutExtension(text2).Split('-')[0]));
File.WriteAllBytes(Path.Combine(text, string.Format("{0:D2}-{1}-{2}.bin", num + 1, sending ? "Send" : "Receive", command)), buffer.ToArray());
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct ColorsReader : ISpanReader<Color[]>
{
public Color[] ReadFrom(ref SpanReader reader, ProtocolVersion? protocolVersion = null, int? index = null, int? outerCount = null)
{
ushort num = reader.Read<ushort>();
Color[] array = new Color[num];
for (int i = 0; i < num; i++)
{
byte r = reader.Read<byte>();
byte g = reader.Read<byte>();
byte b = reader.Read<byte>();
reader.Read<byte>();
array[i] = new Color(r, g, b);
}
return array;
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct DeviceReader : ISpanReader<Device>
{
public Device ReadFrom(ref SpanReader reader, ProtocolVersion? protocolVersion = null, int? index = null, int? outerCount = null)
{
if (protocolVersion.HasValue)
{
ProtocolVersion valueOrDefault = protocolVersion.GetValueOrDefault();
if (index.HasValue)
{
int valueOrDefault2 = index.GetValueOrDefault();
reader.Read<uint>();
int type = reader.Read<int>();
string name = reader.ReadLengthAndString();
string vendor = (valueOrDefault.SupportsVendorString ? reader.ReadLengthAndString() : null);
string description = reader.ReadLengthAndString();
string version = reader.ReadLengthAndString();
string serial = reader.ReadLengthAndString();
string location = reader.ReadLengthAndString();
ushort value = reader.Read<ushort>();
int activeModeIndex = reader.Read<int>();
ModesReader modesReader = default(ModesReader);
ProtocolVersion? protocolVersion2 = valueOrDefault;
int? outerCount2 = value;
Mode[] modes = modesReader.ReadFrom(ref reader, protocolVersion2, null, outerCount2);
Zone[] zones = default(ZonesReader).ReadFrom(ref reader, valueOrDefault, valueOrDefault2);
Led[] leds = default(LedsReader).ReadFrom(ref reader);
Color[] colors = default(ColorsReader).ReadFrom(ref reader);
return new Device(valueOrDefault2, (DeviceType)type, name, vendor, description, version, serial, location, activeModeIndex, modes, zones, leds, colors);
}
throw new ArgumentNullException("index");
}
throw new ArgumentNullException("protocolVersion");
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct LedsReader : ISpanReader<Led[]>
{
public Led[] ReadFrom(ref SpanReader reader, ProtocolVersion? protocolVersion = null, int? index = null, int? outerCount = null)
{
ushort num = reader.Read<ushort>();
Led[] array = new Led[num];
for (int i = 0; i < num; i++)
{
string name = reader.ReadLengthAndString();
uint value = reader.Read<uint>();
array[i] = new Led(i, name, value);
}
return array;
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct MatrixMapReader : ISpanReader<MatrixMap>
{
public MatrixMap ReadFrom(ref SpanReader reader, ProtocolVersion? protocolVersion = null, int? index = null, int? outerCount = null)
{
uint num = reader.Read<uint>();
uint num2 = reader.Read<uint>();
uint[,] array = new uint[num, num2];
for (int i = 0; i < num; i++)
{
for (int j = 0; j < num2; j++)
{
array[i, j] = reader.Read<uint>();
}
}
return new MatrixMap(num, num2, array);
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct ModesReader : ISpanReader<Mode[]>
{
public Mode[] ReadFrom(ref SpanReader reader, ProtocolVersion? protocolVersion = null, int? index = null, int? outerCount = null)
{
if (protocolVersion.HasValue)
{
ProtocolVersion valueOrDefault = protocolVersion.GetValueOrDefault();
if (outerCount.HasValue)
{
int valueOrDefault2 = outerCount.GetValueOrDefault();
Mode[] array = new Mode[valueOrDefault2];
for (int i = 0; i < array.Length; i++)
{
string name = reader.ReadLengthAndString();
int value = reader.Read<int>();
ModeFlags flags = (ModeFlags)reader.Read<uint>();
uint speedMin = reader.Read<uint>();
uint speedMax = reader.Read<uint>();
uint brightnessMin = (valueOrDefault.SupportsBrightnessAndSaveMode ? reader.Read<uint>() : 0u);
uint brightnessMax = (valueOrDefault.SupportsBrightnessAndSaveMode ? reader.Read<uint>() : 0u);
uint colorMin = reader.Read<uint>();
uint colorMax = reader.Read<uint>();
uint speed = reader.Read<uint>();
uint brightness = (valueOrDefault.SupportsBrightnessAndSaveMode ? reader.Read<uint>() : 0u);
uint direction = reader.Read<uint>();
ColorMode colorMode = (ColorMode)reader.Read<uint>();
Color[] colors = default(ColorsReader).ReadFrom(ref reader);
array[i] = new Mode(valueOrDefault, i, name, value, flags, speedMin, speedMax, brightnessMin, brightnessMax, colorMin, colorMax, speed, brightness, (Direction)direction, colorMode, colors);
}
return array;
}
throw new ArgumentNullException("outerCount");
}
throw new ArgumentNullException("protocolVersion");
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct PluginsReader : ISpanReader<Plugin[]>
{
public Plugin[] ReadFrom(ref SpanReader reader, ProtocolVersion? p = null, int? i = null, int? outerCount = null)
{
reader.Read<uint>();
ushort num = reader.Read<ushort>();
Plugin[] array = new Plugin[num];
for (int j = 0; j < num; j++)
{
string name = reader.ReadLengthAndString();
string description = reader.ReadLengthAndString();
string version = reader.ReadLengthAndString();
uint index = reader.Read<uint>();
int sdkVersion = reader.Read<int>();
array[j] = new Plugin(name, description, version, index, sdkVersion);
}
return array;
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct PrimitiveReader<T> : ISpanReader<T> where T : unmanaged
{
public T ReadFrom(ref SpanReader reader, ProtocolVersion? protocolVersion = null, int? index = null, int? outerCount = null)
{
return reader.Read<T>();
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct ProfilesReader : ISpanReader<string[]>
{
public string[] ReadFrom(ref SpanReader reader, ProtocolVersion? p = null, int? i = null, int? outerCount = null)
{
reader.Read<uint>();
ushort num = reader.Read<ushort>();
string[] array = new string[num];
for (int j = 0; j < num; j++)
{
array[j] = reader.ReadLengthAndString();
}
return array;
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct ProtocolVersionReader : ISpanReader<ProtocolVersion>
{
public ProtocolVersion ReadFrom(ref SpanReader reader, ProtocolVersion? p = null, int? i = null, int? outerCount = null)
{
return ProtocolVersion.FromNumber(reader.Read<uint>());
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct SegmentsReader : ISpanReader<Segment[]>
{
public Segment[] ReadFrom(ref SpanReader reader, ProtocolVersion? protocolVersion = null, int? index = null, int? outerCount = null)
{
ushort num = reader.Read<ushort>();
Segment[] array = new Segment[num];
for (int i = 0; i < num; i++)
{
string name = reader.ReadLengthAndString();
ZoneType type = (ZoneType)reader.Read<uint>();
uint start = reader.Read<uint>();
uint ledCount = reader.Read<uint>();
array[i] = new Segment(i, name, type, start, ledCount);
}
return array;
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct ZonesReader : ISpanReader<Zone[]>
{
public Zone[] ReadFrom(ref SpanReader reader, ProtocolVersion? protocolVersion = null, int? index = null, int? outerCount = null)
{
if (protocolVersion.HasValue)
{
ProtocolVersion valueOrDefault = protocolVersion.GetValueOrDefault();
if (index.HasValue)
{
int valueOrDefault2 = index.GetValueOrDefault();
ushort num = reader.Read<ushort>();
Zone[] array = new Zone[num];
for (int i = 0; i < num; i++)
{
string name = reader.ReadLengthAndString();
ZoneType type = (ZoneType)reader.Read<uint>();
uint ledsMin = reader.Read<uint>();
uint ledsMax = reader.Read<uint>();
uint ledCount = reader.Read<uint>();
MatrixMap matrixMap = ((reader.Read<ushort>() > 0) ? default(MatrixMapReader).ReadFrom(ref reader) : null);
Segment[] segments = (valueOrDefault.SupportsSegmentsAndPlugins ? default(SegmentsReader).ReadFrom(ref reader, protocolVersion) : Array.Empty<Segment>());
array[i] = new Zone(i, valueOrDefault2, name, type, ledCount, ledsMin, ledsMax, matrixMap, segments);
}
return array;
}
throw new ArgumentNullException("index");
}
throw new ArgumentNullException("protocolVersion");
}
}
internal readonly record struct Args<T1>(T1 Arg1) : ISpanWritable where T1 : unmanaged
{
public unsafe int Length => sizeof(T1);
public void WriteTo(ref SpanWriter writer)
{
writer.Write(Arg1);
}
}
internal readonly record struct Args<T1, T2>(T1 Arg1, T2 Arg2) : ISpanWritable where T1 : unmanaged where T2 : unmanaged
{
public unsafe int Length => sizeof(T1) + sizeof(T2);
public void WriteTo(ref SpanWriter writer)
{
writer.Write(Arg1);
writer.Write(Arg2);
}
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal readonly struct EmptyArg : ISpanWritable
{
public int Length => 0;
public void WriteTo(ref SpanWriter writer)
{
}
}
internal readonly record struct ModeArg(Mode Mode) : ISpanWritable
{
public int Length
{
get
{
int num = 0;
num += 4;
num += 36;
num += Mode.Name.Length + 1;
num += 4 * Mode.Colors.Length;
if (Mode.ProtocolVersion.SupportsBrightnessAndSaveMode)
{
num += 12;
}
return num;
}
}
public void WriteTo(ref SpanWriter writer)
{
writer.WriteLengthAndString(Mode.Name);
writer.Write(Mode.Value);
writer.Write((uint)Mode.Flags);
writer.Write(Mode.SpeedMin);
writer.Write(Mode.SpeedMax);
if (Mode.ProtocolVersion.SupportsBrightnessAndSaveMode)
{
writer.Write(Mode.BrightnessMin);
writer.Write(Mode.BrightnessMax);
}
writer.Write(Mode.ColorMin);
writer.Write(Mode.ColorMax);
writer.Write(Mode.Speed);
if (Mode.ProtocolVersion.SupportsBrightnessAndSaveMode)
{
writer.Write(Mode.Brightness);
}
writer.Write((uint)Mode.Direction);
writer.Write((uint)Mode.ColorMode);
writer.Write((ushort)Mode.Colors.Length);
Span<byte> span = MemoryMarshal.Cast<Color, byte>(Mode.Colors);
writer.Write((ReadOnlySpan<byte>)span);
}
}
internal readonly record struct ModeOperationArg(uint ModeId, ModeArg Mode) : ISpanWritable
{
public int Length => 8 + Mode.Length;
public void WriteTo(ref SpanWriter writer)
{
writer.Write(Length);
writer.Write(ModeId);
Mode.WriteTo(ref writer);
}
}
internal readonly struct PacketHeader : ISpanWritable
{
internal const int LENGTH = 16;
internal static ReadOnlySpan<byte> MagicBytes => "ORGB"u8;
internal uint DeviceId { get; }
internal CommandId Command { get; }
internal uint DataLength { get; }
public int Length => 16;
public PacketHeader(uint deviceId, CommandId command, uint length)
{
DeviceId = deviceId;
Command = command;
DataLength = length;
}
public static PacketHeader FromSpan(ReadOnlySpan<byte> span)
{
SpanReader spanReader = new SpanReader(span);
if (!spanReader.ReadBytes(4).SequenceEqual(MagicBytes))
{
throw new ArgumentException("Magic bytes \"ORGB\" were not found");
}
uint deviceId = spanReader.Read<uint>();
CommandId command = (CommandId)spanReader.Read<uint>();
uint length = spanReader.Read<uint>();
return new PacketHeader(deviceId, command, length);
}
public void WriteTo(ref SpanWriter writer)
{
writer.Write(MagicBytes);
writer.Write(DeviceId);
writer.Write((uint)Command);
writer.Write(DataLength);
}
}
internal readonly record struct ProtocolVersionArg(ProtocolVersion Version) : ISpanWritable
{
public int Length => 4;
public void WriteTo(ref SpanWriter writer)
{
writer.Write(Version.Number);
}
}
internal readonly record struct StringArg(string Value) : ISpanWritable
{
public int Length => Value.Length + 1;
public void WriteTo(ref SpanWriter writer)
{
writer.Write(Value);
}
}
internal readonly record struct UpdateLedsArg(ushort ColorCount) : ISpanWritable
{
public int Length => 6;
public unsafe void WriteTo(ref SpanWriter writer)
{
writer.Write((uint)(Length + sizeof(Color) * ColorCount));
writer.Write(ColorCount);
}
}
internal readonly record struct UpdateZoneLedsArg(uint ZoneId, ushort ColorCount) : ISpanWritable
{
public int Length => 10;
public unsafe void WriteTo(ref SpanWriter writer)
{
writer.Write((uint)(Length + sizeof(Color) * ColorCount));
writer.Write(ZoneId);
writer.Write(ColorCount);
}
}
[BepInPlugin("ksm.OpenRGB", "OpenRGB-BepInEx", "1.0.0")]
public class OpenRGBBepInEx : BaseUnityPlugin
{
public static OpenRGBBepInEx Instance { get; private set; }
internal static ManualLogSource Logger { get; private set; }
private void Awake()
{
Logger = ((BaseUnityPlugin)this).Logger;
Instance = this;
Logger.LogInfo((object)"ksm.OpenRGB v1.0.0 has loaded!");
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "ksm.OpenRGB";
public const string PLUGIN_NAME = "OpenRGB-BepInEx";
public const string PLUGIN_VERSION = "1.0.0";
}
}
namespace ksm.OpenRGB.Utils
{
public static class ColorUtils
{
public static Color FromHsv(double hue, double saturation, double value)
{
if ((saturation < 0.0 || saturation > 1.0) ? true : false)
{
throw new ArgumentOutOfRangeException("saturation");
}
if ((value < 0.0 || value > 1.0) ? true : false)
{
throw new ArgumentOutOfRangeException("value");
}
while (hue < 0.0)
{
hue += 360.0;
}
while (hue >= 360.0)
{
hue -= 360.0;
}
int num = Convert.ToInt32(Math.Floor(hue / 60.0)) % 6;
double num2 = hue / 60.0 - Math.Floor(hue / 60.0);
value *= 255.0;
byte b = Convert.ToByte(value);
byte b2 = Convert.ToByte(value * (1.0 - saturation));
byte b3 = Convert.ToByte(value * (1.0 - num2 * saturation));
byte b4 = Convert.ToByte(value * (1.0 - (1.0 - num2) * saturation));
return num switch
{
0 => new Color(b, b4, b2),
1 => new Color(b3, b, b2),
2 => new Color(b2, b, b4),
3 => new Color(b2, b3, b),
4 => new Color(b4, b2, b),
_ => new Color(b, b2, b3),
};
}
public static (double h, double s, double v) ToHsv(this Color clr)
{
byte b = Math.Max(clr.R, Math.Max(clr.G, clr.B));
byte b2 = Math.Min(clr.R, Math.Min(clr.G, clr.B));
int num = b - b2;
double num2 = 0.0;
if (num != 0)
{
if (clr.R == b)
{
num2 = (double)(clr.G - clr.B) / (double)num;
}
else if (clr.G == b)
{
num2 = 2.0 + (double)(clr.B - clr.R) / (double)num;
}
else if (clr.B == b)
{
num2 = 4.0 + (double)(clr.R - clr.G) / (double)num;
}
}
num2 *= 60.0;
if (num2 < 0.0)
{
num2 += 360.0;
}
double item = ((b == 0) ? 0.0 : (1.0 - 1.0 * (double)(int)b2 / (double)(int)b));
double item2 = (double)(int)b / 255.0;
return (num2, item, item2);
}
public static IEnumerable<Color> GetHueRainbow(int amount, double hueStart = 0.0, double huePercent = 1.0, double saturation = 1.0, double value = 1.0)
{
return from i in Enumerable.Range(0, amount)
select FromHsv(hueStart + 360.0 * huePercent / (double)amount * (double)i, saturation, value);
}
public static IEnumerable<Color> GetSinRainbow(int amount, int floor = 127, int width = 128, double range = 1.0, double offset = Math.PI / 2.0)
{
return from i in Enumerable.Range(0, amount)
select new Color((byte)((double)floor + (double)width * Math.Sin(offset + Math.PI * 2.0 * range / (double)amount * (double)i + 0.0)), (byte)((double)floor + (double)width * Math.Sin(offset + Math.PI * 2.0 * range / (double)amount * (double)i + Math.PI * 2.0 / 3.0)), (byte)((double)floor + (double)width * Math.Sin(offset + Math.PI * 2.0 * range / (double)amount * (double)i + 4.1887902047863905)));
}
}
internal static class SocketExtensions
{
public static void Connect(this Socket socket, string host, int port, int timeoutMs, CancellationToken cancellationToken)
{
Task task = SocketTaskExtensions.ConnectAsync(socket, host, port);
Task.WaitAny(new Task[1] { task }, timeoutMs, cancellationToken);
if (socket.Connected)
{
return;
}
socket.Close();
throw new TimeoutException("Could not connect to OpenRGB");
}
public static async Task ReceiveAllAsync(this Socket socket, Memory<byte> buffer, CancellationToken cancellationToken)
{
int num2;
for (int recv = 0; recv < buffer.Length; recv += num2)
{
int num = recv;
num2 = await SocketTaskExtensions.ReceiveAsync(socket, buffer.Slice(num, buffer.Length - num), SocketFlags.None, cancellationToken);
if (num2 == 0)
{
break;
}
}
}
public static async Task SendAllAsync(this Socket socket, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
{
int num;
for (int sent = 0; sent < buffer.Length; sent += await SocketTaskExtensions.SendAsync(socket, buffer.Slice(num, buffer.Length - num), SocketFlags.None, cancellationToken))
{
num = sent;
}
}
public static void SendAll(this Socket socket, ReadOnlySpan<byte> buffer)
{
int num2;
for (int i = 0; i < buffer.Length; i += num2)
{
int num = i;
num2 = socket.Send(buffer.Slice(num, buffer.Length - num));
}
}
}
internal ref struct SpanReader
{
private ReadOnlySpan<byte> Span { get; }
private int Position { get; set; }
public SpanReader(ReadOnlySpan<byte> span)
{
Span = span;
Position = 0;
}
internal unsafe T Read<T>() where T : unmanaged
{
ReadOnlySpan<byte> span = Span;
int position = Position;
T result = MemoryMarshal.Read<T>(span.Slice(position, span.Length - position));
Position += sizeof(T);
return result;
}
public ReadOnlySpan<byte> ReadBytes(int length)
{
ReadOnlySpan<byte> span = Span;
int position = Position;
ReadOnlySpan<byte> result = span.Slice(position, Position + length - position);
Position += length;
return result;
}
public string ReadLengthAndString()
{
int num = Read<ushort>();
if (num == 0)
{
return string.Empty;
}
Encoding aSCII = Encoding.ASCII;
ReadOnlySpan<byte> readOnlySpan = ReadBytes(num);
return aSCII.GetString(readOnlySpan.Slice(0, readOnlySpan.Length - 1));
}
}
internal ref struct SpanWriter
{
public Span<byte> Span { get; }
public int Position { get; private set; }
public SpanWriter(Span<byte> span)
{
Span = span;
Position = 0;
}
public unsafe void Write<T>(T value) where T : unmanaged
{
Span<byte> span = Span;
int position = Position;
MemoryMarshal.Write(span.Slice(position, span.Length - position), ref value);
Position += sizeof(T);
}
public void Write(ReadOnlySpan<byte> span)
{
Span<byte> span2 = Span;
int position = Position;
span.CopyTo(span2.Slice(position, span2.Length - position));
Position += span.Length;
}
public void WriteLengthAndString(string value)
{
Write((ushort)(value.Length + 1));
Write(value);
}
public void Write(string value)
{
int byteCount = Encoding.ASCII.GetByteCount(value.AsSpan());
Encoding.ASCII.GetBytes(value.AsSpan(), Span.Slice(Position, byteCount));
Position += byteCount;
Write((byte)0);
}
}
}