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

Allow Nintendo Switch Online controllers to be reported as Official Pro Controllers #534

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ GIT_BRANCH := $(shell git symbolic-ref --short HEAD | sed s/[^a-zA-Z0-9_-]/_/g)
GIT_HASH := $(shell git rev-parse --short HEAD)
GIT_TAG := $(shell git describe --tags `git rev-list --tags --max-count=1`)

VERSION := $(shell [[ $(GIT_TAG) =~ ^v([0-9]+).([0-9]+).([0-9]+) ]] && printf '0x%02X%02X%02X' $${BASH_REMATCH[1]} $${BASH_REMATCH[2]} $${BASH_REMATCH[3]})
VERSION := $(shell printf "0x%02X%02X%02X" $(shell echo "$(GIT_TAG)" | sed -E 's/^v([0-9]+).([0-9]+).([0-9]+)/\1 \2 \3/g'))
BUILD_VERSION := $(GIT_TAG:v%=%)-$(GIT_BRANCH)-$(GIT_HASH)
BUILD_DATE := $(shell date)

TARGETS := mcmitm_version.cpp mc_mitm

all: $(TARGETS)

mcmitm_version.cpp: .git/HEAD .git/index
echo "namespace ams::mitm { unsigned int mc_version = $(VERSION); const char *mc_build_name = \"$(BUILD_VERSION)\"; const char *mc_build_date = \"$$(date)\"; }" > mc_mitm/source/$@
echo "namespace ams::mitm { unsigned int mc_version = $(VERSION); const char *mc_build_name = \"$(BUILD_VERSION)\"; const char *mc_build_date = \"$(BUILD_DATE)\"; }" > mc_mitm/source/$@

mc_mitm:
$(MAKE) -C $@
Expand Down
2 changes: 2 additions & 0 deletions mc_mitm/config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@
;enable_dualsense_player_leds=false
; Set Dualsense vibration intensity, 12.5% per increment. Valid range [1-8] where 1=12.5%, 8=100% [default 4(50%)]
;dualsense_vibration_intensity=4
; Change the controller type reported by NSO controllers, allowing to be completely remapped as valid pro controllers [default false]
;spoof_nso_as_pro_controller=false
8 changes: 8 additions & 0 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,8 @@
#include "btm_mitm_service.hpp"
#include "btm_shim.h"
#include "../controllers/controller_management.hpp"
#include "../mcmitm_config.hpp"
#include "../controllers/switch_controller.hpp"

namespace ams::mitm::btm {

Expand Down Expand Up @@ -83,6 +85,9 @@ namespace ams::mitm::btm {
if (!controller::IsOfficialSwitchControllerName(device->name.name)) {
std::strncpy(device->name.name, controller::pro_controller_name, sizeof(device->name) - 1);
}
else if (mitm::GetGlobalConfig()->misc.spoof_nso_as_pro_controller && ams::controller::IsNsoController(device->profile_info.hid_device_info.vid, device->profile_info.hid_device_info.pid)) {
device->profile_info.hid_device_info.pid = 0x2009;
}
}

return ams::ResultSuccess();
Expand All @@ -97,6 +102,9 @@ namespace ams::mitm::btm {
if (!controller::IsOfficialSwitchControllerName(device->name)) {
std::strncpy(device->name, controller::pro_controller_name, sizeof(device->name) - 1);
}
else if (mitm::GetGlobalConfig()->misc.spoof_nso_as_pro_controller && ams::controller::IsNsoController(device->profile_info.hid_device_info.vid, device->profile_info.hid_device_info.pid)) {
device->profile_info.hid_device_info.pid = 0x2009;
}
}

return ams::ResultSuccess();
Expand Down
97 changes: 93 additions & 4 deletions mc_mitm/source/controllers/switch_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,27 @@ namespace ams::controller {
SwitchPlayerNumber_Four, //1111
};


bool ApplyN64Remapping(SwitchButtonData *buttons) {
bool alternatescheme = buttons->Y; //C-Up
buttons->Y = buttons->ZR; //C-Down -> Y
buttons->ZR = buttons->rstick_press; //ZR -> ZR
buttons->rstick_press = 0;
if (alternatescheme) {
buttons->lstick_press = buttons->L; //L -> LEFT STICK PRESS
buttons->rstick_press = buttons->R; //R -> RIGHT STICK PRESS
buttons->L = 0;
buttons->R = 0;
}
return alternatescheme;
}

}

bool IsNsoController(uint16_t vid, uint16_t pid) {
if (vid == 0x057e && (pid == 0x2017 || pid == 0x2019 || pid == 0x201a))
return true;
else return false;
}

