-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
32 changed files
with
3,590 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Auto detect text files and perform LF normalization | ||
* text=auto | ||
|
||
# Custom for Visual Studio | ||
*.cs diff=csharp | ||
*.sln merge=union | ||
*.csproj merge=union | ||
*.vbproj merge=union | ||
*.fsproj merge=union | ||
*.dbproj merge=union | ||
|
||
# Standard to msysgit | ||
*.doc diff=astextplain | ||
*.DOC diff=astextplain | ||
*.docx diff=astextplain | ||
*.DOCX diff=astextplain | ||
*.dot diff=astextplain | ||
*.DOT diff=astextplain | ||
*.pdf diff=astextplain | ||
*.PDF diff=astextplain | ||
*.rtf diff=astextplain | ||
*.RTF diff=astextplain |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# Hagr | ||
|
||
Bridging Nintendo Switch Pro controller and XInput | ||
|
||
## Introduction | ||
|
||
Nintendo Switch Pro controller is a very well designed but also expensive product. Unfortunately it doesn't work on PC out of the box. The reason is that most modern PC games use [XInput](https://en.wikipedia.org/wiki/DirectInput#XInput) to communicate with gamepads but XInput doesn't support Pro controller. Games on Steam may support the device but that's not good enough. | ||
|
||
*Hagr*, meaning skilled or handy in Old Norse, is a tool that communicates with the device and mimics XInput interface so that games can pick up signals from a Pro controller without any modification. | ||
|
||
## How to Use | ||
|
||
Hagr comes in the form of a set of DLLs. There are four DLLs in total and you have to copy them to the folder of your game, *i.e.*, the folder where you can see the EXE file of the game. Remember to back up any `xinput*.dll` file that already exists. Normally a game only needs one or two of the DLLs. Nevertheless, as it's tedious to check which ones your game really needs, just copy all of them and we can call it a day. | ||
|
||
Please note that you DO need to check beforehand whether the game in question is 32-bit or 64-bit. Here's a [quick guide](https://superuser.com/questions/358434/how-to-check-if-a-binary-is-32-or-64-bit-on-windows#889267) of doing it. A 32-bit EXE is unable to load a 64-bit DLL and vise versa. | ||
|
||
## Building the Code | ||
|
||
Just build `hagr.sln` with Visual Studio, preferably 2019. Output binaries will then be located inside `bin/` folder. In the output folder you will also be able to see `TestMe.exe`. It's a simple test program which sends queries to XInput and shows results at a rate of about 60 ticks per second. | ||
|
||
## Limitations | ||
|
||
Hagr is an experimental project. I hope it works for as many use cases as possible but please be expecting situations where it doesn't. There are several limitations which may or may not be resolved in the future. | ||
|
||
- Only **one** controller is supported, and it has to be a Pro controller. | ||
- Only wired connection is supported. | ||
- Thumbstick calibration values are currently hard-coded. | ||
- Vibration via `XInputSetState()` is currently not implemented. | ||
- There's no guarantee that every game using XInput will load the DLLs. Some games have unique ways to start up. | ||
|
||
## An Incomplete List of Working Games | ||
|
||
This is a non-exhaustive list. It only shows those that have been tested by me. | ||
|
||
- Borderlands 3 (64-bit; in `OakGame/Binaries/Win64/` folder) | ||
- Overwatch (64-bit; in `_retail_/` folder) | ||
|
||
## TODO List | ||
|
||
- `XInputSetState()` support. | ||
- Better ways of displaying Hagr status. | ||
|
||
## Acknowledgement | ||
|
||
This project wouldn't be useful in any way without the following pioneer works: | ||
|
||
- [SDL](https://hg.libsdl.org/SDL/file/tip/src/joystick/hidapi/SDL_hidapi_switch.c) | ||
- [dekuNukem/Nintendo_Switch_Reverse_Engineering](https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering) | ||
|
||
## Copyright | ||
|
||
Copyright (C) 2020 Mifan Bang <https://debug.tw>. | ||
|
||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. | ||
|
||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
|
||
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#include <windows.h> | ||
|
||
#define RES_APP_VER "0.1.0" | ||
#define RES_APP_VER_INT 0,1,0 | ||
|
||
|
||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL | ||
|
||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
// Version | ||
|
||
VS_VERSION_INFO VERSIONINFO | ||
FILEVERSION RES_APP_VER_INT | ||
PRODUCTVERSION RES_APP_VER_INT | ||
FILEFLAGSMASK 0x3fL | ||
FILEFLAGS 0x0L | ||
FILEOS 0x40004L | ||
FILETYPE 0x0L | ||
FILESUBTYPE 0x0L | ||
BEGIN | ||
BLOCK "StringFileInfo" | ||
BEGIN | ||
BLOCK "041d04b0" | ||
BEGIN | ||
VALUE "CompanyName", "Mifan Bang" | ||
VALUE "FileDescription", "Bridging Nintendo Switch Pro controller and XInput" | ||
VALUE "FileVersion", RES_APP_VER | ||
VALUE "InternalName", "hagr.dll" | ||
VALUE "LegalCopyright", "Copyright (C) 2020 Mifan Bang. https://debug.tw" | ||
VALUE "OriginalFilename", "hagr.dll" | ||
VALUE "ProductName", "Hagr" | ||
VALUE "ProductVersion", RES_APP_VER | ||
END | ||
END | ||
BLOCK "VarFileInfo" | ||
BEGIN | ||
VALUE "Translation", 0x41d, 0x4b0 | ||
END | ||
END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 16 | ||
VisualStudioVersion = 16.0.29926.136 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hagr", "vcproj\hagr.vcxproj", "{F10755F3-C007-4D2D-9DAD-C46B42377563}" | ||
EndProject | ||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hagrStub-1_3", "vcproj\hagrStub-1_3.vcxproj", "{2F3E60F4-C508-4557-8F89-6E397D8BC68E}" | ||
EndProject | ||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hagrStub-9_1_0", "vcproj\hagrStub-9_1_0.vcxproj", "{830344D1-8B8A-4158-82F0-83ACB5E5AC09}" | ||
EndProject | ||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hagrStub-uap", "vcproj\hagrStub-uap.vcxproj", "{86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E}" | ||
EndProject | ||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestMe", "vcproj\TestMe.vcxproj", "{4FFE9904-19CF-4AB1-A703-4B5A5B13EAB2}" | ||
ProjectSection(ProjectDependencies) = postProject | ||
{830344D1-8B8A-4158-82F0-83ACB5E5AC09} = {830344D1-8B8A-4158-82F0-83ACB5E5AC09} | ||
{86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E} = {86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E} | ||
{F10755F3-C007-4D2D-9DAD-C46B42377563} = {F10755F3-C007-4D2D-9DAD-C46B42377563} | ||
{2F3E60F4-C508-4557-8F89-6E397D8BC68E} = {2F3E60F4-C508-4557-8F89-6E397D8BC68E} | ||
EndProjectSection | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|x64 = Debug|x64 | ||
Debug|x86 = Debug|x86 | ||
Release|x64 = Release|x64 | ||
Release|x86 = Release|x86 | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{F10755F3-C007-4D2D-9DAD-C46B42377563}.Debug|x64.ActiveCfg = Debug|x64 | ||
{F10755F3-C007-4D2D-9DAD-C46B42377563}.Debug|x64.Build.0 = Debug|x64 | ||
{F10755F3-C007-4D2D-9DAD-C46B42377563}.Debug|x86.ActiveCfg = Debug|Win32 | ||
{F10755F3-C007-4D2D-9DAD-C46B42377563}.Debug|x86.Build.0 = Debug|Win32 | ||
{F10755F3-C007-4D2D-9DAD-C46B42377563}.Release|x64.ActiveCfg = Release|x64 | ||
{F10755F3-C007-4D2D-9DAD-C46B42377563}.Release|x64.Build.0 = Release|x64 | ||
{F10755F3-C007-4D2D-9DAD-C46B42377563}.Release|x86.ActiveCfg = Release|Win32 | ||
{F10755F3-C007-4D2D-9DAD-C46B42377563}.Release|x86.Build.0 = Release|Win32 | ||
{2F3E60F4-C508-4557-8F89-6E397D8BC68E}.Debug|x64.ActiveCfg = Debug|x64 | ||
{2F3E60F4-C508-4557-8F89-6E397D8BC68E}.Debug|x64.Build.0 = Debug|x64 | ||
{2F3E60F4-C508-4557-8F89-6E397D8BC68E}.Debug|x86.ActiveCfg = Debug|Win32 | ||
{2F3E60F4-C508-4557-8F89-6E397D8BC68E}.Debug|x86.Build.0 = Debug|Win32 | ||
{2F3E60F4-C508-4557-8F89-6E397D8BC68E}.Release|x64.ActiveCfg = Release|x64 | ||
{2F3E60F4-C508-4557-8F89-6E397D8BC68E}.Release|x64.Build.0 = Release|x64 | ||
{2F3E60F4-C508-4557-8F89-6E397D8BC68E}.Release|x86.ActiveCfg = Release|Win32 | ||
{2F3E60F4-C508-4557-8F89-6E397D8BC68E}.Release|x86.Build.0 = Release|Win32 | ||
{830344D1-8B8A-4158-82F0-83ACB5E5AC09}.Debug|x64.ActiveCfg = Debug|x64 | ||
{830344D1-8B8A-4158-82F0-83ACB5E5AC09}.Debug|x64.Build.0 = Debug|x64 | ||
{830344D1-8B8A-4158-82F0-83ACB5E5AC09}.Debug|x86.ActiveCfg = Debug|Win32 | ||
{830344D1-8B8A-4158-82F0-83ACB5E5AC09}.Debug|x86.Build.0 = Debug|Win32 | ||
{830344D1-8B8A-4158-82F0-83ACB5E5AC09}.Release|x64.ActiveCfg = Release|x64 | ||
{830344D1-8B8A-4158-82F0-83ACB5E5AC09}.Release|x64.Build.0 = Release|x64 | ||
{830344D1-8B8A-4158-82F0-83ACB5E5AC09}.Release|x86.ActiveCfg = Release|Win32 | ||
{830344D1-8B8A-4158-82F0-83ACB5E5AC09}.Release|x86.Build.0 = Release|Win32 | ||
{86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E}.Debug|x64.ActiveCfg = Debug|x64 | ||
{86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E}.Debug|x64.Build.0 = Debug|x64 | ||
{86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E}.Debug|x86.ActiveCfg = Debug|Win32 | ||
{86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E}.Debug|x86.Build.0 = Debug|Win32 | ||
{86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E}.Release|x64.ActiveCfg = Release|x64 | ||
{86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E}.Release|x64.Build.0 = Release|x64 | ||
{86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E}.Release|x86.ActiveCfg = Release|Win32 | ||
{86FDB7D9-7C1A-44D4-9D2C-AECCEA215D0E}.Release|x86.Build.0 = Release|Win32 | ||
{4FFE9904-19CF-4AB1-A703-4B5A5B13EAB2}.Debug|x64.ActiveCfg = Debug|x64 | ||
{4FFE9904-19CF-4AB1-A703-4B5A5B13EAB2}.Debug|x64.Build.0 = Debug|x64 | ||
{4FFE9904-19CF-4AB1-A703-4B5A5B13EAB2}.Debug|x86.ActiveCfg = Debug|Win32 | ||
{4FFE9904-19CF-4AB1-A703-4B5A5B13EAB2}.Debug|x86.Build.0 = Debug|Win32 | ||
{4FFE9904-19CF-4AB1-A703-4B5A5B13EAB2}.Release|x64.ActiveCfg = Release|x64 | ||
{4FFE9904-19CF-4AB1-A703-4B5A5B13EAB2}.Release|x64.Build.0 = Release|x64 | ||
{4FFE9904-19CF-4AB1-A703-4B5A5B13EAB2}.Release|x86.ActiveCfg = Release|Win32 | ||
{4FFE9904-19CF-4AB1-A703-4B5A5B13EAB2}.Release|x86.Build.0 = Release|Win32 | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {755C8C48-4D7F-4EC8-A2ED-D93BB41DBCF8} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* | ||
* hagr - bridging Nintendo Switch Pro controller and XInput | ||
* Copyright (C) 2020 Mifan Bang <https://debug.tw>. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include "AutoHandle.h" | ||
|
||
#include <windows.h> | ||
|
||
|
||
|
||
bool IsHandleValid(Handle handle) | ||
{ | ||
return handle != INVALID_HANDLE_VALUE && handle != nullptr; | ||
} | ||
|
||
|
||
|
||
AutoHandle::AutoHandle() noexcept | ||
: m_handle(nullptr) | ||
{ | ||
} | ||
|
||
AutoHandle::AutoHandle(Handle handle) noexcept | ||
: m_handle(handle) | ||
{ | ||
} | ||
|
||
AutoHandle::AutoHandle(AutoHandle&& handle) noexcept | ||
: m_handle(handle.m_handle) | ||
{ | ||
handle.m_handle = nullptr; | ||
} | ||
|
||
AutoHandle::~AutoHandle() noexcept | ||
{ | ||
Close(); | ||
} | ||
|
||
AutoHandle& AutoHandle::operator = (Handle other) noexcept | ||
{ | ||
Close(); | ||
m_handle = other; | ||
return *this; | ||
} | ||
|
||
AutoHandle& AutoHandle::operator = (AutoHandle&& other) noexcept | ||
{ | ||
*this = other.m_handle; // call "operator = (Handle)" version | ||
other.m_handle = nullptr; | ||
return *this; | ||
} | ||
|
||
AutoHandle::operator Handle () const noexcept | ||
{ | ||
return m_handle; | ||
} | ||
|
||
AutoHandle::operator bool () const noexcept | ||
{ | ||
return IsHandleValid(m_handle); | ||
} | ||
|
||
void AutoHandle::Close() noexcept | ||
{ | ||
if (static_cast<bool>(*this)) | ||
CloseHandle(m_handle); | ||
m_handle = nullptr; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* hagr - bridging Nintendo Switch Pro controller and XInput | ||
* Copyright (C) 2020 Mifan Bang <https://debug.tw>. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#pragma once | ||
|
||
|
||
using Handle = void*; | ||
|
||
|
||
bool IsHandleValid(Handle handle); | ||
|
||
|
||
class AutoHandle | ||
{ | ||
public: | ||
AutoHandle() noexcept; | ||
AutoHandle(Handle handle) noexcept; | ||
AutoHandle(AutoHandle&&) noexcept; | ||
~AutoHandle() noexcept; | ||
|
||
AutoHandle& operator = (Handle other) noexcept; | ||
AutoHandle& operator = (AutoHandle&& other) noexcept; | ||
|
||
operator bool () const noexcept; | ||
operator Handle () const noexcept; | ||
|
||
void Close() noexcept; | ||
|
||
AutoHandle(const AutoHandle&) = delete; | ||
AutoHandle& operator = (const AutoHandle&) = delete; | ||
|
||
private: | ||
Handle m_handle; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* hagr - bridging Nintendo Switch Pro controller and XInput | ||
* Copyright (C) 2020 Mifan Bang <https://debug.tw>. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include "DebugUtils.h" | ||
|
||
#include <iomanip> | ||
#include <sstream> | ||
|
||
#include "Pipes.h" | ||
#include "ProInternals.h" | ||
|
||
|
||
|
||
void DebugOutputString([[maybe_unused]] const wchar_t* str) | ||
{ | ||
#ifdef _DEBUG | ||
OutputDebugStringW(str); | ||
#endif // _DEBUG | ||
} | ||
|
||
|
||
void DebugOutputPacket([[maybe_unused]] const Buffer& buffer) | ||
{ | ||
#ifdef _DEBUG | ||
unsigned int packetIdx = 0; | ||
const auto& funcDumpPacket = [&packetIdx](const Packet& packet) { | ||
std::ostringstream oss; | ||
|
||
oss << packetIdx << ": "; | ||
for (unsigned int i = 0; i < sizeof(packet); ++i) | ||
oss << std::setfill('0') << std::setw(2) << std::uppercase << std::hex << static_cast<unsigned int>(reinterpret_cast<const uint8_t*>(&packet)[i]) << " "; | ||
oss << std::endl; | ||
OutputDebugStringA(oss.str().c_str()); | ||
|
||
++packetIdx; | ||
return true; | ||
}; | ||
|
||
IterateBuffer<Packet>(buffer, funcDumpPacket); | ||
#endif // _DEBUG | ||
} |
Oops, something went wrong.