diff --git a/mc_mitm/config.ini b/mc_mitm/config.ini index ad0c02f9..1ae35d8d 100644 --- a/mc_mitm/config.ini +++ b/mc_mitm/config.ini @@ -26,3 +26,5 @@ ;dualsense_enable_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/PID reported by every official controller but Joycons to be a Pro Controller, allowing full button remapping [default false] +;force_pro_controller=false diff --git a/mc_mitm/source/btm_mitm/btm_mitm_service.cpp b/mc_mitm/source/btm_mitm/btm_mitm_service.cpp index cc7f9dd8..cc37093c 100644 --- a/mc_mitm/source/btm_mitm/btm_mitm_service.cpp +++ b/mc_mitm/source/btm_mitm/btm_mitm_service.cpp @@ -16,6 +16,7 @@ #include "btm_mitm_service.hpp" #include "btm_shim.h" #include "../controllers/controller_management.hpp" +#include "../mcmitm_config.hpp" namespace ams::mitm::btm { @@ -55,6 +56,10 @@ namespace ams::mitm::btm { if (!controller::IsOfficialSwitchControllerName(device->name)) { std::strncpy(device->name, controller::ProControllerName, sizeof(device->name) - 1); } + else if (mitm::GetGlobalConfig()->misc.force_pro_controller && controller::IsNotJoyconOrProController(device->name)) { + device->profile_info.hid_device_info.vid = 0x057e; //This may not have any effect, it's just to fix problems with a Licensed Pro Controller + device->profile_info.hid_device_info.pid = 0x2009; //Change Pid to a Pro controller one + } } R_SUCCEED(); @@ -108,6 +113,10 @@ namespace ams::mitm::btm { if (!controller::IsOfficialSwitchControllerName(device->name.name)) { std::strncpy(device->name.name, controller::ProControllerName, sizeof(device->name) - 1); } + else if (mitm::GetGlobalConfig()->misc.force_pro_controller && controller::IsNotJoyconOrProController(device->name.name)) { + device->profile_info.hid_device_info.vid = 0x057e; //This may not have any effect, it's just to fix problems with a Licensed Pro Controller + device->profile_info.hid_device_info.pid = 0x2009; //Change Pid to a Pro controller one + } } R_SUCCEED(); diff --git a/mc_mitm/source/controllers/controller_management.cpp b/mc_mitm/source/controllers/controller_management.cpp index a147ef69..4276f1fc 100644 --- a/mc_mitm/source/controllers/controller_management.cpp +++ b/mc_mitm/source/controllers/controller_management.cpp @@ -16,6 +16,7 @@ #include "controller_management.hpp" #include #include "../utils.hpp" +#include "../mcmitm_config.hpp" namespace ams::controller { @@ -57,6 +58,7 @@ namespace ams::controller { if (IsOfficialSwitchControllerName(hos::GetVersion() < hos::Version_13_0_0 ? device->name.name : device->name2)) return ControllerType_Switch; + for (auto hwId : WiiController::hardware_ids) { if ( (device->vid == hwId.vid) && (device->pid == hwId.pid) ) { return ControllerType_Wii; @@ -224,6 +226,16 @@ namespace ams::controller { return false; } + bool IsNotJoyconOrProController(const std::string& name) { + std::string JoyconName = "Joy-Con"; + std::string ProControllerName = "Pro Controller"; + if (name.rfind(JoyconName) == 0 || name.rfind(ProControllerName) == 0) { + return false; + } + + return true; + } + void AttachHandler(const bluetooth::Address *address) { bluetooth::DevicesSettings device_settings; R_ABORT_UNLESS(btdrvGetPairedDeviceInfo(*address, &device_settings)); @@ -234,7 +246,9 @@ namespace ams::controller { switch (Identify(&device_settings)) { case ControllerType_Switch: - controller = std::make_shared(address, id); + if (mitm::GetGlobalConfig()->misc.force_pro_controller && IsNotJoyconOrProController(hos::GetVersion() < hos::Version_13_0_0 ? device_settings.name.name : device_settings.name2)) + controller = std::make_shared(address, id); + else controller = std::make_shared(address, id); break; case ControllerType_Wii: controller = std::make_shared(address, id); diff --git a/mc_mitm/source/controllers/controller_management.hpp b/mc_mitm/source/controllers/controller_management.hpp index 09ed0396..45a33d06 100644 --- a/mc_mitm/source/controllers/controller_management.hpp +++ b/mc_mitm/source/controllers/controller_management.hpp @@ -18,6 +18,7 @@ #include #include "switch_controller.hpp" +#include "forced_pro_controller.hpp" #include "wii_controller.hpp" #include "dualshock3_controller.hpp" #include "dualshock4_controller.hpp" @@ -88,6 +89,7 @@ namespace ams::controller { ControllerType Identify(const bluetooth::DevicesSettings *device); bool IsAllowedDeviceClass(const bluetooth::DeviceClass *cod); bool IsOfficialSwitchControllerName(const std::string& name); + bool IsNotJoyconOrProController(const std::string& name); void AttachHandler(const bluetooth::Address *address); void RemoveHandler(const bluetooth::Address *address); diff --git a/mc_mitm/source/controllers/forced_pro_controller.cpp b/mc_mitm/source/controllers/forced_pro_controller.cpp new file mode 100644 index 00000000..d3d73bf5 --- /dev/null +++ b/mc_mitm/source/controllers/forced_pro_controller.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2020-2022 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 . + */ +#include "forced_pro_controller.hpp" +#include "../mcmitm_config.hpp" + +namespace ams::controller { + + namespace { + + bool ApplyN64Remapping(SwitchButtonData *buttons) { + bool alternatescheme = buttons->Y; //C-Up + buttons->Y = buttons->ZR; //C-Down -> Y + buttons->ZR = buttons->lstick_press; //ZR -> ZR + buttons->lstick_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; + } + + } + + ForcedProController::ForcedProController(const bluetooth::Address *address, HardwareID id) + : SwitchController(address, id) + , m_is_n64_controller(false) { + if (m_id.pid == 0x2019) + m_is_n64_controller = true; + } + + + Result ForcedProController::HandleDataReportEvent(const bluetooth::HidReportEventInfo *event_info) { + const bluetooth::HidReport *report; + if (hos::GetVersion() >= hos::Version_9_0_0) { + report = &event_info->data_report.v9.report; + } else if (hos::GetVersion() >= hos::Version_7_0_0) { + report = reinterpret_cast(&event_info->data_report.v7.report); + } else { + report = reinterpret_cast(&event_info->data_report.v1.report); + } + + if (!m_future_responses.empty()) { + if ((m_future_responses.back()->GetType() == BtdrvHidEventType_Data) && (m_future_responses.back()->GetUserData() == report->data[0])) { + m_future_responses.back()->SetData(*event_info); + } + } + + std::scoped_lock lk(m_input_mutex); + + this->UpdateControllerState(report); + + auto input_report = reinterpret_cast(m_input_report.data); + if (input_report->id == 0x21) { + auto response = &input_report->type0x21.hid_command_response; + switch (response->id) { + case HidCommand_SerialFlashRead: + if (response->data.serial_flash_read.address == 0x6050) { + if (ams::mitm::GetSystemLanguage() == 10) { + uint8_t data[] = {0xff, 0xd7, 0x00, 0x00, 0x57, 0xb7, 0x00, 0x57, 0xb7, 0x00, 0x57, 0xb7}; + std::memcpy(response->data.serial_flash_read.data, data, sizeof(data)); + } + else { + uint8_t data[] = {0x32, 0x32, 0x32, 0xe6, 0xe6, 0xe6, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46}; + std::memcpy(response->data.serial_flash_read.data, data, sizeof(data)); + } + } + else if (m_is_n64_controller) { + switch (response->data.serial_flash_read.address) { + case 0x603d: //Factory stick calibration + memcpy(&response->data.serial_flash_read.data[9], &response->data.serial_flash_read.data[3], 3); //Swap calibration triplets for the right stick (https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/spi_flash_notes.md) + memcpy(&m_n64_calibrated_stick_zero, &response->data.serial_flash_read.data[3], 3); + memcpy(&response->data.serial_flash_read.data[12], &response->data.serial_flash_read.data[6], 3); + memcpy(&response->data.serial_flash_read.data[15], &response->data.serial_flash_read.data[0], 3); + break; + case 0x8010: //User stick calibration + if (response->data.serial_flash_read.data[0] != 0xFF && response->data.serial_flash_read.data[1] != 0xFF) { + memcpy(&response->data.serial_flash_read.data[11], &response->data.serial_flash_read.data[0], 2); //Copy magic numbers that signal the presence of a user calibration + memcpy(&response->data.serial_flash_read.data[13], &response->data.serial_flash_read.data[5], 3); //Swap calibration triplets for the right stick + memcpy(&m_n64_calibrated_stick_zero, &response->data.serial_flash_read.data[5], 3); + memcpy(&response->data.serial_flash_read.data[16], &response->data.serial_flash_read.data[8], 3); + memcpy(&response->data.serial_flash_read.data[19], &response->data.serial_flash_read.data[2], 3); + } + break; + case 0x6080: //Left stick parameters (always read first) (first 6 bytes are 6-Axis Horizontal Offsets which we don't need) + memcpy(&m_n64_left_stick_param, &response->data.serial_flash_read.data[6],sizeof(SwitchAnalogStickParameters)); + break; + case 0x6098: //Right stick parameters (always read after the left ones) + memcpy(&response->data.serial_flash_read.data, &m_n64_left_stick_param, sizeof(SwitchAnalogStickParameters)); + break; + } + } + break; + case HidCommand_GetDeviceInfo: + response->data.get_device_info.type = 0x03; + response->data.get_device_info._unk2 = 0x02; + break; + } + } + + if (m_is_n64_controller) { + if (ApplyN64Remapping(&input_report->buttons)) { + input_report->right_stick.SetData(input_report->left_stick.GetX(), input_report->left_stick.GetY()); + memcpy(&input_report->left_stick.m_xy, &m_n64_calibrated_stick_zero, 3); + } + else memcpy(&input_report->right_stick.m_xy, &m_n64_calibrated_stick_zero, 3); + } + + this->ApplyButtonCombos(&input_report->buttons); + + return bluetooth::hid::report::WriteHidDataReport(m_address, &m_input_report); + } + + Result ForcedProController::HandleOutputDataReport(const bluetooth::HidReport *report) { + auto output_report = reinterpret_cast(&report->data); + if (output_report->id == 0x01 && (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); + } + +} diff --git a/mc_mitm/source/controllers/forced_pro_controller.hpp b/mc_mitm/source/controllers/forced_pro_controller.hpp new file mode 100644 index 00000000..997262b5 --- /dev/null +++ b/mc_mitm/source/controllers/forced_pro_controller.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020-2022 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 . + */ +#pragma once +#include "switch_controller.hpp" + +namespace ams::controller { + + class ForcedProController : public SwitchController { + + public: + ForcedProController(const bluetooth::Address *address, HardwareID id); + virtual ~ForcedProController() {}; + + Result HandleDataReportEvent(const bluetooth::HidReportEventInfo *event_info); + Result HandleOutputDataReport(const bluetooth::HidReport *report); + private: + bool m_is_n64_controller; + SwitchAnalogStickParameters m_n64_left_stick_param; + uint8_t m_n64_calibrated_stick_zero[3]; + + }; + +} diff --git a/mc_mitm/source/mcmitm_config.cpp b/mc_mitm/source/mcmitm_config.cpp index cd0402b3..3dd1c0df 100644 --- a/mc_mitm/source/mcmitm_config.cpp +++ b/mc_mitm/source/mcmitm_config.cpp @@ -36,7 +36,8 @@ namespace ams::mitm { .dualshock4_lightbar_brightness = 5, .dualsense_lightbar_brightness = 5, .dualsense_enable_player_leds = true, - .dualsense_vibration_intensity = 4 + .dualsense_vibration_intensity = 4, + .force_pro_controller = false } }; @@ -44,7 +45,7 @@ namespace ams::mitm { 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) { @@ -107,8 +108,10 @@ namespace ams::mitm { ParseBoolean(value, &config->misc.dualsense_enable_player_leds); } else if (strcasecmp(name, "dualsense_vibration_intensity") == 0) { ParseInt(value, &config->misc.dualsense_vibration_intensity, 1, 8); - } - } else { + } else if (strcasecmp(name, "force_pro_controller") == 0) + ParseBoolean(value, &config->misc.force_pro_controller); + } + else { return 0; } diff --git a/mc_mitm/source/mcmitm_config.hpp b/mc_mitm/source/mcmitm_config.hpp index 6e66f5d5..6c01e25c 100644 --- a/mc_mitm/source/mcmitm_config.hpp +++ b/mc_mitm/source/mcmitm_config.hpp @@ -36,6 +36,7 @@ namespace ams::mitm { int dualsense_lightbar_brightness; bool dualsense_enable_player_leds; int dualsense_vibration_intensity; + bool force_pro_controller; } misc; };