diff --git a/nx/include/switch/services/hiddbg.h b/nx/include/switch/services/hiddbg.h index c5ff3ade1..0e074a66f 100644 --- a/nx/include/switch/services/hiddbg.h +++ b/nx/include/switch/services/hiddbg.h @@ -8,16 +8,27 @@ #include "../services/hid.h" #include "../services/sm.h" -/// HdlsDeviceInfo +/// HdlsDeviceInfo, for [7.0.0-8.1.0]. typedef struct { - u32 type; ///< Only one bit can be set. BIT(N*4+0) = Pro-Controller, BIT(N*4+1) = Joy-Con Left, BIT(N*4+2) = Joy-Con Right, BIT(N*4+3) = invalid. Where N is 0-1. BIT(8-10) = Pro-Controller, BIT(11) = Famicom-Controller, BIT(12) = Famicom-Controller II with microphone, BIT(13) = NES-Controller(DeviceType=0x200), BIT(14) = NES-Controller(DeviceType=0x400), BIT(15-16) = invalid, BIT(17) = unknown(DeviceType=0x8000), BIT(18-20) = invalid, BIT(21-23) = unknown(DeviceType=0x80000000). - u32 singleColorBody; ///< RGBA Single Body Color - u32 singleColorButtons; ///< RGBA Single Buttons Color - u8 type2; ///< Additional type field used with the above type field (only applies to type bit0-bit2 and bit21), if the value doesn't match one of the following a default is used. Type Pro-Controller: value 0x3 indicates that the controller is connected via USB. Type BIT(21): value 0x3 = unknown. When value is 0x2, state is merged with an existing controller (when the type value is compatible with this). Otherwise, it's a dedicated controller. - u8 pad[0x3]; ///< Padding + u32 deviceTypeInternal; ///< Only one bit can be set. BIT(N*4+0) = Pro-Controller, BIT(N*4+1) = Joy-Con Left, BIT(N*4+2) = Joy-Con Right, BIT(N*4+3) = invalid. Where N is 0-1. BIT(8-10) = Pro-Controller, BIT(11) = Famicom-Controller, BIT(12) = Famicom-Controller II with microphone, BIT(13) = NES-Controller(DeviceType=0x200), BIT(14) = NES-Controller(DeviceType=0x400), BIT(15-16) = invalid, BIT(17) = unknown(DeviceType=0x8000), BIT(18-20) = invalid, BIT(21-23) = unknown(DeviceType=0x80000000). + u32 singleColorBody; ///< RGBA Single Body Color. + u32 singleColorButtons; ///< RGBA Single Buttons Color. + u8 npadInterfaceType; ///< \ref HidNpadInterfaceType. Additional type field used with the above type field (only applies to type bit0-bit2 and bit21), if the value doesn't match one of the following a default is used. Type Pro-Controller: value 0x3 indicates that the controller is connected via USB. Type BIT(21): value 0x3 = unknown. When value is 0x2, state is merged with an existing controller (when the type value is compatible with this). Otherwise, it's a dedicated controller. + u8 pad[0x3]; ///< Padding. +} HiddbgHdlsDeviceInfoV7; + +/// HdlsDeviceInfo, for [9.0.0+]. Converted to/from \ref HiddbgHdlsDeviceInfoV7 on prior sysvers. +typedef struct { + u8 deviceType; ///< \ref HidDeviceType + u8 npadInterfaceType; ///< \ref HidNpadInterfaceType. Additional type field used with the above type field (only applies to ::HidDeviceType_JoyRight1, ::HidDeviceType_JoyLeft2, ::HidDeviceType_FullKey3, and ::HidDeviceType_System19), if the value doesn't match one of the following a default is used. ::HidDeviceType_FullKey3: ::NpadInterfaceType_USB indicates that the controller is connected via USB. :::HidDeviceType_System19: ::NpadInterfaceType_USB = unknown. When value is ::NpadInterfaceType_Rail, state is merged with an existing controller (with ::HidDeviceType_JoyRight1 / ::HidDeviceType_JoyLeft2). Otherwise, it's a dedicated controller. + u8 pad[0x2]; ///< Padding. + u32 singleColorBody; ///< RGBA Single Body Color. + u32 singleColorButtons; ///< RGBA Single Buttons Color. + u32 colorLeftGrip; ///< [9.0.0+] RGBA Left Grip Color. + u32 colorRightGrip; ///< [9.0.0+] RGBA Right Grip Color. } HiddbgHdlsDeviceInfo; -/// HdlsState +/// HdlsState, for [7.0.0-8.1.0]. typedef struct { u8 powerConnected; ///< powerConnected for the main PowerInfo, see \ref HidFlags. u8 flags; ///< ORRed with powerConnected to set the value of the first byte for \ref HidFlags. For example, value 1 here will set isCharging for the main PowerInfo. @@ -27,6 +38,16 @@ typedef struct { JoystickPosition joysticks[JOYSTICK_NUM_STICKS]; ///< \ref JoystickPosition u8 unk_x20; ///< Unused for input. Set with output from \ref hiddbgDumpHdlsStates. Not set by \ref hiddbgGetAbstractedPadsState. u8 padding[0x3]; ///< Padding +} HiddbgHdlsStateV7; + +/// HdlsState, for [9.0.0+]. Converted to/from \ref HiddbgHdlsDeviceInfoV7 on prior sysvers. +typedef struct { + u32 batteryCharge; ///< batteryCharge for the main PowerInfo, see \ref HidPowerInfo. + u32 flags; ///< Used to set the main PowerInfo for \ref HidFlags. BIT(0) -> powerConnected, BIT(1) -> isCharging. + u64 buttons; ///< See \ref HidControllerKeys. [9.0.0+] Masked with 0xfffffffff00fffff. + JoystickPosition joysticks[JOYSTICK_NUM_STICKS]; ///< \ref JoystickPosition + u8 unk_x20; ///< Unused for input. Set with output from \ref hiddbgDumpHdlsStates. + u8 padding[0x3]; ///< Padding } HiddbgHdlsState; /// HdlsNpadAssignmentEntry @@ -46,14 +67,29 @@ typedef struct { HiddbgHdlsNpadAssignmentEntry entries[0x10]; ///< \ref HiddbgHdlsNpadAssignmentEntry } HiddbgHdlsNpadAssignment; -/// HdlsStateListEntry +/// HdlsStateListEntryV7, for [7.0.0-8.1.0]. +typedef struct { + u64 HdlsHandle; ///< HdlsHandle + HiddbgHdlsDeviceInfoV7 device; ///< \ref HiddbgHdlsDeviceInfoV7. With \ref hiddbgApplyHdlsStateList this is only used when creating new devices. + HiddbgHdlsStateV7 state; ///< \ref HiddbgHdlsState +} HiddbgHdlsStateListEntryV7; + +/// HdlsStateListV7, for [7.0.0-8.1.0]. This contains a list of all controllers, including non-virtual controllers. +typedef struct { + s32 total_entries; ///< Total entries for the below entries. + u32 pad; ///< Padding + HiddbgHdlsStateListEntryV7 entries[0x10]; ///< \ref HiddbgHdlsStateListEntryV7 +} HiddbgHdlsStateListV7; + +/// HdlsStateListEntry, for [9.0.0+]. Converted to/from \ref HiddbgHdlsStateListEntryV7 on prior sysvers. typedef struct { u64 HdlsHandle; ///< HdlsHandle HiddbgHdlsDeviceInfo device; ///< \ref HiddbgHdlsDeviceInfo. With \ref hiddbgApplyHdlsStateList this is only used when creating new devices. - HiddbgHdlsState state; ///< \ref HiddbgHdlsState + alignas(8) HiddbgHdlsState state; ///< \ref HiddbgHdlsState } HiddbgHdlsStateListEntry; -/// HdlsStateList. This contains a list of all controllers, including non-virtual controllers. +/// HdlsStateList, for [9.0.0+]. Converted to/from \ref HiddbgHdlsStateListV7 on prior sysvers. +/// This contains a list of all controllers, including non-virtual controllers. typedef struct { s32 total_entries; ///< Total entries for the below entries. u32 pad; ///< Padding @@ -62,16 +98,16 @@ typedef struct { /// AbstractedPadState typedef struct { - u32 type; ///< Type. Converted to HiddbgHdlsDeviceInfo::type internally by \ref hiddbgSetAutoPilotVirtualPadState. BIT(0) -> BIT(0), BIT(1) -> BIT(15), BIT(2-3) -> BIT(1-2), BIT(4-5) -> BIT(1-2), BIT(6) -> BIT(3). BIT(7-11) -> BIT(11-15), BIT(12-14) -> BIT(12-14), BIT(15) -> BIT(17), BIT(31) -> BIT(21). + u32 type; ///< Type. Converted to HiddbgHdlsDeviceInfoV7::type internally by \ref hiddbgSetAutoPilotVirtualPadState. BIT(0) -> BIT(0), BIT(1) -> BIT(15), BIT(2-3) -> BIT(1-2), BIT(4-5) -> BIT(1-2), BIT(6) -> BIT(3). BIT(7-11) -> BIT(11-15), BIT(12-14) -> BIT(12-14), BIT(15) -> BIT(17), BIT(31) -> BIT(21). u8 flags; ///< Flags. Only bit0 is used by \ref hiddbgSetAutoPilotVirtualPadState: when clear it will skip using the rest of the input and run \ref hiddbgUnsetAutoPilotVirtualPadState internally. u8 pad[0x3]; ///< Padding u32 singleColorBody; ///< RGBA Single Body Color u32 singleColorButtons; ///< RGBA Single Buttons Color - u8 type2; ///< See HiddbgHdlsDeviceInfo::type2. + u8 npadInterfaceType; ///< See HiddbgHdlsDeviceInfo::npadInterfaceType. u8 pad2[0x3]; ///< Padding - HiddbgHdlsState state; ///< State + HiddbgHdlsStateV7 state; ///< State u8 unused[0x60]; ///< Unused with \ref hiddbgSetAutoPilotVirtualPadState. Not set by \ref hiddbgGetAbstractedPadsState. } HiddbgAbstractedPadState; @@ -90,6 +126,11 @@ Result hiddbgUpdateDesignInfo(u32 colorBody, u32 colorButtons, u32 colorLeftGrip /// This doesn't seem to be usable? Result hiddbgReadSerialFlash(u32 offset, void* buffer, size_t size, u64 UniquePadId); +/// Gets the internal DeviceType for the specified controller. See hidsys.h for UniquePadId. +/// Only available with [6.0.0+]. +/// Pre-9.0.0 the output is an u32, with [9.0.0+] it's an u8. +Result hiddbgGetUniquePadDeviceTypeSetInternal(u64 UniquePadId, u32 *out); + /// Gets a list of AbstractedPadHandles, where AbstractedPadHandles is the output array with max entries = count. total_entries is total entries written to the output array. /// Only available with [5.0.0-8.1.0]. Result hiddbgGetAbstractedPadHandles(u64 *AbstractedPadHandles, s32 count, s32 *total_entries); diff --git a/nx/source/services/hiddbg.c b/nx/source/services/hiddbg.c index 0d82e427c..c57162cb8 100644 --- a/nx/source/services/hiddbg.c +++ b/nx/source/services/hiddbg.c @@ -16,6 +16,31 @@ static size_t g_hiddbgPtrbufsize; static bool g_hiddbgHdlsInitialized; static TransferMemory g_hiddbgHdlsTmem; +static const u32 g_hiddbgDeviceTypeInternalTable[] = { + BIT(20), // DeviceType 0 Invalid + BIT(0*4+2), // DeviceType 1 JoyRight + BIT(0*4+1), // DeviceType 2 JoyLeft + BIT(1*4+0), // DeviceType 3 FullKey + BIT(1*4+1), // DeviceType 4 JoyLeft + BIT(1*4+2), // DeviceType 5 JoyRight + BIT(8), // DeviceType 6 FullKey + BIT(11), // DeviceType 7 LarkLeft (HVC) + BIT(12), // DeviceType 8 LarkRight (HVC) + BIT(13), // DeviceType 9 LarkLeft (NES) + BIT(14), // DeviceType 10 LarkRight (NES) + BIT(15), // DeviceType 11 Invalid + BIT(16), // DeviceType 12 Palma (Invalid for DeviceTypeInternal) + BIT(9), // DeviceType 13 FullKey + BIT(20), // DeviceType 14 Invalid + BIT(10), // DeviceType 15 FullKey + BIT(18), // DeviceType 16 Invalid + BIT(19), // DeviceType 17 Invalid + BIT(20), // DeviceType 18 Invalid + BIT(21), // DeviceType 19 ::HidDeviceTypeBits_System with HidControllerType |= TYPE_PROCONTROLLER. + BIT(22), // DeviceType 20 ::HidDeviceTypeBits_System with HidControllerType |= TYPE_JOYCON_PAIR. + BIT(23), // DeviceType 21 ::HidDeviceType System with HidControllerType |= TYPE_JOYCON_PAIR. +}; + Result hiddbgInitialize(void) { atomicIncrement64(&g_hiddbgRefCnt); @@ -292,6 +317,53 @@ Result hiddbgReadSerialFlash(u32 offset, void* buffer, size_t size, u64 UniquePa return rc; } +Result hiddbgGetUniquePadDeviceTypeSetInternal(u64 UniquePadId, u32 *out) { + if (hosversionBefore(6,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + Result rc; + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 UniquePadId; + } *raw; + + raw = serviceIpcPrepareHeader(&g_hiddbgSrv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 234; + raw->UniquePadId = UniquePadId; + + rc = serviceIpcDispatch(&g_hiddbgSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 out; + } *resp; + + serviceIpcParse(&g_hiddbgSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && out) { //Pre-9.0.0 output is an u32, with [9.0.0+] it's an u8. + if (hosversionBefore(9,0,0)) + *out = resp->out; + else + *out = resp->out & 0xFF; + } + } + + return rc; +} + Result hiddbgGetAbstractedPadHandles(u64 *AbstractedPadHandles, s32 count, s32 *total_entries) { if (hosversionBefore(5,0,0) || hosversionAtLeast(9,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); @@ -326,6 +398,8 @@ Result hiddbgGetAbstractedPadHandles(u64 *AbstractedPadHandles, s32 count, s32 * serviceIpcParse(&g_hiddbgSrv, &r, sizeof(*resp)); resp = r.Raw; + rc = resp->result; + if (R_SUCCEEDED(rc) && total_entries) *total_entries = resp->total_entries; } @@ -366,6 +440,8 @@ Result hiddbgGetAbstractedPadState(u64 AbstractedPadHandle, HiddbgAbstractedPadS serviceIpcParse(&g_hiddbgSrv, &r, sizeof(*resp)); resp = r.Raw; + rc = resp->result; + if (R_SUCCEEDED(rc) && state) memcpy(state, &resp->state, sizeof(*state)); } @@ -407,6 +483,8 @@ Result hiddbgGetAbstractedPadsState(u64 *AbstractedPadHandles, HiddbgAbstractedP serviceIpcParse(&g_hiddbgSrv, &r, sizeof(*resp)); resp = r.Raw; + rc = resp->result; + if (R_SUCCEEDED(rc) && total_entries) *total_entries = resp->total_entries; } @@ -448,6 +526,8 @@ Result hiddbgSetAutoPilotVirtualPadState(s8 AbstractedVirtualPadId, const Hiddbg serviceIpcParse(&g_hiddbgSrv, &r, sizeof(*resp)); resp = r.Raw; + + rc = resp->result; } return rc; @@ -485,6 +565,8 @@ Result hiddbgUnsetAutoPilotVirtualPadState(s8 AbstractedVirtualPadId) { serviceIpcParse(&g_hiddbgSrv, &r, sizeof(*resp)); resp = r.Raw; + + rc = resp->result; } return rc; @@ -497,6 +579,84 @@ Result hiddbgUnsetAllAutoPilotVirtualPadState(void) { return _hiddbgCmdNoIO(323); } +static u32 _hiddbgConvertDeviceTypeToDeviceTypeInternal(u8 deviceType) { + if (deviceType >= sizeof(g_hiddbgDeviceTypeInternalTable)/sizeof(u32)) return g_hiddbgDeviceTypeInternalTable[0]; + return g_hiddbgDeviceTypeInternalTable[deviceType]; +} + +static u8 _hiddbgConvertDeviceTypeInternalToDeviceType(u32 deviceType) { + for (u32 i=0; ideviceTypeInternal = _hiddbgConvertDeviceTypeToDeviceTypeInternal(in->deviceType); + out->singleColorBody = in->singleColorBody; + out->singleColorButtons = in->singleColorButtons; + out->npadInterfaceType = in->npadInterfaceType; +} + +static void _hiddbgConvertHdlsDeviceInfoFromV7(HiddbgHdlsDeviceInfo *out, const HiddbgHdlsDeviceInfoV7 *in) { + memset(out, 0, sizeof(*out)); + + out->deviceType = _hiddbgConvertDeviceTypeInternalToDeviceType(in->deviceTypeInternal); + out->npadInterfaceType = in->npadInterfaceType; + out->singleColorBody = in->singleColorBody; + out->singleColorButtons = in->singleColorButtons; + //Leave out color*Grip at zero since V7 doesn't have those. +} + +static void _hiddbgConverHiddbgHdlsStateToV7(HiddbgHdlsStateV7 *out, const HiddbgHdlsState *in) { + memset(out, 0, sizeof(*out)); + + out->powerConnected = (in->flags & BIT(0)) != 0; + out->flags = (in->flags & BIT(1)) != 0; + out->batteryCharge = in->batteryCharge; + out->buttons = in->buttons; + memcpy(out->joysticks, in->joysticks, sizeof(in->joysticks)); + out->unk_x20 = in->unk_x20; +} + +static void _hiddbgConverHiddbgHdlsStateFromV7(HiddbgHdlsState *out, const HiddbgHdlsStateV7 *in) { + memset(out, 0, sizeof(*out)); + + out->batteryCharge = in->batteryCharge; + out->flags = (in->powerConnected & 1) | ((in->flags & 1)<<1); + out->buttons = in->buttons; + memcpy(out->joysticks, in->joysticks, sizeof(in->joysticks)); + out->unk_x20 = in->unk_x20; +} + +static void _hiddbgConvertHdlsStateListToV7(HiddbgHdlsStateListV7 *out, const HiddbgHdlsStateList *in) { + s32 count; + memset(out, 0, sizeof(*out)); + out->total_entries = in->total_entries; + count = out->total_entries > 0x10 ? 0x10 : out->total_entries; + + for (s32 i=0; ientries[i].HdlsHandle = in->entries[i].HdlsHandle; + _hiddbgConvertHdlsDeviceInfoToV7(&out->entries[i].device, &in->entries[i].device); + _hiddbgConverHiddbgHdlsStateToV7(&out->entries[i].state, &in->entries[i].state); + } +} + +static void _hiddbgConvertHdlsStateListFromV7(HiddbgHdlsStateList *out, const HiddbgHdlsStateListV7 *in) { + s32 count; + memset(out, 0, sizeof(*out)); + out->total_entries = in->total_entries; + count = out->total_entries > 0x10 ? 0x10 : out->total_entries; + + for (s32 i=0; ientries[i].HdlsHandle = in->entries[i].HdlsHandle; + _hiddbgConvertHdlsDeviceInfoFromV7(&out->entries[i].device, &in->entries[i].device); + _hiddbgConverHiddbgHdlsStateFromV7(&out->entries[i].state, &in->entries[i].state); + } +} + static Result _hiddbgAttachHdlsWorkBuffer(TransferMemory *tmem) { IpcCommand c; ipcInitialize(&c); @@ -594,7 +754,15 @@ Result hiddbgDumpHdlsStates(HiddbgHdlsStateList *state) { rc = _hiddbgCmdNoIO(327); if (R_FAILED(rc)) return rc; - if (state) memcpy(state, g_hiddbgHdlsTmem.src_addr, sizeof(*state)); + if (state) { + if (hosversionBefore(9,0,0)) { + HiddbgHdlsStateListV7 statev7; + memcpy(&statev7, g_hiddbgHdlsTmem.src_addr, sizeof(statev7)); + _hiddbgConvertHdlsStateListFromV7(state, &statev7); + } + else + memcpy(state, g_hiddbgHdlsTmem.src_addr, sizeof(*state)); + } return rc; } @@ -622,10 +790,54 @@ Result hiddbgApplyHdlsStateList(const HiddbgHdlsStateList *state) { if (state==NULL) return MAKERESULT(Module_Libnx, LibnxError_BadInput); - memcpy(g_hiddbgHdlsTmem.src_addr, state, sizeof(*state)); + if (hosversionBefore(9,0,0)) { + HiddbgHdlsStateListV7 statev7; + _hiddbgConvertHdlsStateListToV7(&statev7, state); + memcpy(g_hiddbgHdlsTmem.src_addr, &statev7, sizeof(statev7)); + } + else + memcpy(g_hiddbgHdlsTmem.src_addr, state, sizeof(*state)); + return _hiddbgCmdNoIO(329); } +static Result _hiddbgAttachHdlsVirtualDeviceV7(u64 *HdlsHandle, const HiddbgHdlsDeviceInfoV7 *info) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + HiddbgHdlsDeviceInfoV7 info; + } *raw; + + raw = serviceIpcPrepareHeader(&g_hiddbgSrv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 330; + raw->info = *info; + + Result rc = serviceIpcDispatch(&g_hiddbgSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u64 handle; + } *resp; + + serviceIpcParse(&g_hiddbgSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && HdlsHandle) *HdlsHandle = resp->handle; + } + + return rc; +} + static Result _hiddbgAttachHdlsVirtualDevice(u64 *HdlsHandle, const HiddbgHdlsDeviceInfo *info) { IpcCommand c; ipcInitialize(&c); @@ -670,7 +882,13 @@ Result hiddbgAttachHdlsVirtualDevice(u64 *HdlsHandle, const HiddbgHdlsDeviceInfo if (!g_hiddbgHdlsInitialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); - return _hiddbgAttachHdlsVirtualDevice(HdlsHandle, info); + if (hosversionBefore(9,0,0)) { + HiddbgHdlsDeviceInfoV7 infov7; + _hiddbgConvertHdlsDeviceInfoToV7(&infov7, info); + return _hiddbgAttachHdlsVirtualDeviceV7(HdlsHandle, &infov7); + } + else + return _hiddbgAttachHdlsVirtualDevice(HdlsHandle, info); } Result hiddbgDetachHdlsVirtualDevice(u64 HdlsHandle) { @@ -690,16 +908,30 @@ static Result _hiddbgSetHdlsState(u64 HdlsHandle, const HiddbgHdlsState *state) struct { u64 magic; u64 cmd_id; - HiddbgHdlsState state; - u64 handle; + union { + struct { + HiddbgHdlsStateV7 state; + u64 handle; + } v7; // [7.0.0-8.1.0] + struct { + u64 handle; + HiddbgHdlsState state; + } v9; // [9.0.0+] + }; } *raw; raw = serviceIpcPrepareHeader(&g_hiddbgSrv, &c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 332; - memcpy(&raw->state, state, sizeof(*state)); - raw->handle = HdlsHandle; + if (hosversionBefore(9,0,0)) { + _hiddbgConverHiddbgHdlsStateToV7(&raw->v7.state, state); + raw->v7.handle = HdlsHandle; + } + else { + raw->v9.handle = HdlsHandle; + memcpy(&raw->v9.state, state, sizeof(*state)); + } Result rc = serviceIpcDispatch(&g_hiddbgSrv);