Result LedsMaskToPlayerNumber(uint8_t led_mask, uint8_t *player_number) {
Expand Down Expand Up @@ -67,7 +88,7 @@ namespace ams::controller {
if (this->HasSetTsiDisableFlag())
m_settsi_supported = false;

return ams::ResultSuccess();
return ams::ResultSuccess();
}

bool SwitchController::HasSetTsiDisableFlag() {
Expand Down Expand Up @@ -109,11 +130,57 @@ namespace ams::controller {
uint8_t data[] = {0xff, 0xd7, 0x00, 0x00, 0x57, 0xb7, 0x00, 0x57, 0xb7, 0x00, 0x57, 0xb7};
std::memcpy(input_report->type0x21.hid_command_response.data.serial_flash_read.data, data, sizeof(data));
}
else if (mitm::GetGlobalConfig()->misc.spoof_nso_as_pro_controller && IsNsoController(m_id.vid, m_id.pid)) {
uint8_t data[] = {0x32, 0x32, 0x32, 0xe6, 0xe6, 0xe6, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46};
std::memcpy(input_report->type0x21.hid_command_response.data.serial_flash_read.data, data, sizeof(data));
}
}
}
else if (input_report->type0x21.hid_command_response.id == HidCommand_GetDeviceInfo && mitm::GetGlobalConfig()->misc.spoof_nso_as_pro_controller && IsNsoController(m_id.vid, m_id.pid)) {
input_report->type0x21.hid_command_response.data.get_device_info.type = 0x03;
input_report->type0x21.hid_command_response.data.get_device_info._unk2 = 0x02;
}
else if (input_report->type0x21.hid_command_response.id == HidCommand_SerialFlashRead && mitm::GetGlobalConfig()->misc.spoof_nso_as_pro_controller && m_id.vid == 0x057e && m_id.pid == 0x2019) {
if (input_report->type0x21.hid_command_response.data.serial_flash_read.address == 0x603d) { //Factory calibration
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x9] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x0];
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0xA] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x1];
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0xB] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x2];
memcpy(&input_report->type0x21.hid_command_response.data.serial_flash_read.data[0xC], &input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x3], 3);
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x10] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x6];
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x11] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x7];
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x12] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x8];
}
else if (input_report->type0x21.hid_command_response.data.serial_flash_read.address == 0x8010) { //User Calibration
if (input_report->type0x21.hid_command_response.data.serial_flash_read.data[0] != 0xFF && input_report->type0x21.hid_command_response.data.serial_flash_read.data[1] != 0xFF) {
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0xB] = input_report->type0x21.hid_command_response.data.serial_flash_read.data[0];
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0xC] = input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x1];
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0xD] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x2];
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0xE] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x3];
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0xF] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x4];
memcpy(&input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x10], &input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x5], 3);
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x13] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x8];
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x14] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x9];
input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x15] = 0xFF - input_report->type0x21.hid_command_response.data.serial_flash_read.data[0xA];
}
}
else if (input_report->type0x21.hid_command_response.data.serial_flash_read.address == 0x6080) { //Copy left stick parameters
memcpy(&m_n64_left_stick_param, &input_report->type0x21.hid_command_response.data.serial_flash_read.data[0x6],sizeof(SwitchAnalogStickParameters));
}
else if (input_report->type0x21.hid_command_response.data.serial_flash_read.address == 0x6098) { //Paste left stick parameters on top of the right one (all 0XFF because there isn't one)
memcpy(&input_report->type0x21.hid_command_response.data.serial_flash_read.data, &m_n64_left_stick_param, sizeof(SwitchAnalogStickParameters));
}
}
}

if (mitm::GetGlobalConfig()->misc.spoof_nso_as_pro_controller && m_id.vid == 0x057e && m_id.pid == 0x2019) {
if (ApplyN64Remapping(&input_report->buttons)) {
input_report->right_stick.SetData(input_report->left_stick.GetX(), input_report->left_stick.GetY());
input_report->left_stick.SetData(STICK_ZERO, STICK_ZERO);
}
else input_report->right_stick.SetData(STICK_ZERO, STICK_ZERO);
}

this->ApplyButtonCombos(&input_report->buttons);
this->ApplyButtonCombos(&input_report->buttons);

return bluetooth::hid::report::WriteHidDataReport(m_address, &m_input_report);
}
Expand Down Expand Up @@ -142,14 +209,36 @@ namespace ams::controller {
}

