From 04158dce391eb565bac22cf887c41aac3f633caf Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Sun, 20 Mar 2016 17:46:07 +0100 Subject: [PATCH] Added C++ project --- .gitignore | 1 + Artemis/Artemis.sln | 30 +++ Artemis/Artemis/DAL/ProfileProvider.cs | 38 ++- .../KeyboardProviders/Logitech/Orion.cs | 9 +- .../Games/TheDivision/TheDivisionModel.cs | 2 +- Artemis/Artemis/Resources/LogitechLED.dll | Bin 171520 -> 30720 bytes Artemis/LogiLed2Artemis/Logger.cpp | 104 +++++++ Artemis/LogiLed2Artemis/Logger.h | 82 ++++++ Artemis/LogiLed2Artemis/LogiLed2Artemis.def | 49 ++++ .../LogiLed2Artemis/LogiLed2Artemis.vcxproj | 168 ++++++++++++ .../LogiLed2Artemis.vcxproj.filters | 41 +++ Artemis/LogiLed2Artemis/LogiLedDefs.h | 135 ++++++++++ Artemis/LogiLed2Artemis/main.cpp | 254 ++++++++++++++++++ Artemis/LogiLed2Artemis/main.h | 2 + 14 files changed, 911 insertions(+), 4 deletions(-) create mode 100644 Artemis/LogiLed2Artemis/Logger.cpp create mode 100644 Artemis/LogiLed2Artemis/Logger.h create mode 100644 Artemis/LogiLed2Artemis/LogiLed2Artemis.def create mode 100644 Artemis/LogiLed2Artemis/LogiLed2Artemis.vcxproj create mode 100644 Artemis/LogiLed2Artemis/LogiLed2Artemis.vcxproj.filters create mode 100644 Artemis/LogiLed2Artemis/LogiLedDefs.h create mode 100644 Artemis/LogiLed2Artemis/main.cpp create mode 100644 Artemis/LogiLed2Artemis/main.h diff --git a/.gitignore b/.gitignore index 5115c12d8..98ca98966 100644 --- a/.gitignore +++ b/.gitignore @@ -189,3 +189,4 @@ FakesAssemblies/ # Visual Studio 6 workspace options file *.opt +*.opendb diff --git a/Artemis/Artemis.sln b/Artemis/Artemis.sln index cf6e97100..201149a73 100644 --- a/Artemis/Artemis.sln +++ b/Artemis/Artemis.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Artemis", "Artemis\Artemis.csproj", "{ED9997A2-E54C-4E9F-9350-62BE672C3ABE}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LogiLed2Artemis", "LogiLed2Artemis\LogiLed2Artemis.vcxproj", "{D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CD_ROM|Any CPU = CD_ROM|Any CPU @@ -54,6 +56,34 @@ Global {ED9997A2-E54C-4E9F-9350-62BE672C3ABE}.SingleImage|x64.Build.0 = Release|x64 {ED9997A2-E54C-4E9F-9350-62BE672C3ABE}.SingleImage|x86.ActiveCfg = Release|x86 {ED9997A2-E54C-4E9F-9350-62BE672C3ABE}.SingleImage|x86.Build.0 = Release|x86 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.CD_ROM|Any CPU.ActiveCfg = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.CD_ROM|Any CPU.Build.0 = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.CD_ROM|x64.ActiveCfg = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.CD_ROM|x64.Build.0 = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.CD_ROM|x86.ActiveCfg = Release|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.CD_ROM|x86.Build.0 = Release|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.Debug|x64.ActiveCfg = Debug|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.Debug|x64.Build.0 = Debug|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.Debug|x86.ActiveCfg = Debug|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.Debug|x86.Build.0 = Debug|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.DVD-5|Any CPU.ActiveCfg = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.DVD-5|Any CPU.Build.0 = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.DVD-5|x64.ActiveCfg = Debug|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.DVD-5|x64.Build.0 = Debug|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.DVD-5|x86.ActiveCfg = Debug|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.DVD-5|x86.Build.0 = Debug|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.Release|Any CPU.ActiveCfg = Release|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.Release|x64.ActiveCfg = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.Release|x64.Build.0 = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.Release|x86.ActiveCfg = Release|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.Release|x86.Build.0 = Release|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.SingleImage|Any CPU.ActiveCfg = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.SingleImage|Any CPU.Build.0 = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.SingleImage|x64.ActiveCfg = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.SingleImage|x64.Build.0 = Release|x64 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.SingleImage|x86.ActiveCfg = Release|Win32 + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C}.SingleImage|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Artemis/Artemis/DAL/ProfileProvider.cs b/Artemis/Artemis/DAL/ProfileProvider.cs index a3d3aadd1..09a60f6e3 100644 --- a/Artemis/Artemis/DAL/ProfileProvider.cs +++ b/Artemis/Artemis/DAL/ProfileProvider.cs @@ -1,17 +1,24 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; using System.Linq; using Artemis.Models; +using Newtonsoft.Json; namespace Artemis.DAL { internal class ProfileProvider { + private List _profiles; + /// /// Get all profiles /// /// All profiles public List GetAll() { + ReadProfiles(); return new List(); } @@ -33,5 +40,34 @@ namespace Artemis.DAL public void AddOrUpdate(ProfileModel profile) { } + + private void ReadProfiles() + { + CheckProfiles(); + _profiles = new List(); + + // Create the directory structure + var profileFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Artemis\profiles"; + var profileFiles = Directory.GetFiles(profileFolder, "*.json", SearchOption.AllDirectories); + + // Parse the JSON files into objects and add them if they are valid + foreach (var file in profileFiles) + { + var prof = JsonConvert.DeserializeObject(File.ReadAllText(file)); + if (prof.GameName?.Length > 1 && prof.KeyboardName?.Length > 1 && prof.Name?.Length > 1) + _profiles.Add(prof); + } + } + + private void CheckProfiles() + { + // Create the directory structure + var profileFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Artemis\profiles"; + if (Directory.Exists(profileFolder)) + return; + + Directory.CreateDirectory(profileFolder); + Debug.WriteLine("Place presets"); + } } } \ No newline at end of file diff --git a/Artemis/Artemis/KeyboardProviders/Logitech/Orion.cs b/Artemis/Artemis/KeyboardProviders/Logitech/Orion.cs index eb7a76195..d6fa0155e 100644 --- a/Artemis/Artemis/KeyboardProviders/Logitech/Orion.cs +++ b/Artemis/Artemis/KeyboardProviders/Logitech/Orion.cs @@ -18,7 +18,7 @@ namespace Artemis.KeyboardProviders.Logitech Name = "Logitech G910 RGB"; CantEnableText = "Couldn't connect to your Logitech G910.\n" + "Please check your cables and updating the Logitech Gaming Software\n" + - $"A minimum version of 8.81.15 is required. (Detected {_versionString})\n\n" + + "A minimum version of 8.81.15 is required.\n\n" + "If needed, you can select a different keyboard in Artemis under settings."; Height = 6; Width = 21; @@ -42,7 +42,12 @@ namespace Artemis.KeyboardProviders.Logitech // Turn it into one long number... var version = int.Parse($"{majorNum}{minorNum}{buildNum}"); - _versionString = $"{majorNum}.{minorNum}.{buildNum}"; + CantEnableText = "Couldn't connect to your Logitech G910.\n" + + "Please check your cables and updating the Logitech Gaming Software\n" + + $"A minimum version of 8.81.15 is required (detected {majorNum}.{minorNum}.{buildNum}).\n\n" + + "If the detected version differs from the version LGS is reporting, reinstall LGS or see the FAQ.\n\n" + + "If needed, you can select a different keyboard in Artemis under settings."; + return version >= 88115; } diff --git a/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs b/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs index 28caad6f6..b5b025e87 100644 --- a/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs +++ b/Artemis/Artemis/Modules/Games/TheDivision/TheDivisionModel.cs @@ -118,7 +118,7 @@ namespace Artemis.Modules.Games.TheDivision } // Parses Division key data to game data - private void InterpertrateDivisionKey(int[] parts) + private void InterpertrateDivisionKey(IReadOnlyList parts) { var keyCode = parts[1]; var rPer = parts[2]; diff --git a/Artemis/Artemis/Resources/LogitechLED.dll b/Artemis/Artemis/Resources/LogitechLED.dll index 2d722626e732787023621b9594aeab19152359b2..044b0e79959ec3b325a7e0b7192f7861d165de86 100644 GIT binary patch literal 30720 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 J=YNp~{tuncvDp9s literal 171520 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 U% +#include +#include + + +// Ok this looks butt-ugleh but its just to force the default log level to debug if we compile in debug +#ifdef _DEBUG +LogLevel CLogger::m_eLogLevel = LogLevel::Debug; +#else +LogLevel CLogger::m_eLogLevel = LogLevel::All; +#endif +FILE* CLogger::m_pFile = 0; + +void CLogger::InitLogging(const char* szFile) +{ + if (!m_pFile) + { + m_pFile = fopen(szFile, "a+"); + if (!m_pFile) + { + // Hum, we couldn't open the file for writing + printf("ERROR: Unable to open log file %s.\n", szFile); + } + } +} + +void CLogger::EndLogging(void) +{ + if (m_pFile) + { + fclose(m_pFile); + m_pFile = 0; + } +} + +void CLogger::OutputLog_s(const char* sz, const LogLevel eLevel) +{ + // If the level of this log entry is less important than what we are told to log, just return + if (eLevel < m_eLogLevel) + return; + + // If we don't have a file to write to, bail. + if (!m_pFile) + return; + + char szOutput[512] = {0}; + + time_t rawtime; + struct tm* timeinfo; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + // Timestamp the log entry + size_t uiLen = sprintf(szOutput, "<%.2d/%.2d/%.2d - %.2d:%.2d:%.2d> %s\n", timeinfo->tm_mday, timeinfo->tm_mon + 1, timeinfo->tm_year + 1900, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, sz); + + // Write the text to the file + fwrite(szOutput, 1, uiLen, m_pFile); + // Flush the log file to the disk. May move this to a seperate function + fflush(m_pFile); + + // Output the text to any console that may be attached + printf("%s", szOutput); +} + +void CLogger::OutputLog(const char* sz, const LogLevel eLevel, ...) +{ + char szText[1024] = {0}; + va_list marker; + va_start(marker, eLevel); + vsprintf(szText, sz, marker); + va_end(marker); + + // Since this function is pretty much the same as the safer function, we'll just redirect it there + OutputLog_s(szText, eLevel); +} + diff --git a/Artemis/LogiLed2Artemis/Logger.h b/Artemis/LogiLed2Artemis/Logger.h new file mode 100644 index 000000000..41cab02d2 --- /dev/null +++ b/Artemis/LogiLed2Artemis/Logger.h @@ -0,0 +1,82 @@ +// Original work by VRocker https://github.com/VRocker/LogiLed2Corsair +// I'm mainly a C# developer, and these modification aren't a piece of art, but it suits our needs. + +// The MIT License (MIT) +// +// Copyright (c) 2015 VRocker +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once +#ifndef _LOGGER_H +#define _LOGGER_H + +#include + +enum class LogLevel +{ + Debug, + All, + Critical, + Warning, + Information, + User, + Internal, + None, + + // Just to force the compiler to treat the enum options as an int + FORCE_32BIT = 0x7fffffff +}; + +class CLogger +{ +public: + static LogLevel GetLogLevel(void) + { + return m_eLogLevel; + } + + static void SetLogLevel(const LogLevel e) + { +#ifndef _DEBUG + if ( e == LogLevel::Debug ) return; +#endif + m_eLogLevel = e; + } + + static void InitLogging(const char* szFile); + // Always remember to end logging when you are finished otherwise you will have file handles floating around + static void EndLogging(void); + + // Output the text to a log file. + static void OutputLog_s(const char* sz, const LogLevel eLevel); + static void OutputLog(const char* sz, const LogLevel eLevel, ...); + + static FILE* GetFile() + { + return m_pFile; + } + +private: + static LogLevel m_eLogLevel; + static FILE* m_pFile; +}; + +#endif + diff --git a/Artemis/LogiLed2Artemis/LogiLed2Artemis.def b/Artemis/LogiLed2Artemis/LogiLed2Artemis.def new file mode 100644 index 000000000..ef38be3f1 --- /dev/null +++ b/Artemis/LogiLed2Artemis/LogiLed2Artemis.def @@ -0,0 +1,49 @@ +// Original work by VRocker https://github.com/VRocker/LogiLed2Corsair +// I'm mainly a C# developer, and these modification aren't a piece of art, but it suits our needs. + +// The MIT License (MIT) +// +// Copyright (c) 2015 VRocker +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +LIBRARY LogiLed2Artemis.dll + +EXPORTS + + LogiLedInit + LogiLedGetSdkVersion + LogiLedSetTargetDevice + LogiLedSaveCurrentLighting + LogiLedSetLighting + LogiLedRestoreLighting + LogiLedFlashLighting + LogiLedPulseLighting + LogiLedStopEffects + LogiLedSetLightingFromBitmap + LogiLedSetLightingForKeyWithScanCode + LogiLedSetLightingForKeyWithHidCode + LogiLedSetLightingForKeyWithQuartzCode + LogiLedSetLightingForKeyWithKeyName + LogiLedSaveLightingForKey + LogiLedRestoreLightingForKey + LogiLedFlashSingleKey + LogiLedPulseSingleKey + LogiLedStopEffectsOnKey + LogiLedShutdown diff --git a/Artemis/LogiLed2Artemis/LogiLed2Artemis.vcxproj b/Artemis/LogiLed2Artemis/LogiLed2Artemis.vcxproj new file mode 100644 index 000000000..670478206 --- /dev/null +++ b/Artemis/LogiLed2Artemis/LogiLed2Artemis.vcxproj @@ -0,0 +1,168 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {D2EDB8F3-F0CB-4670-B472-0B46D5800D2C} + Win32Proj + LogiLed2Artemis + LogiLed2Artemis + + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + true + Unicode + + + DynamicLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + $(LibraryPath) + + + false + + + false + $(ProjectName) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LOGILEDCORSAIR_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + LogiLed2Artemis.def + ../CUESDK/lib/i386/CUESDK_2013.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LOGILEDCORSAIR_EXPORTS;%(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + + + Windows + true + LogiLed2Artemis.def + %(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LOGILEDCORSAIR_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + LogiLed2Artemis.def + ../CUESDK/lib/i386/CUESDK_2013.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LOGILEDCORSAIR_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + LogiLed2Artemis.def + %(AdditionalDependencies) + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Artemis/LogiLed2Artemis/LogiLed2Artemis.vcxproj.filters b/Artemis/LogiLed2Artemis/LogiLed2Artemis.vcxproj.filters new file mode 100644 index 000000000..34a553926 --- /dev/null +++ b/Artemis/LogiLed2Artemis/LogiLed2Artemis.vcxproj.filters @@ -0,0 +1,41 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/Artemis/LogiLed2Artemis/LogiLedDefs.h b/Artemis/LogiLed2Artemis/LogiLedDefs.h new file mode 100644 index 000000000..e1d2a4f24 --- /dev/null +++ b/Artemis/LogiLed2Artemis/LogiLedDefs.h @@ -0,0 +1,135 @@ +#pragma once + +const int LOGITECH_LED_MOUSE = 0x0001; +const int LOGITECH_LED_KEYBOARD = 0x0002; +const int LOGITECH_LED_ALL = LOGITECH_LED_MOUSE | LOGITECH_LED_KEYBOARD; + +#define LOGI_LED_BITMAP_WIDTH 21 +#define LOGI_LED_BITMAP_HEIGHT 6 +#define LOGI_LED_BITMAP_BYTES_PER_KEY 4 + +#define LOGI_LED_BITMAP_SIZE LOGI_LED_BITMAP_WIDTH*LOGI_LED_BITMAP_HEIGHT*LOGI_LED_BITMAP_BYTES_PER_KEY + +#define LOGI_LED_DURATION_INFINITE 0 + +#define LOGI_DEVICETYPE_MONOCHROME_ORD 0 +#define LOGI_DEVICETYPE_RGB_ORD 1 +#define LOGI_DEVICETYPE_PERKEY_RGB_ORD 2 + +#define LOGI_DEVICETYPE_MONOCHROME (1 << LOGI_DEVICETYPE_MONOCHROME_ORD) +#define LOGI_DEVICETYPE_RGB (1 << LOGI_DEVICETYPE_RGB_ORD) +#define LOGI_DEVICETYPE_PERKEY_RGB (1 << LOGI_DEVICETYPE_PERKEY_RGB_ORD) + +#define LOGI_DEVICETYPE_ALL (LOGI_DEVICETYPE_MONOCHROME | LOGI_DEVICETYPE_RGB | LOGI_DEVICETYPE_PERKEY_RGB) + +namespace LogiLed +{ + typedef enum + { + ESC = 0x01, + F1 = 0x3b, + F2 = 0x3c, + F3 = 0x3d, + F4 = 0x3e, + F5 = 0x3f, + F6 = 0x40, + F7 = 0x41, + F8 = 0x42, + F9 = 0x43, + F10 = 0x44, + F11 = 0x57, + F12 = 0x58, + PRINT_SCREEN = 0x137, + SCROLL_LOCK = 0x46, + PAUSE_BREAK = 0x145, + TILDE = 0x29, + ONE = 0x02, + TWO = 0x03, + THREE = 0x04, + FOUR = 0x05, + FIVE = 0x06, + SIX = 0x07, + SEVEN = 0x08, + EIGHT = 0x09, + NINE = 0x0A, + ZERO = 0x0B, + MINUS = 0x0C, + EQUALS = 0x0D, + BACKSPACE = 0x0E, + INSERT = 0x152, + HOME = 0x147, + PAGE_UP = 0x149, + NUM_LOCK = 0x45, + NUM_SLASH = 0x135, + NUM_ASTERISK = 0x37, + NUM_MINUS = 0x4A, + TAB = 0x0F, + Q = 0x10, + W = 0x11, + E = 0x12, + R = 0x13, + T = 0x14, + Y = 0x15, + U = 0x16, + I = 0x17, + O = 0x18, + P = 0x19, + OPEN_BRACKET = 0x1A, + CLOSE_BRACKET = 0x1B, + BACKSLASH = 0x2B, + KEYBOARD_DELETE = 0x153, + END = 0x14F, + PAGE_DOWN = 0x151, + NUM_SEVEN = 0x47, + NUM_EIGHT = 0x48, + NUM_NINE = 0x49, + NUM_PLUS = 0x4E, + CAPS_LOCK = 0x3A, + A = 0x1E, + S = 0x1F, + D = 0x20, + F = 0x21, + G = 0x22, + H = 0x23, + J = 0x24, + K = 0x25, + L = 0x26, + SEMICOLON = 0x27, + APOSTROPHE = 0x28, + ENTER = 0x1C, + NUM_FOUR = 0x4B, + NUM_FIVE = 0x4C, + NUM_SIX = 0x4D, + LEFT_SHIFT = 0x2A, + Z = 0x2C, + X = 0x2D, + C = 0x2E, + V = 0x2F, + B = 0x30, + N = 0x31, + M = 0x32, + COMMA = 0x33, + PERIOD = 0x34, + FORWARD_SLASH = 0x35, + RIGHT_SHIFT = 0x36, + ARROW_UP = 0x148, + NUM_ONE = 0x4F, + NUM_TWO = 0x50, + NUM_THREE = 0x51, + NUM_ENTER = 0x11C, + LEFT_CONTROL = 0x1D, + LEFT_WINDOWS = 0x15B, + LEFT_ALT = 0x38, + SPACE = 0x39, + RIGHT_ALT = 0x138, + RIGHT_WINDOWS = 0x15C, + APPLICATION_SELECT = 0x15D, + RIGHT_CONTROL = 0x11D, + ARROW_LEFT = 0x14B, + ARROW_DOWN = 0x150, + ARROW_RIGHT = 0x14D, + NUM_ZERO = 0x52, + NUM_PERIOD = 0x53, + } KeyName; +} + diff --git a/Artemis/LogiLed2Artemis/main.cpp b/Artemis/LogiLed2Artemis/main.cpp new file mode 100644 index 000000000..c224e7daf --- /dev/null +++ b/Artemis/LogiLed2Artemis/main.cpp @@ -0,0 +1,254 @@ +// Original work by VRocker https://github.com/VRocker/LogiLed2Corsair +// I'm mainly a C# developer, and these modification aren't a piece of art, but it suits our needs. + +// The MIT License (MIT) +// +// Copyright (c) 2015 VRocker +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "main.h" +#include +#include +#include +#include "LogiLedDefs.h" +#define WIN32_LEAN_AND_MEAN +#include +#include "Logger.h" + +#include +#include + + +static bool g_hasInitialised = false; +const char* game = ""; + +void cleanup() +{ + CLogger::EndLogging(); +} + +BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + { + atexit(cleanup); + + CLogger::InitLogging("Log.txt"); + CLogger::SetLogLevel(LogLevel::None); + + // Get the process that loaded the DLL + TCHAR divisionFind[] = _T("Division"); + TCHAR gtaFind[] = _T("GTA"); + TCHAR szPath[MAX_PATH]; + GetModuleFileName(NULL, szPath, MAX_PATH); + + if (_tcscmp(szPath, divisionFind) != 0) + game = "division"; + else if (_tcscmp(szPath, gtaFind) != 0) + game = "gta"; + + CLogger::OutputLog("Attached to process.", LogLevel::Debug); + } + break; + + case DLL_PROCESS_DETACH: + { + cleanup(); + + CLogger::OutputLog_s("Detached from process.", LogLevel::Debug); + } + break; + } + + return true; +} + +bool LogiLedInit() +{ + CLogger::OutputLog_s("Logitech LED init called.", LogLevel::Debug); + g_hasInitialised = true; + return true; +} + +bool LogiLedGetSdkVersion(int* majorNum, int* minorNum, int* buildNum) +{ + CLogger::OutputLog("LogiLedGetSdkVersion called.", LogLevel::Debug); + + // Mimic the SDK version + *majorNum = 8; + *minorNum = 81; + *buildNum = 15; + + return true; +} + +bool LogiLedSetTargetDevice(int targetDevice) +{ + CLogger::OutputLog("LogiLedSetTargetDevice called (%i)", LogLevel::Debug, targetDevice); + + // Logitech SDK says this function returns false if LogiLedInit hasn't been called + return g_hasInitialised; +} + +bool LogiLedSaveCurrentLighting() +{ + CLogger::OutputLog("LogiLedSaveCurrentLighting called (%i)", LogLevel::Debug); + return true; +} + +void Transmit(const char* msg) +{ + //Pipe Init Data + HANDLE hPipe1; + char buf[100]; + LPTSTR lpszPipename1 = TEXT("\\\\.\\pipe\\artemis"); + + hPipe1 = CreateFile(lpszPipename1, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (hPipe1 == NULL || hPipe1 == INVALID_HANDLE_VALUE) + { + CLogger::OutputLog("Could not open the pipe - (error %i)", LogLevel::Debug, GetLastError()); + return; + } + + DWORD cbWritten; + WriteFile(hPipe1, msg, strlen(msg), &cbWritten, NULL); + CLogger::OutputLog_s("Transmitted to pipe", LogLevel::Debug); +} + +bool LogiLedSetLighting(int redPercentage, int greenPercentage, int bluePercentage) +{ + CLogger::OutputLog("LogiLedSetLighting called (%i %i %i)", LogLevel::Debug, redPercentage, greenPercentage, bluePercentage); + + std::ostringstream os; + os << "0 0 " << redPercentage << " " << greenPercentage << " " << bluePercentage; + Transmit(os.str().c_str()); + + return true; +} + +bool LogiLedRestoreLighting() +{ + CLogger::OutputLog("LogiLedRestoreLighting called", LogLevel::Debug); + return true; +} + +bool LogiLedFlashLighting(int redPercentage, int greenPercentage, int bluePercentage, int milliSecondsDuration, int milliSecondsInterval) +{ + CLogger::OutputLog("LogiLedFlashLighting called (%i %i %i %i %i)", LogLevel::Debug, redPercentage, greenPercentage, bluePercentage, milliSecondsDuration, milliSecondsInterval); + return true; +} + +bool LogiLedPulseLighting(int redPercentage, int greenPercentage, int bluePercentage, int milliSecondsDuration, int milliSecondsInterval) +{ + CLogger::OutputLog("LogiLedPulseLighting called (%i %i %i %i %i)", LogLevel::Debug, redPercentage, greenPercentage, bluePercentage, milliSecondsDuration, milliSecondsInterval); + return true; +} + +bool LogiLedStopEffects() +{ + CLogger::OutputLog_s("LogiLedStopEffects called", LogLevel::Debug); + return true; +} + +bool LogiLedSetLightingFromBitmap(unsigned char bitmap[]) +{ + CLogger::OutputLog_s("LogiLedSetLightingFromBitmap called", LogLevel::Debug); + return true; +} + +bool LogiLedSetLightingForKeyWithScanCode(int keyCode, int redPercentage, int greenPercentage, int bluePercentage) +{ + CLogger::OutputLog("LogiLedSetLightingForKeyWithScanCode called [Key: %i] (%i %i %i)", LogLevel::Debug, keyCode, redPercentage, greenPercentage, bluePercentage); + return true; +} + +bool LogiLedSetLightingForKeyWithHidCode(int keyCode, int redPercentage, int greenPercentage, int bluePercentage) +{ + CLogger::OutputLog("LogiLedSetLightingForKeyWithHidCode called [Key: %i] (%i %i %i)", LogLevel::Debug, keyCode, redPercentage, greenPercentage, bluePercentage); + return true; +} + +bool LogiLedSetLightingForKeyWithQuartzCode(int keyCode, int redPercentage, int greenPercentage, int bluePercentage) +{ + CLogger::OutputLog("LogiLedSetLightingForKeyWithQuartzCode called [Key: %i] (%i %i %i)", LogLevel::Debug, keyCode, redPercentage, greenPercentage, bluePercentage); + return true; +} + +bool LogiLedSetLightingForKeyWithKeyName(LogiLed::KeyName keyName, int redPercentage, int greenPercentage, int bluePercentage) +{ + CLogger::OutputLog("LogiLedSetLightingForKeyWithKeyName called [Key: %i] (%i %i %i)", LogLevel::Debug, keyName, redPercentage, greenPercentage, bluePercentage); + + // Only transmit interesting keys. This can most likely be done prettier, but I'm no C++ dev. + if (game == "division" && + (keyName == LogiLed::F1 || + keyName == LogiLed::F2 || + keyName == LogiLed::F3 || + keyName == LogiLed::F4 || + keyName == LogiLed::R || + keyName == LogiLed::G || + keyName == LogiLed::V) + ) + { + std::ostringstream os; + os << "1 " << keyName << " " << redPercentage << " " << greenPercentage << " " << bluePercentage; + std::string s = os.str(); + Transmit(os.str().c_str()); + } + return true; +} + +bool LogiLedSaveLightingForKey(LogiLed::KeyName keyName) +{ + CLogger::OutputLog("LogiLedSaveLightingForKey called [Key: %i]", LogLevel::Debug, keyName); + return true; +} + +bool LogiLedRestoreLightingForKey(LogiLed::KeyName keyName) +{ + CLogger::OutputLog("LogiLedRestoreLightingForKey called [Key: %i]", LogLevel::Debug, keyName); + return true; +} + +bool LogiLedFlashSingleKey(LogiLed::KeyName keyName, int redPercentage, int greenPercentage, int bluePercentage, int msDuration, int msInterval) +{ + CLogger::OutputLog("LogiLedFlashSingleKey called [Key: %i] (%i %i %i %i %i)", LogLevel::Debug, keyName, redPercentage, greenPercentage, bluePercentage, msDuration, msInterval); + return true; +} + +bool LogiLedPulseSingleKey(LogiLed::KeyName keyName, int startRedPercentage, int startGreenPercentage, int startBluePercentage, int finishRedPercentage, int finishGreenPercentage, int finishBluePercentage, int msDuration, bool isInfinite) +{ + CLogger::OutputLog("LogiLedPulseSingleKey called [Key: %i] (%i %i %i %i %i %i %i %i)", LogLevel::Debug, keyName, startRedPercentage, startGreenPercentage, startBluePercentage, finishRedPercentage, finishGreenPercentage, finishBluePercentage, msDuration, isInfinite); + return true; +} + +bool LogiLedStopEffectsOnKey(LogiLed::KeyName keyName) +{ + CLogger::OutputLog("LogiLedStopEffectsOnKey called [Key: %i]", LogLevel::Debug, keyName); + return true; +} + +void LogiLedShutdown() +{ + CLogger::OutputLog_s("LogiLedShutdown called.", LogLevel::Debug); + g_hasInitialised = false; +} + diff --git a/Artemis/LogiLed2Artemis/main.h b/Artemis/LogiLed2Artemis/main.h new file mode 100644 index 000000000..3f59c932d --- /dev/null +++ b/Artemis/LogiLed2Artemis/main.h @@ -0,0 +1,2 @@ +#pragma once +