Skip to content
Open
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
47 changes: 47 additions & 0 deletions mc_mitm/source/bluetooth_mitm/bluetooth/bluetooth_hid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,53 @@ 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();
os::SystemEvent *GetUserForwardEvent();

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();
Expand Down
120 changes: 112 additions & 8 deletions mc_mitm/source/controllers/emulated_switch_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
#include "emulated_switch_controller.hpp"
#include "../mcmitm_config.hpp"
#include "../bluetooth_mitm/bluetooth/bluetooth_hid.hpp"

namespace ams::controller {

Expand Down Expand Up @@ -86,6 +87,21 @@ namespace ams::controller {
dec->low_band_amp = rumble_amp_lut_f[lo_amp_ind];
}

const SwitchControllerColours default_colours_pro_controller = {
.body = {0x32, 0x32, 0x32},
.buttons = {0xe6, 0xe6, 0xe6},
.left_grip = {0x46, 0x46, 0x46},
.right_grip = {0x46, 0x46, 0x46}
};

const SwitchControllerColours default_colours_joycon = {
.body = {0x82, 0x82, 0x82},
.buttons = {0x0f, 0x0f, 0x0f},
.left_grip = {0xff, 0xff, 0xff},
.right_grip = {0xff, 0xff, 0xff}
};


// CRC-8 with polynomial 0x7 for NFC/IR packets
u8 crc8_lut[] = {
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
Expand Down Expand Up @@ -120,6 +136,8 @@ namespace ams::controller {

EmulatedSwitchController::EmulatedSwitchController(const bluetooth::Address *address, HardwareID id)
: SwitchController(address, id)
, m_emulated_type(SwitchControllerType_ProController)
, m_colours(default_colours_pro_controller)
, m_charging(false)
, m_ext_power(false)
, m_battery(BATTERY_MAX)
Expand Down Expand Up @@ -148,6 +166,7 @@ namespace ams::controller {
}

void EmulatedSwitchController::ClearControllerState() {
std::memset(&m_buttons_previous, 0, sizeof(m_buttons_previous));
std::memset(&m_buttons, 0, sizeof(m_buttons));
m_left_stick.SetData(STICK_CENTER, STICK_CENTER);
m_right_stick.SetData(STICK_CENTER, STICK_CENTER);
Expand All @@ -165,6 +184,62 @@ namespace ams::controller {
input_report->buttons = m_buttons;
input_report->left_stick = m_left_stick;
input_report->right_stick = m_right_stick;

// swap to right joycon
if (m_buttons.home && m_buttons.plus) {
this->SetEmulatedControllerType(m_emulated_type == SwitchControllerType_ProController ? SwitchControllerType_RightJoyCon : SwitchControllerType_ProController);
}

// swap to left joycon
if (m_buttons.home && m_buttons.minus) {
this->SetEmulatedControllerType(m_emulated_type == SwitchControllerType_ProController ? SwitchControllerType_LeftJoyCon : SwitchControllerType_ProController);
}


/* Note:
In change grip menu, to activate a horizontal joycon press "sl and sr"
On a wii remote, this equates to A+B currently */

// Fixup for identifying as horizontal joycon
switch (m_emulated_type) {

// Right joycon implementation
case SwitchControllerType_RightJoyCon:
// invert Y axis as this is the right joyvon
m_left_stick.InvertY();

// set stick data after inversion
input_report->right_stick.SetData(m_left_stick.GetY(), m_left_stick.GetX());

input_report->buttons.SL_R = m_buttons.L | m_buttons.ZL;
input_report->buttons.SR_R = m_buttons.R | m_buttons.ZR;
input_report->buttons.A = m_buttons.B;
input_report->buttons.B = m_buttons.Y;
input_report->buttons.X = m_buttons.A;
input_report->buttons.Y = m_buttons.X;
break;

// Left joycon implementation
case SwitchControllerType_LeftJoyCon:
// invert X axis as this is the left joycon
m_left_stick.InvertX();

// set stick data after inversion
input_report->left_stick.SetData(m_left_stick.GetY(), m_left_stick.GetX());

input_report->buttons.SL_L = m_buttons.L | m_buttons.ZL;
input_report->buttons.SR_L = m_buttons.R | m_buttons.ZR;
input_report->buttons.dpad_down = m_buttons.A;
input_report->buttons.dpad_left = m_buttons.B;
input_report->buttons.dpad_up = m_buttons.Y;
input_report->buttons.dpad_right = m_buttons.X;
break;

default:
break;
}

m_buttons_previous = m_buttons;

std::memcpy(&input_report->type0x30.motion_data, &m_motion_data, sizeof(m_motion_data));
m_input_report.size = offsetof(SwitchInputReport, type0x30) + sizeof(input_report->type0x30);
Expand Down Expand Up @@ -276,20 +351,22 @@ namespace ams::controller {
}

Result EmulatedSwitchController::HandleHidCommandGetDeviceInfo(const SwitchHidCommand *command) {
const FirmwareVersion fw = (m_emulated_type == SwitchControllerType_ProController) ? pro_controller_fw_version : joycon_fw_version;

const SwitchHidCommandResponse response = {
.ack = 0x82,
.id = command->id,
.data = {
.get_device_info = {
.fw_ver = {
.major = 0x03,
.minor = 0x48
.major = fw.major,
.minor = fw.minor
},
.type = 0x03,
.type = m_emulated_type,
._unk0 = 0x02,
.address = m_address,
._unk1 = 0x01,
._unk2 = 0x02
._unk1 = static_cast<uint8_t>(m_emulated_type == SwitchControllerType_LeftJoyCon ? 0x03 : 0x01),
._unk2 = static_cast<uint8_t>(m_emulated_type == SwitchControllerType_ProController ? 0x02 : 0x01)
}
}
};
Expand Down Expand Up @@ -436,7 +513,7 @@ namespace ams::controller {
}

Result EmulatedSwitchController::HandleHidCommandMcuWrite(const SwitchHidCommand *command) {
const SwitchHidCommandResponse response = {
const SwitchHidCommandResponse procontrollerResponse = {
.ack = 0xa0,
.id = command->id,
.data = {
Expand All @@ -449,8 +526,30 @@ namespace ams::controller {
}
}
};

R_RETURN(this->FakeHidCommandResponse(&response));

const SwitchHidCommandResponse joyconResponse = {
.ack = 0xa0,
.id = command->id,
.data = {
.raw = {
0x01, 0x00, 0xff, 0x00, 0x08, 0x00, 0x1b, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf6
}
}
};

// set joycon or pro controller
if (m_emulated_type != SwitchControllerType_ProController)
{
R_RETURN(this->FakeHidCommandResponse(&joyconResponse));
}
else
{
R_RETURN(this->FakeHidCommandResponse(&procontrollerResponse));
}
}

Result EmulatedSwitchController::HandleHidCommandMcuResume(const SwitchHidCommand *command) {
Expand Down Expand Up @@ -571,6 +670,11 @@ namespace ams::controller {
R_RETURN(bluetooth::hid::report::WriteHidDataReport(m_address, &m_input_report));
}

Result EmulatedSwitchController::SetEmulatedControllerType(SwitchControllerType type) {
m_emulated_type = type;
return bluetooth::hid::VirtualReconnect(&m_address);
}

Result EmulatedSwitchController::HandleNfcIrData(const u8 *nfc_ir) {
AMS_UNUSED(nfc_ir);

Expand Down
9 changes: 9 additions & 0 deletions mc_mitm/source/controllers/emulated_switch_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ namespace ams::controller {
virtual ~EmulatedSwitchController() {};

virtual Result Initialize();

SwitchControllerType GetControllerType(void) { return m_emulated_type; };

bool IsOfficialController() { return false; }

Result HandleOutputDataReport(const bluetooth::HidReport *report) override;
Expand Down Expand Up @@ -65,11 +68,17 @@ namespace ams::controller {
Result FakeHidCommandResponse(const SwitchHidCommandResponse *response);
Result FakeNfcIrResponse(const SwitchNfcIrResponse *response);

Result SetEmulatedControllerType(SwitchControllerType type);

SwitchControllerType m_emulated_type;
SwitchControllerColours m_colours;

bool m_charging;
bool m_ext_power;
u8 m_battery;
u8 m_led_pattern;

SwitchButtonData m_buttons_previous;
SwitchButtonData m_buttons;
SwitchAnalogStick m_left_stick;
SwitchAnalogStick m_right_stick;
Expand Down
24 changes: 21 additions & 3 deletions mc_mitm/source/controllers/switch_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ namespace ams::controller {
SwitchPlayerNumber_Eight,
SwitchPlayerNumber_Unknown = 0xf
};

enum SwitchControllerType : u8 {
SwitchControllerType_LeftJoyCon = 1,
SwitchControllerType_RightJoyCon = 2,
SwitchControllerType_ProController = 3,
};

struct FirmwareVersion {
u8 major;
u8 minor;
};

struct HardwareID {
u16 vid;
Expand All @@ -49,7 +60,7 @@ namespace ams::controller {
u8 b;
} PACKED;

struct ProControllerColours {
struct SwitchControllerColours {
RGBColour body;
RGBColour buttons;
RGBColour left_grip;
Expand All @@ -61,7 +72,8 @@ namespace ams::controller {
u8 X : 1;
u8 B : 1;
u8 A : 1;
u8 : 2; // SR, SL (Right Joy)
u8 SR_R : 1;
u8 SL_R : 1;
u8 R : 1;
u8 ZR : 1;

Expand All @@ -77,7 +89,8 @@ namespace ams::controller {
u8 dpad_up : 1;
u8 dpad_right : 1;
u8 dpad_left : 1;
u8 : 2; // SR, SL (Left Joy)
u8 SR_L : 1;
u8 SL_L : 1;
u8 L : 1;
u8 ZL : 1;
} PACKED;
Expand Down Expand Up @@ -322,6 +335,9 @@ namespace ams::controller {

Result LedsMaskToPlayerNumber(u8 led_mask, u8 *player_number);

constexpr const FirmwareVersion joycon_fw_version = {0x04, 0x07};
constexpr const FirmwareVersion pro_controller_fw_version = {0x03, 0x48};

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

class SwitchController {
Expand All @@ -345,6 +361,8 @@ namespace ams::controller {
virtual ~SwitchController() { };

const bluetooth::Address& Address() const { return m_address; }

virtual SwitchControllerType GetControllerType(void) { return SwitchControllerType_ProController; }; //.Todo: handle this properly for official controllers

virtual bool IsOfficialController() { return true; }

Expand Down