Result SwitchController::HandleOutputDataReport(const bluetooth::HidReport *report) {
auto output_report = reinterpret_cast<const SwitchOutputReport *>(&report->data);
if (output_report->id == 0x01 && mitm::GetGlobalConfig()->misc.spoof_nso_as_pro_controller && IsNsoController(m_id.vid, m_id.pid) && (output_report->type0x01.hid_command.id == HidCommand_SerialFlashWrite || output_report->type0x01.hid_command.id == HidCommand_SerialFlashSectorErase)) {
SwitchInputReport input_report = {
.id = 0x21,
.timer = 1,
.conn_info = 1,
.battery = BATTERY_MAX,
.vibrator = 0,
};

input_report.type0x21.hid_command_response = {
.ack = 0x80,
.id = output_report->type0x01.hid_command.id,
.data = {
.serial_flash_write = {
.status = 0x1 //Force write protected response just to be safe
}
}
};

return bluetooth::hid::report::WriteHidDataReport(m_address, &m_input_report);
}
return this->WriteDataReport(report);
}

Result SwitchController::WriteDataReport(const bluetooth::HidReport *report) {
return btdrvWriteHidData(m_address, report);
}

Result SwitchController::WriteDataReport(const bluetooth::HidReport *report, uint8_t response_id, bluetooth::HidReport *out_report) {
Result SwitchController::WriteDataReport(const bluetooth::HidReport *report, uint8_t response_id, bluetooth::HidReport *out_report) {
auto response = std::make_shared<HidResponse>(BtdrvHidEventType_Data);
response->SetUserData(response_id);
m_future_responses.push(response);
Expand Down Expand Up @@ -206,7 +295,7 @@ namespace ams::controller {
}

auto response_data = response->GetData();

Result result;
const bluetooth::HidReport *get_report;
if (hos::GetVersion() >= hos::Version_9_0_0) {
Expand Down
2 changes: 2 additions & 0 deletions mc_mitm/source/controllers/switch_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ namespace ams::controller {
} __attribute__ ((__packed__));

Result LedsMaskToPlayerNumber(uint8_t led_mask, uint8_t *player_number);
bool IsNsoController(uint16_t vid, uint16_t pid);

std::string GetControllerDirectory(const bluetooth::Address *address);

Expand Down Expand Up @@ -358,6 +359,7 @@ namespace ams::controller {
virtual Result HandleOutputDataReport(const bluetooth::HidReport *report);
private:
bool HasSetTsiDisableFlag();
SwitchAnalogStickParameters m_n64_left_stick_param;

protected:
Result WriteDataReport(const bluetooth::HidReport *report);
Expand Down
11 changes: 7 additions & 4 deletions mc_mitm/source/mcmitm_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@ namespace ams::mitm {
.enable_dualshock4_lightbar = true,
.enable_dualsense_lightbar = true,
.enable_dualsense_player_leds = true,
.dualsense_vibration_intensity = 4
.dualsense_vibration_intensity = 4,
.spoof_nso_as_pro_controller = false
}
};

void ParseBoolean(const char *value, bool *out) {
if (strcasecmp(value, "true") == 0)
*out = true;
else if (strcasecmp(value, "false") == 0)
*out = false;
*out = false;
}

void ParseInt(const char *value, int *out, int min=INT_MIN, int max=INT_MAX) {
Expand Down Expand Up @@ -75,9 +76,9 @@ namespace ams::mitm {

if (strcasecmp(section, "general") == 0) {
if (strcasecmp(name, "enable_rumble") == 0)
ParseBoolean(value, &config->general.enable_rumble);
ParseBoolean(value, &config->general.enable_rumble);
else if (strcasecmp(name, "enable_motion") == 0)
ParseBoolean(value, &config->general.enable_motion);
ParseBoolean(value, &config->general.enable_motion);
}
else if (strcasecmp(section, "bluetooth") == 0) {
if (strcasecmp(name, "host_name") == 0)
Expand All @@ -94,6 +95,8 @@ namespace ams::mitm {
ParseBoolean(value, &config->misc.enable_dualsense_player_leds);
else if (strcasecmp(name, "dualsense_vibration_intensity") == 0)
ParseInt(value, &config->misc.dualsense_vibration_intensity, 1, 8);
else if (strcasecmp(name, "spoof_nso_as_pro_controller") == 0)
ParseBoolean(value, &config->misc.spoof_nso_as_pro_controller);
}
else {
return 0;
Expand Down
1 change: 1 addition & 0 deletions mc_mitm/source/mcmitm_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace ams::mitm {
bool enable_dualsense_lightbar;
bool enable_dualsense_player_leds;
int dualsense_vibration_intensity;
bool spoof_nso_as_pro_controller;
} misc;
};

Expand Down