Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement controller profiles for emulated controllers and a few QOL options #302

Open
wants to merge 37 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2d1f8da
bluetooth-mitm: define switch controller types
ndeadly Oct 8, 2020
01ffefe
bluetooth-mitm: define SR and SL buttons in SwitchButtonData
ndeadly Oct 8, 2020
2472842
bluetooth-mitm: add support for identifying and re-mapping controls a…
ndeadly Oct 8, 2020
f8c3c49
bluetooth-mitm: add a method to perform a virtual reconnect on a cont…
ndeadly Oct 10, 2020
f400a20
bluetooth-mitm: define current official controller firmware versions
ndeadly Oct 10, 2020
dc4f34d
bluetooth-mitm: add method to change the type of emulated switch cont…
ndeadly Oct 10, 2020
f764482
bluetooth-mitm: store button state from previous report to allow diff…
ndeadly Oct 10, 2020
f444882
bluetooth-mitm: bump joycon firmware version
ndeadly Oct 10, 2020
02e2ed9
bluetooth-mitm: disable some potentially unnecessary code in VirtualR…
ndeadly Nov 7, 2020
fbad1e4
bluetooth-mitm: messing about with setting fake controller type, firm…
ndeadly Nov 7, 2020
f06b1f1
bluetooth-mitm: fix up to work with recent changes
ndeadly Feb 12, 2021
0805881
btm-mitm: use controller handler type to determine which controller n…
ndeadly Feb 18, 2021
e579e1c
bluetooth-mitm: update VirtualReconnect to use SignalFakeEvent function
ndeadly Feb 22, 2021
e16d4db
bluetooth-mitm: use switch statement for spi flash read locations
ndeadly Feb 27, 2021
ab3d4df
bluetooth-mitm: add missing padding bytes when initialising Connectio…
ndeadly Feb 28, 2021
230e585
bluetooth-mitm: set unknown byte in device info subcommand response t…
ndeadly Feb 28, 2021
06899d3
bluetooth-mitm: copy in fake serial number string when emulating joycons
ndeadly Feb 28, 2021
2b496fd
bluetooth-mitm: change joycon mode button combo to R3 + L3 to avoid u…
ndeadly Feb 28, 2021
6151a4a
bluetooth-mitm: send separate mcu config reply when identifying as jo…
ndeadly Sep 14, 2020
88e767c
bluetooth-mitm: remove sending serial for fake joycon, doesn't seem n…
ndeadly Feb 28, 2021
80b6b21
bluetooth-mitm: improve handling of controller colours, define defaul…
ndeadly Feb 28, 2021
12da390
bluetooth-mitm: static_cast conditional 8-bit values in struct initia…
ndeadly Feb 28, 2021
a1d19d5
bluetooth-mitm: clean up joycon emulation code a bit
ndeadly Mar 11, 2021
3c20440
mc.mitm: fix issues following rebase and update for libnx changes
ndeadly Jun 23, 2021
1abb8e2
Update libnx master
ndeadly Jun 23, 2021
84fab6d
Move controller specific settings from missioncontrol.ini to default.…
Banz99 Dec 17, 2021
b67ad92
Implement per controller profiles (using their bt address) and a glob…
Banz99 Dec 17, 2021
9d5e6f5
Implement an option for digital pad to left analog stick mapping and …
Banz99 Dec 17, 2021
5221e60
Implement an analog stick axis reversal option
Banz99 Dec 17, 2021
9a4142b
Implement an option to disable the home button
Banz99 Dec 17, 2021
ac9da69
Implement a toggle for a western layout mapping option, indipendent f…
Banz99 Dec 17, 2021
5d4be97
Replace the option to disable Sony controllers LEDs with a specific s…
Banz99 Dec 17, 2021
b35000b
Implement a custom option for DS4 polling rate
Banz99 Dec 17, 2021
ebfe2e1
Implement an option to specify the emulated controller type
Banz99 Jan 8, 2022
f0455c8
Merge remote-tracking branch 'Nd/experiment/identify-as-joycon' into …
Banz99 Jan 8, 2022
99ca0cc
Correct weird behaviour resulting in the stick reporting a value on t…
Banz99 Jan 8, 2022
66fc1a7
Replace button combo with a request to hid for the actual orientation…
Banz99 Mar 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dist: all
mkdir -p dist/config/MissionControl
mkdir -p dist/config/MissionControl/controllers
cp mc_mitm/config.ini dist/config/MissionControl/missioncontrol.ini.template
cp mc_mitm/default.ini dist/config/MissionControl/controllers/default.ini

