diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj index 9a20f58be..c3848c7c9 100644 --- a/Artemis/Artemis/Artemis.csproj +++ b/Artemis/Artemis/Artemis.csproj @@ -163,10 +163,6 @@ False - - ..\packages\NamedPipeWrapper.1.4.0\lib\net40\NamedPipeWrapper.dll - True - ..\packages\NAudio.1.7.3\lib\net35\NAudio.dll True @@ -371,7 +367,8 @@ - + + diff --git a/Artemis/Artemis/KeyboardProviders/Logitech/Utilities/LogitechGSDK.cs b/Artemis/Artemis/KeyboardProviders/Logitech/Utilities/LogitechGSDK.cs index 87495914c..b3e1bd9ed 100644 --- a/Artemis/Artemis/KeyboardProviders/Logitech/Utilities/LogitechGSDK.cs +++ b/Artemis/Artemis/KeyboardProviders/Logitech/Utilities/LogitechGSDK.cs @@ -68,7 +68,7 @@ namespace Artemis.KeyboardProviders.Logitech.Utilities int greenPercentage, int bluePercentage); [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] - public static extern bool LogiLedSetLightingForKeyWithKeyName(KeyboardNames keyCode, int redPercentage, + public static extern bool LogiLedSetLightingForKeyWithKeyName(int keyCode, int redPercentage, int greenPercentage, int bluePercentage); [DllImport("LogitechLedEnginesWrapper ", CallingConvention = CallingConvention.Cdecl)] diff --git a/Artemis/Artemis/Managers/MainManager.cs b/Artemis/Artemis/Managers/MainManager.cs index 444aa2c88..79e394518 100644 --- a/Artemis/Artemis/Managers/MainManager.cs +++ b/Artemis/Artemis/Managers/MainManager.cs @@ -2,11 +2,13 @@ using System.Diagnostics; using System.Linq; using System.Threading; +using System.Windows.Forms; using Artemis.Events; using Artemis.Models; using Artemis.Services; using Artemis.Utilities.GameState; using Artemis.Utilities.Keyboard; +using Artemis.Utilities.LogitechDll; using Caliburn.Micro; namespace Artemis.Managers @@ -47,8 +49,13 @@ namespace Artemis.Managers // Create and start the web server GameStateWebServer = new GameStateWebServer(); GameStateWebServer.Start(); + + // Start the named pipe + PipeServer = new PipeServer(); + PipeServer.Start("artemis"); } + public PipeServer PipeServer { get; set; } public BackgroundWorker UpdateWorker { get; set; } public BackgroundWorker ProcessWorker { get; set; } @@ -65,6 +72,7 @@ namespace Artemis.Managers public bool Suspended { get; set; } public bool Running { get; private set; } + public event PauseCallbackHandler PauseCallback; /// @@ -137,7 +145,9 @@ namespace Artemis.Managers { Stop(); ProcessWorker.CancelAsync(); + ProcessWorker.CancelAsync(); GameStateWebServer.Stop(); + //NamedPipeServer.StopServer(); } public void Restart() diff --git a/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs b/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs index a8b25b42e..92dc1a9a6 100644 --- a/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs +++ b/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs @@ -1,6 +1,10 @@ -using System.Drawing; +using System; +using System.Diagnostics; +using System.Drawing; +using Artemis.KeyboardProviders.Logitech.Utilities; using Artemis.Managers; using Artemis.Models; +using Artemis.Utilities.LogitechDll; namespace Artemis.Modules.Games.TheDivision { @@ -23,17 +27,42 @@ namespace Artemis.Modules.Games.TheDivision public override void Dispose() { Initialized = false; + DllManager.RestoreDll(); } public override void Enable() { Initialized = false; - - // Enable logic, if any - + DllManager.PlaceDll(); + MainManager.PipeServer.PipeMessage += PipeServerOnPipeMessage; Initialized = true; } + private void PipeServerOnPipeMessage(string reply) + { + // Convert the given string to a list of ints + var stringParts = reply.Split(' '); + var parts = new int[stringParts.Length]; + for (var i = 0; i < stringParts.Length; i++) + parts[i] = int.Parse(stringParts[i]); + + if (parts[0] == 1) + InterpertrateDivisionKey(parts); + + } + + // Parses Division key data to game data + private void InterpertrateDivisionKey(int[] parts) + { + // F1 to F4 indicate the player and his party. Blinks red on damage taken + + // R blinks white when low on ammo + + // G turns white when holding a grenade, turns off when out of grenades + + // V blinks on low HP + } + public override void Update() { } diff --git a/Artemis/Artemis/Resources/LogitechLED.dll b/Artemis/Artemis/Resources/LogitechLED.dll index 9ecba3c77..2d722626e 100644 Binary files a/Artemis/Artemis/Resources/LogitechLED.dll and b/Artemis/Artemis/Resources/LogitechLED.dll differ diff --git a/Artemis/Artemis/Utilities/LogitechDll/DllManager.cs b/Artemis/Artemis/Utilities/LogitechDll/DllManager.cs index 1485c2272..739968f69 100644 --- a/Artemis/Artemis/Utilities/LogitechDll/DllManager.cs +++ b/Artemis/Artemis/Utilities/LogitechDll/DllManager.cs @@ -10,14 +10,17 @@ namespace Artemis.Utilities.LogitechDll public static bool RestoreDll() { - if (!File.Exists(LogitechPath + @"\LogitechLed.dll.bak")) + if (!File.Exists(LogitechPath + @"\LogitechLed.dll") || !File.Exists(LogitechPath + @"\artemis.txt")) return false; - + // Get rid of our own DLL File.Delete(LogitechPath + @"\LogitechLed.dll"); + // Restore the backup - File.Move(LogitechPath + @"\LogitechLed.dll.bak", - LogitechPath + @"\LogitechLed.dll"); + if (File.Exists(LogitechPath + @"\LogitechLed.dll.bak")) + File.Move(LogitechPath + @"\LogitechLed.dll.bak", LogitechPath + @"\LogitechLed.dll"); + + File.Delete(LogitechPath + @"\artemis.txt"); return true; } @@ -43,6 +46,9 @@ namespace Artemis.Utilities.LogitechDll File.WriteAllBytes(LogitechPath + @"\LogitechLED.dll", Resources.LogitechLED); + // A token to show the file is placed + File.Create(LogitechPath + @"\artemis.txt"); + // If the user doesn't have a Logitech device, the CLSID will be missing // and we should create it ourselves. if (!RegistryKeyPlaced()) @@ -56,7 +62,7 @@ namespace Artemis.Utilities.LogitechDll if (!RegistryKeyPlaced()) return false; - return File.Exists(LogitechPath + @"\LogitechLed.dll"); + return File.Exists(LogitechPath + @"\artemis.txt"); } private static bool RegistryKeyPlaced() diff --git a/Artemis/Artemis/Utilities/LogitechDll/LogitechNamedPipe.cs b/Artemis/Artemis/Utilities/LogitechDll/LogitechNamedPipe.cs deleted file mode 100644 index 99a2bbfef..000000000 --- a/Artemis/Artemis/Utilities/LogitechDll/LogitechNamedPipe.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Diagnostics; -using NamedPipeWrapper; - -namespace Artemis.Utilities.LogitechDll -{ - public class LogitechNamedPipe - { - public LogitechNamedPipe() - { - LogitechPipe = new NamedPipeServer("ArtemisLogitech"); - - LogitechPipe.ClientMessage += LogitechPipeOnClientMessage; - LogitechPipe.Start(); - } - - public NamedPipeServer LogitechPipe { get; set; } - - private void LogitechPipeOnClientMessage(NamedPipeConnection connection, string message) - { - Debug.WriteLine(message); - } - } -} \ No newline at end of file diff --git a/Artemis/Artemis/Utilities/LogitechDll/NamedPipeServer.cs b/Artemis/Artemis/Utilities/LogitechDll/NamedPipeServer.cs new file mode 100644 index 000000000..5557ad0bf --- /dev/null +++ b/Artemis/Artemis/Utilities/LogitechDll/NamedPipeServer.cs @@ -0,0 +1,189 @@ +using System; +using System.IO; +using System.IO.Pipes; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using Microsoft.Win32.SafeHandles; + +namespace Artemis.Utilities.LogitechDll +{ + public class NamedPipeServer + { + public const uint DUPLEX = 0x00000003; + public const uint FILE_FLAG_OVERLAPPED = 0x40000000; + + public const int BUFFER_SIZE = 100; + private SafeFileHandle clientHandle; + public Client clientse; + public int ClientType; + private Thread listenThread; + + public string pipeName; + + public NamedPipeServer(string PName, int Mode) + { + pipeName = PName; + ClientType = Mode; //0 Reading Pipe, 1 Writing Pipe + } + + public event PipeDataReceivedEventHandler PipeDataReceived; + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern SafeFileHandle CreateNamedPipe( + string pipeName, + uint dwOpenMode, + uint dwPipeMode, + uint nMaxInstances, + uint nOutBufferSize, + uint nInBufferSize, + uint nDefaultTimeOut, + IntPtr lpSecurityAttributes); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int ConnectNamedPipe( + SafeFileHandle hNamedPipe, + IntPtr lpOverlapped); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int DisconnectNamedPipe( + SafeFileHandle hNamedPipe); + + public void Start() + { + listenThread = new Thread(ListenForClients); + listenThread.Start(); + } + + private void ListenForClients() + { + while (true) + { + clientHandle = CreateNamedPipe(pipeName, DUPLEX | FILE_FLAG_OVERLAPPED, 0, 255, BUFFER_SIZE, BUFFER_SIZE, + 0, IntPtr.Zero); + //could not create named pipe + if (clientHandle.IsInvalid) + return; + + var success = ConnectNamedPipe(clientHandle, IntPtr.Zero); + + //could not connect client + if (success == 0) + return; + + clientse = new Client(); + clientse.handle = clientHandle; + clientse.stream = new FileStream(clientse.handle, FileAccess.ReadWrite, BUFFER_SIZE, true); + + if (ClientType == 0) + { + var readThread = new Thread(Read); + readThread.Start(); + } + } + } + + private void Read() + { + //Client client = (Client)clientObj; + //clientse.stream = new FileStream(clientse.handle, FileAccess.ReadWrite, BUFFER_SIZE, true); + byte[] buffer = null; + var encoder = new ASCIIEncoding(); + + while (true) + { + var bytesRead = 0; + + try + { + buffer = new byte[BUFFER_SIZE]; + bytesRead = clientse.stream.Read(buffer, 0, BUFFER_SIZE); + } + catch + { + //read error has occurred + break; + } + + //client has disconnected + if (bytesRead == 0) + break; + + //fire message received event + //if (this.MessageReceived != null) + // this.MessageReceived(clientse, encoder.GetString(buffer, 0, bytesRead)); + + var ReadLength = 0; + for (var i = 0; i < BUFFER_SIZE; i++) + { + if (buffer[i].ToString("x2") != "cc") + { + ReadLength++; + } + else + break; + } + if (ReadLength > 0) + { + var Rc = new byte[ReadLength]; + Buffer.BlockCopy(buffer, 0, Rc, 0, ReadLength); + OnPipeDataReceived(new PipeDataReceivedEventArgs(encoder.GetString(Rc, 0, ReadLength))); + + buffer.Initialize(); + } + } + + //clean up resources + clientse.stream.Close(); + clientse.handle.Close(); + } + + public void SendMessage(string message, Client client) + { + var encoder = new ASCIIEncoding(); + var messageBuffer = encoder.GetBytes(message); + + if (client.stream.CanWrite) + { + client.stream.Write(messageBuffer, 0, messageBuffer.Length); + client.stream.Flush(); + } + } + + public void StopServer() + { + //clean up resources + + DisconnectNamedPipe(clientHandle); + + + listenThread.Abort(); + } + + private void OnPipeDataReceived(PipeDataReceivedEventArgs e) + { + PipeDataReceived?.Invoke(this, e); + } + + public class Client + { + public SafeFileHandle handle; + public FileStream stream; + } + } + + public delegate void PipeDataReceivedEventHandler( + object sender, PipeDataReceivedEventArgs pipeDataReceivedEventArgs); + + public class PipeDataReceivedEventArgs + { + public PipeDataReceivedEventArgs(string data) + { + Data = data; + } + + public string Data { get; set; } + } + + +} \ No newline at end of file diff --git a/Artemis/Artemis/Utilities/LogitechDll/PipeServer.cs b/Artemis/Artemis/Utilities/LogitechDll/PipeServer.cs new file mode 100644 index 000000000..bb0ac497e --- /dev/null +++ b/Artemis/Artemis/Utilities/LogitechDll/PipeServer.cs @@ -0,0 +1,132 @@ +using System; +using System.Diagnostics; +using System.IO.Pipes; +using System.Security.AccessControl; +using System.Security.Principal; +using System.Text; +using System.Threading.Tasks; + +namespace Artemis.Utilities.LogitechDll +{ + // Delegate for passing received message back to caller + public delegate void DelegateMessage(string reply); + + public class PipeServer + { + private string _pipeName; + + public bool Running { get; set; } + public event DelegateMessage PipeMessage; + + public void Start(string pipeName) + { + Running = true; + _pipeName = pipeName; + var task = new Task(PipeLoop); + task.Start(); + } + + public void Stop() + { + Running = false; + } + + private void PipeLoop() + { + try + { + var security = new PipeSecurity(); + var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null); + security.AddAccessRule(new PipeAccessRule(sid, PipeAccessRights.FullControl, + AccessControlType.Allow)); + + while (Running) + { + var namedPipeServerStream = new NamedPipeServerStream(_pipeName, PipeDirection.In, 100, + PipeTransmissionMode.Byte, PipeOptions.None, 100, 100, security); + + namedPipeServerStream.WaitForConnection(); + var buffer = new byte[100]; + namedPipeServerStream.Read(buffer, 0, 100); + namedPipeServerStream.Close(); + + var task = new Task(() => HandleMessage(buffer)); + task.Start(); + } + } + catch + { + // ignored + } + } + + private void HandleMessage(byte[] buffer) + { + var request = Encoding.ASCII.GetString(buffer); + PipeMessage?.Invoke(request); + } + + public void Listen(string pipeName) + { + try + { + var security = new PipeSecurity(); + var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null); + security.AddAccessRule(new PipeAccessRule(sid, PipeAccessRights.FullControl, AccessControlType.Allow)); + + // Set to class level var so we can re-use in the async callback method + _pipeName = pipeName; + // Create the new async pipe + var pipeServer = new NamedPipeServerStream(pipeName, PipeDirection.In, 100, PipeTransmissionMode.Byte, + PipeOptions.Asynchronous, 100, 100, security); + + // Wait for a connection + pipeServer.BeginWaitForConnection(WaitForConnectionCallBack, pipeServer); + } + catch (Exception oEx) + { + Debug.WriteLine(oEx.Message); + } + } + + private void WaitForConnectionCallBack(IAsyncResult iar) + { + try + { + // Get the pipe + var pipeServer = (NamedPipeServerStream) iar.AsyncState; + // End waiting for the connection + pipeServer.EndWaitForConnection(iar); + + var buffer = new byte[255]; + + // Read the incoming message + pipeServer.Read(buffer, 0, 255); + + // Convert byte buffer to string + var stringData = Encoding.UTF8.GetString(buffer, 0, buffer.Length); + Debug.WriteLine(stringData + Environment.NewLine); + + // Pass message back to calling form + PipeMessage?.Invoke(stringData); + + // Kill original sever and create new wait server + pipeServer.Close(); + + var security = new PipeSecurity(); + var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null); + security.AddAccessRule(new PipeAccessRule(sid, PipeAccessRights.FullControl, AccessControlType.Allow)); + + pipeServer = new NamedPipeServerStream(_pipeName, PipeDirection.In, 100, PipeTransmissionMode.Byte, + PipeOptions.Asynchronous, 100, 100, security); + + // Recursively wait for the connection again and again.... + pipeServer.BeginWaitForConnection(WaitForConnectionCallBack, pipeServer); + } + catch + { + // ignored + } + } + } +} \ No newline at end of file diff --git a/Artemis/Artemis/packages.config b/Artemis/Artemis/packages.config index d5231ab31..0b67a3431 100644 --- a/Artemis/Artemis/packages.config +++ b/Artemis/Artemis/packages.config @@ -12,7 +12,6 @@ -