Preliminary emulated Wiimote Extension support. It works in some games. The nunchuck controls are numpad 4, 5, 6, 8 for up down left right and C and Z for the c and z buttons. You have to select Extension connected from the settings to enable it.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@1212 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
John Peterson
2008-11-19 08:31:31 +00:00
parent 482cc819fb
commit 76ca6ac031
15 changed files with 1033 additions and 216 deletions

View File

@ -24,6 +24,7 @@
#include "wiimote_hid.h"
#include "EmuSubroutines.h"
#include "EmuDefinitions.h"
#include "Encryption.h" // for extension encryption
#include "Console.h" // for startConsoleWin, wprintf, GetConsoleHwnd
#include "Config.h" // for g_Config
@ -42,8 +43,7 @@ namespace WiiMoteEmu
//wm_request_status global_struct;
//extern wm_request_status global_struct;
//extern u16 g_channelID;
//void ReadExt();
// ===================================================
@ -61,35 +61,32 @@ void HidOutputReport(u16 _channelID, wm_report* sr) {
case 0x10:
LOGV(WII_IPC_WIIMOTE, 0, "HidOutputReport: unknown sr->channel 0x10");
break;
case WM_LEDS:
case WM_LEDS: // 0x11
WmLeds(_channelID, (wm_leds*)sr->data);
break;
case WM_READ_DATA:
WmReadData(_channelID, (wm_read_data*)sr->data);
case WM_DATA_REPORTING: // 0x12
WmDataReporting(_channelID, (wm_data_reporting*)sr->data);
break;
case WM_REQUEST_STATUS:
case WM_REQUEST_STATUS: // 0x15
//global_struct = (wm_request_status*)sr->data;
WmRequestStatus(_channelID, (wm_request_status*)sr->data);
break;
case WM_IR_PIXEL_CLOCK:
case WM_IR_LOGIC:
case WM_READ_DATA: // 0x17
WmReadData(_channelID, (wm_read_data*)sr->data);
break;
case WM_IR_PIXEL_CLOCK: // 0x13
case WM_IR_LOGIC: // 0x1a
LOGV(WII_IPC_WIIMOTE, 0, " IR Enable 0x%02x 0x%02x", sr->channel, sr->data[0]);
break;
case WM_WRITE_DATA:
case WM_WRITE_DATA: // 0x16
WmWriteData(_channelID, (wm_write_data*)sr->data);
break;
case WM_DATA_REPORTING:
WmDataReporting(_channelID, (wm_data_reporting*)sr->data);
break;
case WM_SPEAKER_ENABLE:
LOGV(WII_IPC_WIIMOTE, 1, " WM Speaker Enable 0x%02x 0x%02x", sr->channel, sr->data[0]);
break;
case WM_SPEAKER_MUTE:
LOGV(WII_IPC_WIIMOTE, 1, " WM Mute Enable 0x%02x 0x%02x", sr->channel, sr->data[0]);
break;
default:
PanicAlert("HidOutputReport: Unknown channel 0x%02x", sr->channel);
return;
@ -98,6 +95,9 @@ void HidOutputReport(u16 _channelID, wm_report* sr) {
}
// ===================================================
/* LED (blue lights) report. */
// ----------------
void WmLeds(u16 _channelID, wm_leds* leds) {
LOGV(WII_IPC_WIIMOTE, 0, "===========================================================");
@ -108,6 +108,8 @@ void WmLeds(u16 _channelID, wm_leds* leds) {
g_Leds = leds->leds;
LOGV(WII_IPC_WIIMOTE, 0, "===========================================================");
}
void WmSendAck(u16 _channelID, u8 _reportID)
{
u8 DataFrame[1024];
@ -135,105 +137,9 @@ void WmSendAck(u16 _channelID, u8 _reportID)
}
void WmDataReporting(u16 _channelID, wm_data_reporting* dr)
{
LOGV(WII_IPC_WIIMOTE, 0, "===========================================================");
LOG(WII_IPC_WIIMOTE, " Set Data reporting mode");
LOG(WII_IPC_WIIMOTE, " Rumble: %x", dr->rumble);
LOG(WII_IPC_WIIMOTE, " Continuous: %x", dr->continuous);
LOG(WII_IPC_WIIMOTE, " All The Time: %x (not only on data change)", dr->all_the_time);
LOG(WII_IPC_WIIMOTE, " Rumble: %x", dr->rumble);
LOG(WII_IPC_WIIMOTE, " Mode: 0x%02x", dr->mode);
g_ReportingMode = dr->mode;
g_ReportingChannel = _channelID;
switch(dr->mode) { //see Wiimote_Update()
case WM_REPORT_CORE:
case WM_REPORT_CORE_ACCEL:
case WM_REPORT_CORE_ACCEL_IR12:
case WM_REPORT_CORE_ACCEL_IR10_EXT6:
break;
default:
PanicAlert("Wiimote: Unknown reporting mode 0x%x", dr->mode);
}
// WmSendAck(_channelID, WM_DATA_REPORTING);
LOGV(WII_IPC_WIIMOTE, 0, "===========================================================");
}
void SendReportCore(u16 _channelID)
{
u8 DataFrame[1024];
u32 Offset = WriteWmReport(DataFrame, WM_REPORT_CORE);
wm_report_core* pReport = (wm_report_core*)(DataFrame + Offset);
Offset += sizeof(wm_report_core);
memset(pReport, 0, sizeof(wm_report_core));
FillReportInfo(pReport->c);
LOG(WII_IPC_WIIMOTE, " SendReportCore()");
g_WiimoteInitialize.pWiimoteInput(_channelID, DataFrame, Offset);
}
void SendReportCoreAccelIr12(u16 _channelID) {
u8 DataFrame[1024];
u32 Offset = WriteWmReport(DataFrame, WM_REPORT_CORE_ACCEL_IR12);
wm_report_core_accel_ir12* pReport = (wm_report_core_accel_ir12*)(DataFrame + Offset);
Offset += sizeof(wm_report_core_accel_ir12);
memset(pReport, 0, sizeof(wm_report_core_accel_ir12));
FillReportInfo(pReport->c);
FillReportAcc(pReport->a);
FillReportIR(pReport->ir[0], pReport->ir[1]);
LOGV(WII_IPC_WIIMOTE, 2, " SendReportCoreAccelIr12()");
g_WiimoteInitialize.pWiimoteInput(_channelID, DataFrame, Offset);
}
void SendReportCoreAccelIr10Ext(u16 _channelID)
{
u8 DataFrame[1024];
u32 Offset = WriteWmReport(DataFrame, WM_REPORT_CORE_ACCEL_IR10_EXT6);
wm_report_core_accel_ir10_ext6* pReport = (wm_report_core_accel_ir10_ext6*)(DataFrame + Offset);
Offset += sizeof(wm_report_core_accel_ir10_ext6);
memset(pReport, 0, sizeof(wm_report_core_accel_ir10_ext6));
FillReportInfo(pReport->c);
FillReportAcc(pReport->a);
FillReportIRBasic(pReport->ir[0], pReport->ir[1]);
LOG(WII_IPC_WIIMOTE, " SendReportCoreAccelIr10Ext()");
g_WiimoteInitialize.pWiimoteInput(_channelID, DataFrame, Offset);
}
void SendReportCoreAccel(u16 _channelID)
{
u8 DataFrame[1024];
u32 Offset = WriteWmReport(DataFrame, WM_REPORT_CORE_ACCEL);
wm_report_core_accel* pReport = (wm_report_core_accel*)(DataFrame + Offset);
Offset += sizeof(wm_report_core_accel);
memset(pReport, 0, sizeof(wm_report_core_accel));
FillReportInfo(pReport->c);
FillReportAcc(pReport->a);
LOG(WII_IPC_WIIMOTE, " SendReportCoreAccel()");
g_WiimoteInitialize.pWiimoteInput(_channelID, DataFrame, Offset);
}
// ===================================================
/* Read data from Wiimote and Extensions registers. */
// ----------------
void WmReadData(u16 _channelID, wm_read_data* rd)
{
LOGV(WII_IPC_WIIMOTE, 0, "===========================================================");
@ -260,42 +166,87 @@ void WmReadData(u16 _channelID, wm_read_data* rd)
u32 blockSize;
switch((address >> 16) & 0xFE)
{
/* case 0xA2:
/**/ case 0xA2:
block = g_RegSpeaker;
blockSize = WIIMOTE_REG_SPEAKER_SIZE;
break;*/
LOGV(WII_IPC_WIIMOTE, 0, "WmReadData: 0xB0 g_RegSpeaker");
break;
case 0xA4:
block = g_RegExt;
blockSize = WIIMOTE_REG_EXT_SIZE;
//PanicAlert("WmReadData: 0xA4 g_RegExt");
LOGV(WII_IPC_WIIMOTE, 0, " Case 0xA4: Read ExtReg ****************************");
//wprintf("WmReadData\n"); ReadExt();
break;
/* case 0xB0:
/**/ case 0xB0:
block = g_RegIr;
blockSize = WIIMOTE_REG_IR_SIZE;
//PanicAlert("WmReadData: 0xB0 g_RegIr");
LOGV(WII_IPC_WIIMOTE, 0, "WmReadData: 0xB0 g_RegIr");
break;*/
break;
default:
PanicAlert("WmWriteData: bad register block!");
return;
}
// -----------------------------------------
// Encrypt data that is read from the Wiimote Extension Register
// -------------
if(((address >> 16) & 0xfe) == 0xa4)
{
wprintf("\n\nWmReadData Address: %08x Offset: %08x Size: %i byte\n",
address, address & 0xffff, (u8)size);
// Debugging
u32 offset = address & 0xffff;
wprintf("Unencrypted data:\n");
for (int i = 0; i < (u8)size; i++)
{
wprintf("%02x ", g_RegExt[i + offset]);
if((i + 1) % 20 == 0) wprintf("\n");
}
// Check if encrypted reads is on
if(g_RegExt[0xf0] == 0xaa)
{
// Copy g_RegExt to g_RegExtTmp
memcpy(g_RegExtTmp, g_RegExt, sizeof(g_RegExt));
// Copy the registry to a temporary space
memcpy(g_RegExtTmp, g_RegExt, sizeof(g_RegExt));
// Encrypt the read
wiimote_encrypt(&g_ExtKey, &g_RegExtTmp[address & 0xffff], (address & 0xffff), (u8)size);
// Debugging
wprintf("\nEncrypted data:\n");
for (int i = 0; i < (u8)size; i++)
{
wprintf("%02x ", g_RegExtTmp[i + offset]);
if((i + 1) % 20 == 0) wprintf("\n");
}
//wprintf("\ng_RegExtTmp after: \n");
//ReadExtTmp();
//wprintf("\nFrom g_RegExt: \n");
//ReadExt();
// Update the block that SendReadDataReply will eventually send to the Wii
block = g_RegExtTmp;
}
wprintf("\n\n\n");
}
//---------
address &= 0xFFFF;
if(address + size > blockSize) {
PanicAlert("WmReadData: address + size out of bounds!");
return;
}
/*// Debugging
wprintf("WmReadData %08x %i\n", address, (u8)size);
for (int i = 0; i < sizeof(block+address); i++)
{
wprintf("%02x ", g_RegExt[i]);
if((i + 1) % 25 == 0) wprintf("\n");
}
wprintf("\n\n");
//---------*/
SendReadDataReply(_channelID, block+address, address, (u8)size);
}
@ -307,6 +258,10 @@ void WmReadData(u16 _channelID, wm_read_data* rd)
}
// ===================================================
/* Write data to Wiimote and Extensions registers. */
// ----------------
void WmWriteData(u16 _channelID, wm_write_data* wd)
{
LOGV(WII_IPC_WIIMOTE, 0, "========================================================");
@ -342,9 +297,13 @@ void WmWriteData(u16 _channelID, wm_write_data* wd)
case 0xA4:
block = g_RegExt; // Extension Controller register
blockSize = WIIMOTE_REG_EXT_SIZE;
LOGV(WII_IPC_WIIMOTE, 0, " Case 0xA4: Write ExtReg *********************************");
//PanicAlert(" Case 0xA4: Write g_RegExt");
//wprintf("WmWriteData\n"); ReadExt();
LOGV(WII_IPC_WIIMOTE, 0, " *******************************************************");
LOGV(WII_IPC_WIIMOTE, 0, "");
LOGV(WII_IPC_WIIMOTE, 0, " Case 0xA4: Write ExtReg");
LOGV(WII_IPC_WIIMOTE, 0, "");
LOGV(WII_IPC_WIIMOTE, 0, " *******************************************************");
wprintf("\n\nWmWriteData Size: %i Address: %08x Offset: %08x \n",
wd->size, address, (address & 0xffff));
break;
case 0xB0:
block = g_RegIr;
@ -354,29 +313,50 @@ void WmWriteData(u16 _channelID, wm_write_data* wd)
PanicAlert("WmWriteData: bad register block!");
return;
}
// Remove for example 0xa40000 from the address
address &= 0xFFFF;
// Check if the address is within bounds
if(address + wd->size > blockSize) {
PanicAlert("WmWriteData: address + size out of bounds!");
return;
}
// finally write the registers to memory
// Finally write the registers to the right structure
memcpy(block + address, wd->data, wd->size);
/*// Debugging
if( ((address >> 16) & 0xFE) == 0xa4 )
// -----------------------------------------
// Generate key for the Wiimote Extension
// -------------
if(blockSize == WIIMOTE_REG_EXT_SIZE)
{
wprintf("WmWriteData %i\n", wd->size);
for (int i = 0; i < sizeof(wd->data); i++)
// Debugging
// Write the data
wprintf("Data: ");
for (int i = 0; i < wd->size; i++)
{
wprintf("%02x ", wd->data[i]);
if((i + 1) % 25 == 0) wprintf("\n");
}
wprintf("\n\n");
wprintf("\n");
//wprintf("Current address: %08x\n", address);
//wprintf("g_RegExt:\n");
//ReadExt();
/* Run the key generation on all writes in the key area, it doesn't matter
that we send it parts of a key, only the last full key will have an
effect */
//if(address >= 0xa40040 && address <= 0xa4004c)
if(address >= 0x40 && address <= 0x4c)
//if(address == 0x4c)
wiimote_gen_key(&g_ExtKey, &g_RegExt[0x40]);
}
//---------*/
memcpy(wd->data, block + address, wd->size);
// -------------
} else {
@ -388,6 +368,10 @@ void WmWriteData(u16 _channelID, wm_write_data* wd)
LOGV(WII_IPC_WIIMOTE, 0, "==========================================================");
}
// ===================================================
/* Generate the right address for wm reports. */
// ----------------
int WriteWmReport(u8* dst, u8 channel) {
u32 Offset = 0;
hid_packet* pHidHeader = (hid_packet*)(dst + Offset);
@ -401,12 +385,9 @@ int WriteWmReport(u8* dst, u8 channel) {
return Offset;
}
// ===================================================
/* Here we produce status report to send to the Wii. We currently ignore the status
request rs and all its eventual instructions it may include (for example turn off
rumble or something else) and just send the status report. */
// ----------------
void WmRequestStatus_(u16 _channelID)
// Custom call version
void WmRequestStatus_(u16 _channelID, int a)
{
//PanicAlert("WmRequestStatus");
LOGV(WII_IPC_WIIMOTE, 0, "================================================");
@ -420,15 +401,24 @@ void WmRequestStatus_(u16 _channelID)
wm_status_report* pStatus = (wm_status_report*)(DataFrame + Offset);
Offset += sizeof(wm_status_report);
memset(pStatus, 0, sizeof(wm_status_report)); // fill the status report with zeroes
// Status values
pStatus->leds = g_Leds;
pStatus->ir = 1;
pStatus->battery = 0x4F; //arbitrary number
// this gets us passed the first error, but later brings up the disconnected error
if(g_Config.bExtensionConnected)
if(a == 1)
{
wprintf("pStatus->extension = 1\n");
pStatus->extension = 1;
}
else
{
wprintf("pStatus->extension = 0\n");
pStatus->extension = 0;
}
LOGV(WII_IPC_WIIMOTE, 0, " Extension: %x", pStatus->extension);
LOGV(WII_IPC_WIIMOTE, 0, " SendStatusReport()");
@ -440,6 +430,11 @@ void WmRequestStatus_(u16 _channelID)
}
// ===================================================
/* Here we produce a status report to send to the Wii. We currently ignore the status
request rs and all its eventual instructions it may include (for example turn off
rumble or something else) and just send the status report. */
// ----------------
void WmRequestStatus(u16 _channelID, wm_request_status* rs)
{
//PanicAlert("WmRequestStatus");
@ -455,11 +450,13 @@ void WmRequestStatus(u16 _channelID, wm_request_status* rs)
wm_status_report* pStatus = (wm_status_report*)(DataFrame + Offset);
Offset += sizeof(wm_status_report);
memset(pStatus, 0, sizeof(wm_status_report)); // fill the status report with zeroes
// Status values
pStatus->leds = g_Leds;
pStatus->ir = 1;
pStatus->battery = 0x4F; //arbitrary number
// this gets us passed the first error, but later brings up the disconnected error
// Read config value for this one
if(g_Config.bExtensionConnected)
pStatus->extension = 1;
else
@ -475,6 +472,10 @@ void WmRequestStatus(u16 _channelID, wm_request_status* rs)
}
// ===================================================
/* Here we produce the actual reply that we send to the Wii. The message is divided
into 16 bytes pieces and sent piece by piece. */
// ----------------
void SendReadDataReply(u16 _channelID, void* _Base, u16 _Address, u8 _Size)
{
LOGV(WII_IPC_WIIMOTE, 0, "=========================================");
@ -498,15 +499,16 @@ void SendReadDataReply(u16 _channelID, void* _Base, u16 _Address, u8 _Size)
pReply->address = Common::swap16(_Address + dataOffset);
// Write a pice
memcpy(pReply->data + dataOffset, _Base, copySize);
if(copySize < 16)
if(copySize < 16) // check if we have less than 16 bytes left to send
{
memset(pReply->data + copySize, 0, 16 - copySize);
}
dataOffset += copySize;
LOG(WII_IPC_WIIMOTE, " SendReadDataReply()");
LOG(WII_IPC_WIIMOTE, " Buttons: 0x%04x", pReply->buttons);
LOG(WII_IPC_WIIMOTE, " Error: 0x%x", pReply->error);
@ -525,6 +527,7 @@ void SendReadDataReply(u16 _channelID, void* _Base, u16 _Address, u8 _Size)
*/
//---------
// Send a piece
g_WiimoteInitialize.pWiimoteInput(_channelID, DataFrame, Offset);
_Size -= copySize;