cd dist; zip -r $(PROJECT_NAME)-$(BUILD_VERSION).zip ./*; cd ../;

Expand Down
55 changes: 55 additions & 0 deletions common/controller_profiles.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2020-2021 ndeadly
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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 <http://www.gnu.org/licenses/>.
*/
#pragma once

struct ControllerProfileConfig {
struct {
uint8_t controller_type;
bool enable_rumble;
bool enable_motion;
} general;

struct {
bool use_western_layout;
uint32_t sony_led_brightness;
uint32_t dualshock_reportrate;
bool swap_dpad_lstick;
bool invert_lstick_xaxis;
bool invert_lstick_yaxis;
bool invert_rstick_xaxis;
bool invert_rstick_yaxis;
bool disable_home_button;
} misc;
};

constexpr ControllerProfileConfig g_cp_global_config = {
.general = {
.controller_type = 3,
.enable_rumble = true,
.enable_motion = true
},
.misc = {
.use_western_layout = false,
.sony_led_brightness = 8,
.dualshock_reportrate = 8,
.swap_dpad_lstick = false,
.invert_lstick_xaxis = false,
.invert_lstick_yaxis = false,
.invert_rstick_xaxis = false,
.invert_rstick_yaxis = false,
.disable_home_button = false
}
};
2 changes: 2 additions & 0 deletions mc_mitm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ ifneq ($(BUILD),$(notdir $(CURDIR)))
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)

SOURCES += ../common

export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))

Expand Down
10 changes: 2 additions & 8 deletions mc_mitm/config.ini
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
[general]
; Enable vibration support for unofficial controllers [default true]
;enable_rumble=true
; Enable motion controls support for unoffical controllers [default true]
;enable_motion=true
; Completely disable the use of any controller profiles present on the SD card and fall back to program defaults
;disable_custom_profiles=false

[bluetooth]
; Override host name of Bluetooth adapter
;host_name=Nintendo Switch!
; Override host mac address of Bluetooth adapter
;host_address=04:20:69:04:20:69

[misc]
; Disable the LED lightbar on Sony Dualshock 4 and Dualsense controllers [default false]
;disable_sony_leds=false
24 changes: 24 additions & 0 deletions mc_mitm/default.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[general]
; Specifies which kind of controller should be emulated (1 = LeftJoyCon, 2 = RightJoyCon, 3 = ProController) [default 3]
;controller_type=3
; Enable vibration support for unofficial controllers [default true]
;enable_rumble=true
; Enable motion controls support for unoffical controllers [default true]
;enable_motion=true

