Nintendo SwitchのJoyConのhackを試してみる、という話の続き。(前回から随分と時間が空いてしまったが)
前回はボタン情報を読み取るだけしかできていなかったが、海外では解析が進んでいてJoyConに内蔵された加速度センサーやジャイロセンサーの値も読み出せるらしい。(前に見たときには解析がそこまで進んでいなかったのだが、最近チェックしたら凄く解析が進んでいるので驚いた。)
海外の情報を参考に、昔作ったWiiリモコン用のソフトをベースに改造して作り直して、JoyConのボタン/スティックとセンサーの情報をリアルタイムに表示させるソフトを作ってみた。
ソースコードは以下のようになっている。(zipファイルでソースコードとexeファイルをダウンロードできるようにしておいた)
// JoyCon Test version 0.1 Copyright(c)2018 by E.Kako // This version supports JoyCon-R only. #include <windows.h> #include <process.h> #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <string.h> #include <time.h> // for tiny_hid_dll #define FUNC __declspec(dllimport) __stdcall extern "C" HANDLE FUNC OpenHidHandle(unsigned short vendor_id, unsigned short product_id); extern "C" void FUNC ReadReport(HANDLE handle,unsigned char *InputReport,int *len); extern "C" void FUNC WriteReport(HANDLE handle,unsigned char *OutputReport, int *len); extern "C" void FUNC CloseHidHandle(HANDLE handle); HANDLE hRsDevHandle; const USHORT VID = 0x057e; // Nintendo const USHORT PID = 0x2007; // JonCon - R #define MAXREPORTSIZE 256 int InputLength,OutputLength; unsigned char InputReport[MAXREPORTSIZE]; unsigned char OutputReport[MAXREPORTSIZE]; int packet_num = 0; unsigned char joycon_loop_flag; unsigned char joycon_in_use; uintptr_t joycon_th; unsigned char res_type; unsigned char button1,button2; unsigned int StickX,StickY; unsigned int Ax,Ay,Az; unsigned int Rx,Ry,Rz; void Sleep2(int msec) { LARGE_INTEGER f; // for hi-res. performance counter LARGE_INTEGER t1,t2; // for hi-res. performance counter double t; QueryPerformanceFrequency(&f); QueryPerformanceCounter(&t1); while (1) { QueryPerformanceCounter(&t2); t = (double)t2.QuadPart - (double)t1.QuadPart; t /= (double)f.QuadPart; t *= 1000; if (t>(double)msec) { return; } } } void cmd_0x01_parameter_set() { OutputReport[0]=0x01; OutputReport[1]=( (++packet_num) % 16); OutputReport[2]=0; OutputReport[3]=0; OutputReport[4]=0; OutputReport[5]=0; OutputReport[6]=0; OutputReport[7]=0; OutputReport[8]=0; OutputReport[9]=0; } void Report_subcmd_0x30(unsigned char data) { cmd_0x01_parameter_set(); OutputReport[10]= 0x30; OutputReport[11]= data; WriteReport( hRsDevHandle , OutputReport , &OutputLength); ReadReport( hRsDevHandle , InputReport, &InputLength); } void Report_subcmd_0x03(unsigned char data) { cmd_0x01_parameter_set(); OutputReport[10]= 0x03; OutputReport[11]= data; WriteReport( hRsDevHandle , OutputReport , &OutputLength); ReadReport( hRsDevHandle , InputReport, &InputLength); } void Report_subcmd_0x40(unsigned char data) { cmd_0x01_parameter_set(); OutputReport[10]= 0x40; OutputReport[11]= data; WriteReport( hRsDevHandle , OutputReport , &OutputLength); ReadReport( hRsDevHandle , InputReport, &InputLength); } void Report_subcmd_0x48(unsigned char data) { cmd_0x01_parameter_set(); OutputReport[10]= 0x48; OutputReport[11]= data; WriteReport( hRsDevHandle , OutputReport , &OutputLength); ReadReport( hRsDevHandle , InputReport, &InputLength); } void Joycon_mode_0x30(void) { Report_subcmd_0x03(0x30); // standard full mode // respond automatically (interval = ??) } void Joycon_mode_0x3F(void) { Report_subcmd_0x03(0x3F); // button ONLY Report Mode (HID gamepad mode) // respond when button changes only } void Joycon_IMU_enable(void) { Report_subcmd_0x40(0x01); } void Joycon_IMU_disable(void) { Report_subcmd_0x40(0x00); } void JoyCon_LED_on() { Report_subcmd_0x30(0x01); // set LED data 0x01 ... LED1 on // 0x01 = led 1(top) , 0x02 = led 2 , 0x04 = led 3 , 0x08 = led 4 // 0x10 = led 1 blink , 0x20 = led 2 blink , 0x40 = led 3 blink , 0x80 = led 4 blink } void JoyCon_LED_off() { Report_subcmd_0x30(0x00); // set LED data -> all OFF } void Joycon_Haptic_enable(void) { Report_subcmd_0x48(0x01); } void Joycon_Haptic_disable(void) { Report_subcmd_0x48(0x00); } void JoyCon_Haptic_test(unsigned char d1,unsigned char d2,unsigned char d3,unsigned char d4) { OutputReport[0]=0x10; OutputReport[1]=( (++packet_num) % 16); OutputReport[2]=d1; OutputReport[3]=d2; OutputReport[4]=d3; OutputReport[5]=d4; OutputReport[6]=d1; OutputReport[7]=d2; OutputReport[8]=d3; OutputReport[9]=d4; WriteReport( hRsDevHandle , OutputReport , &OutputLength); } void JoyCon_Haptic_off(unsigned char d1,unsigned char d2,unsigned char d3,unsigned char d4) { JoyCon_Haptic_test(0x3c,0,0x2f,0x40); } void Joycon_Input(void) { int l; ReadReport( hRsDevHandle , InputReport, &InputLength); res_type=InputReport[0]; if (InputLength == 362) { // button1=InputReport[5]; button2=InputReport[4]; // JoyCon-L button1=InputReport[3]; button2=InputReport[4]; // JoyCon-R // StickX = InputReport[6] | ((unsigned int)InputReport[7]&0x0F)<<8; // JoyCon-L // StickY = (InputReport[7]>>4 ) | (InputReport[8] <<4 ); StickX = InputReport[9] | ((unsigned int)InputReport[10]&0x0F)<<8; // JoyCon-R StickY = (InputReport[10]>>4 ) | (InputReport[11] <<4 ); Ax=InputReport[13] | (unsigned int)InputReport[14]<<8; Ay=InputReport[15] | (unsigned int)InputReport[16]<<8; Az=InputReport[17] | (unsigned int)InputReport[18]<<8; Rx=InputReport[19] | (unsigned int)InputReport[20]<<8; Ry=InputReport[21] | (unsigned int)InputReport[22]<<8; Rz=InputReport[23] | (unsigned int)InputReport[24]<<8; } else { printf("%d\n",InputLength); } } void Joycon_Input_Main(void *) { // Joycon input thread while (joycon_loop_flag) { joycon_in_use=1; Joycon_Input(); Sleep2(16); } joycon_in_use=0; return; } void Start_JoyCon_th(void) { joycon_loop_flag=1; joycon_th = _beginthread( Joycon_Input_Main , 0 , NULL ); } int JoyCon_th_check(void) { res_type=0; Sleep2(100); return (res_type!=0); } void Stop_Joycon_th(void) { joycon_loop_flag=0; Sleep2(100); if (joycon_in_use==1) { TerminateThread( (HANDLE)joycon_th , -1); // kill thread if it stucks } } void MainLoop() { unsigned char c; while (1) { if (kbhit()) { c= getch(); if (c==0x1B) { break; } } printf("button=%02X,%02X , stick=%04X,%04X , Acc=%04X,%04X,%04X , Gyr=%04X,%04X,%04X\n", button1,button2,StickX,StickY,Ax,Ay,Az,Rx,Ry,Rz); Sleep2(16); } } int main( int argc, char *argv[]) { hRsDevHandle = OpenHidHandle(VID,PID); if ( hRsDevHandle == INVALID_HANDLE_VALUE) { MessageBox(0,"JoyCon-R not found.",0,0); return 1; } Joycon_mode_0x30(); // joycon standard mode Joycon_IMU_enable(); // gyro acc sensor enable JoyCon_LED_on(); Start_JoyCon_th(); if ( JoyCon_th_check() ) { MainLoop(); } else { MessageBox(0,"Joycon-R cannot open.",0,0); } Stop_Joycon_th(); JoyCon_LED_off(); Joycon_IMU_disable(); Joycon_mode_0x3F(); // HID mode CloseHidHandle( hRsDevHandle ); return 0; }
download: joycon_test_v01.zip (12.6kbyte)
まだ作っている途中なので、JoyCon-Lに対応していないとかの未完成な部分もあるのだが、とりあえず公開しておく。
—
追記
見直してみたら、関数名にJoyCon~と書いてあるものとJoycon~と書いてあるものが混在していてダサい。
あとで直したものを公開する。
→ 公開した。