[misc]
; Automatically remaps the controller buttons to behave similarly to how they do on western consoles (i.e A to confirm on Xbox controllers, X on Playstation ones)
;use_western_layout=false
; Specify the LED lightbar brightness on Sony controllers (range 0(off)-63(max)) [default 8]
;sony_led_brightness=8
; Specifies a different polling rate value for DualShock 4 controllers (see https://github.com/ndeadly/MissionControl/blob/master/mc_mitm/source/controllers/dualshock4_controller.hpp#L21 for a list of valid values) [default 8(125hz)]
;dualshock_reportrate=8
; Map the dpad of the controller to the left stick and viceversa [default false]
;swap_dpad_lstick=false
; A series of stick axis reversal settings [default false]
;invert_lstick_xaxis=false
;invert_lstick_yaxis=false
;invert_rstick_xaxis=false
;invert_rstick_yaxis=false
; Disable the use of the HOME button system-wide (still accessible via button combos) [default false]
;disable_home_button=false
45 changes: 45 additions & 0 deletions mc_mitm/source/bluetooth_mitm/bluetooth/bluetooth_hid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,51 @@ namespace ams::bluetooth::hid {
g_system_event_fwd.Signal();
}

Result VirtualReconnect(const bluetooth::Address *address) {

if (hos::GetVersion() >= hos::Version_12_0_0) {
struct ConnectionState {
BtdrvHidConnectionStatus status;
BtdrvAddress address;
};

// Signal fake disconnection event
ConnectionState disconnected = { BtdrvHidConnectionStatus_Closed, *address };
SignalFakeEvent(BtdrvHidEventType_Connection, &disconnected, sizeof(disconnected));
g_data_read_event.Wait();

// If we don't wait a bit the console disconnects the controller for real
svcSleepThread(100'000'000ULL);

// Signal fake connection event
ConnectionState connected = { BtdrvHidConnectionStatus_Opened, *address };
SignalFakeEvent(BtdrvHidEventType_Connection, &connected, sizeof(connected));
g_data_read_event.Wait();
}
else {
struct ConnectionState {
BtdrvAddress address;
uint8_t pad[2];
BtdrvHidConnectionStatus status;
};

// Signal fake disconnection event
ConnectionState disconnected = { *address, {0}, BtdrvHidConnectionStatusOld_Closed };
SignalFakeEvent(BtdrvHidEventTypeOld_Connection, &disconnected, sizeof(disconnected));
g_data_read_event.Wait();

// If we don't wait a bit the console disconnects the controller for real
svcSleepThread(100'000'000ULL);

// Signal fake connection event
ConnectionState connected = { *address, {0}, BtdrvHidConnectionStatusOld_Opened };
SignalFakeEvent(BtdrvHidEventTypeOld_Connection, &connected, sizeof(connected));
g_data_read_event.Wait();
}

return ams::ResultSuccess();
}

Result GetEventInfo(bluetooth::HidEventType *type, void *buffer, size_t size) {
std::scoped_lock lk(g_event_info_lock);

Expand Down
1 change: 1 addition & 0 deletions mc_mitm/source/bluetooth_mitm/bluetooth/bluetooth_hid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace ams::bluetooth::hid {
os::SystemEvent *GetForwardEvent(void);
os::SystemEvent *GetUserForwardEvent(void);

Result VirtualReconnect(const bluetooth::Address *address);
void SignalFakeEvent(bluetooth::HidEventType type, const void *data, size_t size);
Result GetEventInfo(bluetooth::HidEventType *type, void *buffer, size_t size);
void HandleEvent(void);
Expand Down
23 changes: 17 additions & 6 deletions mc_mitm/source/btm_mitm/btm_mitm_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "btm_mitm_service.hpp"
#include "btm_shim.h"
#include "../controllers/controller_management.hpp"
//#include "../controllers/switch_controller.hpp"
#include <cstring>

namespace ams::mitm::btm {
Expand All @@ -24,9 +25,14 @@ namespace ams::mitm::btm {

void RenameConnectedDevices(BtmConnectedDeviceV1 devices[], size_t count) {
for (unsigned int i = 0; i < count; ++i) {
auto device = &devices[i];
if (!controller::IsOfficialSwitchControllerName(device->name)) {
std::strncpy(device->name, controller::pro_controller_name, sizeof(device->name) - 1);
auto device = controller::LocateHandler(&devices[i].address);
if (device) {
if (!device->IsOfficialController()) {
if (device->GetControllerType() == controller::SwitchControllerType_ProController)
std::strncpy(devices[i].name, controller::pro_controller_name, sizeof(devices[i].name) - 1);
else
std::strncpy(devices[i].name, "Joy-Con (R)", sizeof(devices[i].name) - 1);
}
}
}
}
Expand Down Expand Up @@ -80,9 +86,14 @@ namespace ams::mitm::btm {
R_TRY(btmGetDeviceInfoDeprecatedFwd(m_forward_service.get(), device_info));

for (unsigned int i = 0; i < device_info->device_count; ++i) {
auto device = &device_info->devices[i];
if (!controller::IsOfficialSwitchControllerName(device->name.name)) {
std::strncpy(device->name.name, controller::pro_controller_name, sizeof(device->name) - 1);
auto device = controller::LocateHandler(&device_info->devices[i].addr);
if (device) {
if (!device->IsOfficialController()) {
if (device->GetControllerType() == controller::SwitchControllerType_ProController)
std::strncpy(device_info->devices[i].name.name, controller::pro_controller_name, sizeof(device_info->devices[i].name) - 1);
else
std::strncpy(device_info->devices[i].name.name, "Joy-Con (R)", sizeof(device_info->devices[i].name) - 1);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion mc_mitm/source/controllers/controller_management.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ namespace ams::controller {
public:
UnknownController(const bluetooth::Address *address, HardwareID id)
: EmulatedSwitchController(address, id) {
m_colours.buttons = {0xff, 0x00, 0x00};
//m_colours.buttons = {0xff, 0x00, 0x00}; TODO: Replicate this behaviour with the SPI flash into account
};
};

Expand Down
117 changes: 117 additions & 0 deletions mc_mitm/source/controllers/controller_profiles_management.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2020-2021 ndeadly
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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 <http://www.gnu.org/licenses/>.
*/
#include "controller_profiles_management.hpp"
#include "../utils.hpp"
#include "../mcmitm_config.hpp"

namespace ams::controller {

namespace {
constexpr const char cp_default_path[] = "sdmc:/config/MissionControl/controllers/default.ini";
constexpr const char controllers_folder[] = "sdmc:/config/MissionControl/controllers/";
constexpr const char profile_container[] = "profile.ini";

void ValidateBrightness(const char *value, uint32_t *out){
uint32_t temp=8;
utils::ParseUInt32(value, &temp);
if(temp <= 63)
*out = temp;
else *out = 8;
}

void ValidateReportRate(const char *value, uint32_t *out){
uint32_t temp=8;
utils::ParseUInt32(value, &temp);
if(temp <= 16)
*out = temp;
else *out = 8;
}

void ValidateControllerType(const char *value, uint8_t *out){
uint32_t temp=3;
utils::ParseUInt32(value, &temp);
if(temp>=1 && temp <= 3)
*out = static_cast<uint8_t>(temp);
else *out = 3;
}

int ControllerProfileIniHandler(void *user, const char *section, const char *name, const char *value) {
auto config = reinterpret_cast<ControllerProfileConfig *>(user);

if (strcasecmp(section, "general") == 0) {
if (strcasecmp(name, "controller_type") == 0)
ValidateControllerType(value, &config->general.controller_type);
else if (strcasecmp(name, "enable_rumble") == 0)
utils::ParseBoolean(value, &config->general.enable_rumble);
else if (strcasecmp(name, "enable_motion") == 0)
utils::ParseBoolean(value, &config->general.enable_motion);
}
else if (strcasecmp(section, "misc") == 0) {
if (strcasecmp(name, "use_western_layout") == 0)
utils::ParseBoolean(value, &config->misc.use_western_layout);
else if (strcasecmp(name, "sony_led_brightness") == 0)
ValidateBrightness(value, &config->misc.sony_led_brightness);
else if (strcasecmp(name, "dualshock_reportrate") == 0)
ValidateReportRate(value, &config->misc.dualshock_reportrate);
else if (strcasecmp(name, "swap_dpad_lstick") == 0)
utils::ParseBoolean(value, &config->misc.swap_dpad_lstick);
else if (strcasecmp(name, "invert_lstick_xaxis") == 0)
utils::ParseBoolean(value, &config->misc.invert_lstick_xaxis);
else if (strcasecmp(name, "invert_lstick_yaxis") == 0)
utils::ParseBoolean(value, &config->misc.invert_lstick_yaxis);
else if (strcasecmp(name, "invert_rstick_xaxis") == 0)
utils::ParseBoolean(value, &config->misc.invert_rstick_xaxis);
else if (strcasecmp(name, "invert_rstick_yaxis") == 0)
utils::ParseBoolean(value, &config->misc.invert_rstick_yaxis);
else if (strcasecmp(name, "disable_home_button") == 0)
utils::ParseBoolean(value, &config->misc.disable_home_button);
}
else {
return 0;
}

return 1;
}
}

Result GetControllerConfig(const bluetooth::Address *address, ControllerProfileConfig *config) {
*config = g_cp_global_config;
if (mitm::GetGlobalConfig()->general.disable_custom_profiles)
return 1;
char custom_config_path[100] = "";
std::strcat(custom_config_path, controllers_folder);
char btaddress_string[2 * sizeof(bluetooth::Address) + 1] = "";
utils::BluetoothAddressToString(address, btaddress_string, 2 * sizeof(bluetooth::Address) + 1);
std::strcat(custom_config_path, btaddress_string);
std::strcat(custom_config_path, "/");
std::strcat(custom_config_path, profile_container);
bool has_file = false;
R_TRY(fs::HasFile(&has_file, custom_config_path));
if (!has_file) {
std::strcpy(custom_config_path, cp_default_path);
}
/* Open the file. */
fs::FileHandle file;
{
if (R_FAILED(fs::OpenFile(std::addressof(file), custom_config_path, fs::OpenMode_Read))) {
return 1;
}
}
ON_SCOPE_EXIT { fs::CloseFile(file); };
util::ini::ParseFile(file, config, ControllerProfileIniHandler);
return ams::ResultSuccess();
}
}
25 changes: 25 additions & 0 deletions mc_mitm/source/controllers/controller_profiles_management.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2020-2021 ndeadly
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "../../../common/controller_profiles.hpp"
#include "../bluetooth_mitm/bluetooth/bluetooth_types.hpp"

namespace ams::controller {

Result GetControllerConfig(const bluetooth::Address *address, ControllerProfileConfig *config);

}
Loading