Stage1: Introducing MotionPlus as emulated extension, allowing us to boot games that require the MotionPlus itself.

I also already implemented most of the needed data structures and datareport handling for the motionplus-nunchuk passthrough mode, which I'll add up next as well.

Wii Sports Resort is now bootable by just using an emulated wiimote (only working under the old wiimote plugin atm,
I asked billiard to port my commit into his plugin since he knows his plugin better than me and its less work for him than for me,
I kept most parts of my code in modules to simplify that task).

Upcoming stage2: Faking the motionplus on the fly on real wiimotes, that means you will be able to play, e.g.  Redsteel2 with a real wiimote and nunchuk or wii sports resort with just your real wiimote.
and nunchuk, but w/o the need of having a real motionpluscontroller connected. The new Zelda 2010 will benefit by that, since it will require a motionplus controller.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5438 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
snzgoo 2010-05-07 22:39:06 +00:00
parent f6ce87765f
commit 7742e1a6dd
11 changed files with 530 additions and 77 deletions

View File

@ -167,7 +167,6 @@ void WiimoteBasicConfigDialog::CreateGUIControls()
m_UprightWiimote[i]->SetToolTip(wxT("Treat the upright position as neutral"));
m_WiiMotionPlusConnected[i] = new wxCheckBox(m_Controller[i], IDC_MOTIONPLUSCONNECTED, wxT("Wii Motion Plus Connected"));
m_WiiMotionPlusConnected[i]->Enable(false);
m_Extension[i] = new wxChoice(m_Controller[i], IDC_EXTCONNECTED, wxDefaultPosition, wxDefaultSize, arrayStringFor_extension, 0, wxDefaultValidator);
@ -382,7 +381,7 @@ void WiimoteBasicConfigDialog::DoUseReal()
// Are we using an extension now? The report that it's removed, then reconnected.
bool UsingExtension = false;
if (WiiMoteEmu::WiiMapping[m_Page].iExtensionConnected != WiiMoteEmu::EXT_NONE)
if ((WiiMoteEmu::WiiMapping[m_Page].iExtensionConnected != WiiMoteEmu::EXT_NONE) || ( WiiMoteEmu::WiiMapping[m_Page].bMotionPlusConnected))
UsingExtension = true;
DEBUG_LOG(WIIMOTE, "DoUseReal() Connect extension: %i", !UsingExtension);
@ -466,6 +465,11 @@ void WiimoteBasicConfigDialog::GeneralSettingsChanged(wxCommandEvent& event)
break;
case IDC_MOTIONPLUSCONNECTED:
WiiMoteEmu::WiiMapping[m_Page].bMotionPlusConnected = m_WiiMotionPlusConnected[m_Page]->IsChecked();
DoExtensionConnectedDisconnected(WiiMoteEmu::EXT_NONE);
if (g_EmulatorRunning)
SLEEP(25);
WiiMoteEmu::UpdateExtRegisterBlocks(m_Page);
DoExtensionConnectedDisconnected();
break;
case IDC_WIIAUTORECONNECT:
WiiMoteEmu::WiiMapping[m_Page].bWiiAutoReconnect = m_WiiAutoReconnect[m_Page]->IsChecked();

View File

@ -755,7 +755,7 @@ void WiimotePadConfigDialog::CreatePadGUIControls()
m_gWiimote[i]->Add(m_sWmVertRight[i], 0, (wxLEFT | wxRIGHT | wxDOWN), 1);
// Extension Mapping
if(WiiMoteEmu::WiiMapping[i].iExtensionConnected == WiiMoteEmu::EXT_NUNCHUCK)
if(WiiMoteEmu::WiiMapping[i].iExtensionConnected == WiiMoteEmu::EXT_NUNCHUK)
{
// Stick controls
m_NunchuckTextStick[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Stick"));
@ -763,11 +763,11 @@ void WiimotePadConfigDialog::CreatePadGUIControls()
for (int x = 0; x <= IDB_NC_SHAKE - IDB_NC_Z; x++)
{
m_statictext_NunChuck[x][i] = new wxStaticText(m_Controller[i], wxID_ANY, ncText[x]);
m_statictext_Nunchuk[x][i] = new wxStaticText(m_Controller[i], wxID_ANY, ncText[x]);
m_Button_NunChuck[x][i] = new wxButton(m_Controller[i], x + IDB_NC_Z, wxEmptyString, wxDefaultPosition, wxSize(BtW, BtH));
m_Button_NunChuck[x][i]->SetFont(m_SmallFont);
m_Sizer_NunChuck[x][i] = new wxBoxSizer(wxHORIZONTAL);
m_Sizer_NunChuck[x][i]->Add(m_statictext_NunChuck[x][i], 0, (wxUP), 4);
m_Sizer_NunChuck[x][i]->Add(m_statictext_Nunchuk[x][i], 0, (wxUP), 4);
m_Sizer_NunChuck[x][i]->Add(m_Button_NunChuck[x][i], 0, (wxLEFT), 2);
}
@ -895,7 +895,7 @@ void WiimotePadConfigDialog::CreatePadGUIControls()
m_sHorizControllerMapping[i]->Add(m_gWiimote[i], 0, (wxLEFT), 5);
switch(WiiMoteEmu::WiiMapping[i].iExtensionConnected)
{
case WiiMoteEmu::EXT_NUNCHUCK:
case WiiMoteEmu::EXT_NUNCHUK:
m_sHorizControllerMapping[i]->Add(m_gNunchuck[i], 0, (wxLEFT), 5);
break;
case WiiMoteEmu::EXT_CLASSIC_CONTROLLER:
@ -1074,7 +1074,7 @@ void WiimotePadConfigDialog::UpdateGUI()
m_Button_Wiimote[x][m_Page]->SetLabel(wxString::FromAscii(
InputCommon::VKToString(WiiMoteEmu::WiiMapping[m_Page].Button[x + WiiMoteEmu::EWM_A]).c_str()));
}
if(WiiMoteEmu::WiiMapping[m_Page].iExtensionConnected == WiiMoteEmu::EXT_NUNCHUCK)
if(WiiMoteEmu::WiiMapping[m_Page].iExtensionConnected == WiiMoteEmu::EXT_NUNCHUK)
{
m_NunchuckComboStick[m_Page]->SetSelection(WiiMoteEmu::WiiMapping[m_Page].Stick.NC);
for (int x = 0; x <= IDB_NC_SHAKE - IDB_NC_Z; x++)
@ -1104,7 +1104,7 @@ void WiimotePadConfigDialog::UpdateGUI()
InputCommon::XKeyToString(WiiMoteEmu::WiiMapping[m_Page].Button[x + WiiMoteEmu::EWM_A], keyStr);
m_Button_Wiimote[x][m_Page]->SetLabel(wxString::FromAscii(keyStr));
}
if(WiiMoteEmu::WiiMapping[m_Page].iExtensionConnected == WiiMoteEmu::EXT_NUNCHUCK)
if(WiiMoteEmu::WiiMapping[m_Page].iExtensionConnected == WiiMoteEmu::EXT_NUNCHUK)
{
m_NunchuckComboStick[m_Page]->SetSelection(WiiMoteEmu::WiiMapping[m_Page].Stick.NC);
for (int x = 0; x <= IDB_NC_SHAKE - IDB_NC_Z; x++)

View File

@ -257,7 +257,7 @@ class WiimotePadConfigDialog : public wxDialog
*m_tTriggerSource[MAX_WIIMOTES],
*m_statictext_Analog[IDB_TRIGGER_R - IDB_ANALOG_LEFT_X + 1][MAX_WIIMOTES],
*m_statictext_Wiimote[IDB_WM_SHAKE - IDB_WM_A + 1][MAX_WIIMOTES],
*m_statictext_NunChuck[IDB_NC_SHAKE - IDB_NC_Z + 1][MAX_WIIMOTES],
*m_statictext_Nunchuk[IDB_NC_SHAKE - IDB_NC_Z + 1][MAX_WIIMOTES],
*m_statictext_Classic[IDB_CC_RD - IDB_CC_A + 1][MAX_WIIMOTES],
*m_statictext_GH3[IDB_GH3_STRUM_DOWN - IDB_GH3_GREEN + 1][MAX_WIIMOTES],
*m_NunchuckTextStick[5],

View File

@ -196,7 +196,7 @@ void SendReportCoreAccelExt16(u16 _channelID)
FillReportAcc(pReport->a);
#endif
if(WiiMapping[g_ID].iExtensionConnected == EXT_NUNCHUCK)
if(WiiMapping[g_ID].iExtensionConnected == EXT_NUNCHUK)
{
#if defined(HAVE_WX) && HAVE_WX
FillReportExtension(pReport->ext);
@ -233,38 +233,53 @@ void SendReportCoreAccelIr10Ext(u16 _channelID)
memset(pReport, 0, sizeof(wm_report_core_accel_ir10_ext6));
// Make a classic extension struct
wm_classic_extension _ext;
wm_GH3_extension _GH3_ext;
memset(&_ext, 0, sizeof(wm_classic_extension));
memset(&_GH3_ext, 0, sizeof(wm_GH3_extension));
#if defined(HAVE_WX) && HAVE_WX
FillReportInfo(pReport->c);
FillReportAcc(pReport->a);
FillReportIRBasic(pReport->ir[0], pReport->ir[1]);
#endif
if(WiiMapping[g_ID].iExtensionConnected == EXT_NUNCHUCK)
if ((WiiMapping[g_ID].bMotionPlusConnected) && (( WiiMapping[g_ID].iExtensionConnected == EXT_NUNCHUK ) || (WiiMapping[g_ID].iExtensionConnected == EXT_NONE)) )
{
#if defined(HAVE_WX) && HAVE_WX
FillReportExtension(pReport->ext);
#endif
}
else if(WiiMapping[g_ID].iExtensionConnected == EXT_CLASSIC_CONTROLLER)
{
#if defined(HAVE_WX) && HAVE_WX
FillReportClassicExtension(_ext);
#endif
// Copy _ext to pReport->ext
memcpy(&pReport->ext, &_ext, sizeof(_ext));
}
else if(WiiMapping[g_ID].iExtensionConnected == EXT_GUITARHERO)
{
#if defined(HAVE_WX) && HAVE_WX
FillReportGuitarHero3Extension(_GH3_ext);
#endif
memcpy(&pReport->ext, &_GH3_ext, sizeof(_GH3_ext));
}
if(WiiMapping[g_ID].iExtensionConnected == EXT_NUNCHUK)
{
FillReportMotionPlus(pReport->ext, true);
}
else if (WiiMapping[g_ID].iExtensionConnected == EXT_NONE)
{
FillReportMotionPlus(pReport->ext, false);
}
}
else {
if(WiiMapping[g_ID].iExtensionConnected == EXT_NUNCHUK)
{
FillReportExtension(pReport->ext);
}
else if(WiiMapping[g_ID].iExtensionConnected == EXT_CLASSIC_CONTROLLER)
{
wm_classic_extension _ext;
memset(&_ext, 0, sizeof(wm_classic_extension));
FillReportClassicExtension(_ext);
// Copy _ext to pReport->ext
memcpy(&pReport->ext, &_ext, sizeof(_ext));
}
else if(WiiMapping[g_ID].iExtensionConnected == EXT_GUITARHERO)
{
wm_GH3_extension _GH3_ext;
memset(&_GH3_ext, 0, sizeof(wm_GH3_extension));
FillReportGuitarHero3Extension(_GH3_ext);
memcpy(&pReport->ext, &_GH3_ext, sizeof(_GH3_ext));
}
}
#endif
INFO_LOG(WIIMOTE, " SendReportCoreAccelIr10Ext(0x37)");
DEBUG_LOG(WIIMOTE, " Channel: %04x", _channelID);
DEBUG_LOG(WIIMOTE, " Size: %08x", Offset);

View File

@ -44,12 +44,16 @@ u8 g_IRClock[MAX_WIIMOTES];
u8 g_IR[MAX_WIIMOTES];
u8 g_Leds[MAX_WIIMOTES];
u8 g_Speaker[MAX_WIIMOTES];
u8 g_MotionPlus[MAX_WIIMOTES];
u8 g_SpeakerMute[MAX_WIIMOTES];
int g_MotionPlusReadError[MAX_WIIMOTES];
u8 g_RegExtTmp[WIIMOTE_REG_EXT_SIZE];
int g_ID; // Current refreshing Wiimote
bool g_ReportingAuto[MAX_WIIMOTES]; // Auto report or passive report
bool g_MotionPlusConnected[MAX_WIIMOTES]; //MotionPlusinitiated
bool g_InterleavedData[MAX_WIIMOTES]; //sending alternated data packets from the nunchuk/motionplus
u8 g_ReportingMode[MAX_WIIMOTES]; // The reporting mode and channel id
u16 g_ReportingChannel[MAX_WIIMOTES];

View File

@ -86,14 +86,17 @@ extern u8 g_IRClock[MAX_WIIMOTES];
extern u8 g_IR[MAX_WIIMOTES];
extern u8 g_Leds[MAX_WIIMOTES];
extern u8 g_Speaker[MAX_WIIMOTES];
extern u8 g_MotionPlus[MAX_WIIMOTES];
extern u8 g_SpeakerMute[MAX_WIIMOTES];
extern int g_MotionPlusReadError[MAX_WIIMOTES];
extern u8 g_RegExtTmp[WIIMOTE_REG_EXT_SIZE];
extern int g_ID;
extern bool g_ReportingAuto[MAX_WIIMOTES];
extern u8 g_ReportingMode[MAX_WIIMOTES];
extern u16 g_ReportingChannel[MAX_WIIMOTES];
extern bool g_InterleavedData[MAX_WIIMOTES];
extern wiimote_key g_ExtKey[MAX_WIIMOTES]; // extension encryption key
extern bool g_Encryption;
@ -113,7 +116,24 @@ static const u8 EepromData_16D0[] = {
0x33, 0xCC, 0x44, 0xBB, 0x00, 0x00, 0x66, 0x99,
0x77, 0x88, 0x00, 0x00, 0x2B, 0x01, 0xE8, 0x13
};
static const u8 motionplus_register[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x79, 0x83, 0x73, 0x54, 0x72, 0xE8, 0x30, 0xC3, 0xCC, 0x4A, 0x34, 0xFC, 0xC8, 0x4F, 0xCC, 0x5B,
0x77, 0x49, 0x75, 0xA4, 0x73, 0x9A, 0x35, 0x52, 0xCA, 0x22, 0x37, 0x26, 0x2D, 0xE5, 0xB5, 0xA2,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x78, 0x76, 0xDD, 0xF5, 0x6A, 0x3C, 0xCF, 0xF7, 0x2A, 0x0E, 0x32, 0xEE, 0x82, 0xFE, 0x2E, 0xFD,
0x19, 0xE7, 0x0A, 0xCA, 0x67, 0x3B, 0x3A, 0x75, 0xF6, 0x45, 0x55, 0x8E, 0x9D, 0x33, 0xCC, 0xEA,
0x6E, 0x52, 0xC6, 0xC6, 0x16, 0x9B, 0xEE, 0x12, 0x2E, 0x3F, 0x77, 0xB1, 0xA1, 0x80, 0x0B, 0x0E,
0xC2, 0x25, 0x05, 0xEA, 0xC3, 0x2F, 0x85, 0x1E, 0x31, 0x53, 0x74, 0xC7, 0xF1, 0x93, 0xF1, 0x2D,
0xC1, 0x6D, 0x84, 0x2A, 0xD8, 0x6F, 0x8A, 0xE5, 0x2D, 0x3B, 0x7B, 0xCC, 0xD2, 0x59, 0xD5, 0xD1,
0x9F, 0x5B, 0x6F, 0xAE, 0x82, 0xDE, 0xEA, 0xC3, 0x73, 0x42, 0x06, 0xA9, 0x77, 0xFF, 0x61, 0xA8,
0x1A, 0x70, 0xE4, 0x16, 0x90, 0x7A, 0x80, 0xF7, 0x79, 0x4B, 0x41, 0x18, 0x82, 0x6C, 0x62, 0x1A,
0x3B, 0xBF, 0xFC, 0xFF, 0x2C, 0xF2, 0x32, 0x97, 0xB8, 0x2F, 0x17, 0xE7, 0xBD, 0x35, 0x1D, 0x0A,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x55, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0xff, 0xff, 0x00, 0x00, 0xa6, 0x20, 0x00, 0x05
};
/* Default calibration for the nunchuck. It should be written to 0x20 - 0x3f of the
extension register. 0x80 is the neutral x and y accelerators and 0xb3 is the
@ -133,6 +153,12 @@ static const u8 wireless_nunchuck_calibration[] =
255, 0, 125, 255,
0, 126, 0xed, 0x43
};
/* Default calibration for the motion plus*/
static const u8 motion_plus_calibration[] =
{
0x79, 0xbe, 0x77, 0x5a, 0x77, 0x38, 0x2f, 0x90, 0xcd, 0x3b, 0x2f, 0xfd, 0xc8, 0x29, 0x9c, 0x75,
0x7d, 0xd4, 0x78, 0xef, 0x78, 0x8a, 0x35, 0xa6, 0xc9, 0x9b, 0x33, 0x50, 0x2d, 0x00, 0xbd, 0x23
};
/* Classic Controller calibration */
static const u8 classic_calibration[] =
@ -146,6 +172,11 @@ static const u8 nunchuck_id[] = { 0x00, 0x00, 0xa4, 0x20, 0x00, 0x00 };
static const u8 classic_id[] = { 0x00, 0x00, 0xa4, 0x20, 0x01, 0x01 };
static const u8 gh3glp_id[] = { 0x00, 0x00, 0xa4, 0x20, 0x01, 0x03 };
static const u8 ghwtdrums_id[] = { 0x01, 0x00, 0xa4, 0x20, 0x01, 0x03 };
static const u8 wbb_id[] = { 0x00, 0x00, 0xa4, 0x20, 0x4, 0x02 };
static const u8 motionplus_id[] = { 0x00, 0x00, 0xa4, 0x20, 0x04, 0x05 };
static const u8 motionplusnunchuk_id[] = { 0x00, 0x00, 0xa4, 0x20, 0x05, 0x05 };
//initial control packet for datatransfers over 0x37 reports
static const u8 motionpluscheck_id[] = { 0xa3, 0x62, 0x45, 0xaa, 0x04, 0x02};
// The id for nothing inserted
static const u8 nothing_id[] = { 0x00, 0x00, 0x00, 0x00, 0x2e, 0x2e };
// The id for a partially inserted extension
@ -155,9 +186,10 @@ static const u8 partially_id[] = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff };
enum EExtensionType
{
EXT_NONE = 0,
EXT_NUNCHUCK,
EXT_NUNCHUK,
EXT_CLASSIC_CONTROLLER,
EXT_GUITARHERO,
EXT_WBB,
};
enum EInputType

View File

@ -416,6 +416,8 @@ void ResetVariables()
for (int i = 0; i < MAX_WIIMOTES; i++)
{
g_ReportingAuto[i] = false;
g_MotionPlusReadError[i] = 0;
g_InterleavedData[i] = false;
g_ReportingMode[i] = 0;
g_ReportingChannel[i] = 0;
WiiMapping[i].Motion.TiltWM.Shake = 0;
@ -492,25 +494,56 @@ void InitCalibration()
// Update the extension calibration values with our default values
void UpdateExtRegisterBlocks(int Slot)
{
// Copy extension id and calibration to its register
if(WiiMapping[Slot].iExtensionConnected == EXT_NUNCHUCK)
if (WiiMapping[Slot].bMotionPlusConnected)
{
memcpy(g_RegExt[Slot] + 0x20, nunchuck_calibration, sizeof(nunchuck_calibration));
memcpy(g_RegExt[Slot] + 0x30, nunchuck_calibration, sizeof(nunchuck_calibration));
memcpy(g_RegExt[Slot] + 0xfa, nunchuck_id, sizeof(nunchuck_id));
}
else if(WiiMapping[Slot].iExtensionConnected == EXT_CLASSIC_CONTROLLER)
{
memcpy(g_RegExt[Slot] + 0x20, classic_calibration, sizeof(classic_calibration));
memcpy(g_RegExt[Slot] + 0x30, classic_calibration, sizeof(classic_calibration));
memcpy(g_RegExt[Slot] + 0xfa, classic_id, sizeof(classic_id));
}
else if(WiiMapping[Slot].iExtensionConnected == EXT_GUITARHERO)
{
// TODO get the correct values here
memcpy(g_RegExt[Slot] + 0x20, classic_calibration, sizeof(classic_calibration));
memcpy(g_RegExt[Slot] + 0x30, classic_calibration, sizeof(classic_calibration));
memcpy(g_RegExt[Slot] + 0xfa, gh3glp_id, sizeof(gh3glp_id));
// Copy extension id and calibration to its register
if (WiiMapping[Slot].iExtensionConnected == EXT_NONE)
{
memset(g_RegExt[Slot],0,sizeof(g_RegExt[0]));
memcpy(g_RegMotionPlus[Slot], motionplus_register, sizeof(motionplus_register));
memcpy(g_RegMotionPlus[Slot] + 0x20, motion_plus_calibration, sizeof(motion_plus_calibration)); //reg 32bytes 0x20-3f;
g_MotionPlus[Slot] = 0;
//memcpy(g_RegMotionPlus[Slot] + 0xfa, motionplus_id, sizeof(motionplus_id));
}
else if(WiiMapping[Slot].iExtensionConnected == EXT_NUNCHUK)
{
memset(g_RegMotionPlus[Slot],0,sizeof(g_RegExt[0]));
memcpy(g_RegMotionPlus[Slot], motionplus_register, sizeof(motionplus_register));
memcpy(g_RegMotionPlus[Slot] + 0x20, motion_plus_calibration, sizeof(motion_plus_calibration)); //reg 32bytes 0x20-3f;
memcpy(g_RegExt[Slot] + 0x20, nunchuck_calibration, sizeof(nunchuck_calibration));
memcpy(g_RegExt[Slot] + 0x30, nunchuck_calibration, sizeof(nunchuck_calibration));
memcpy(g_RegExt[Slot] + 0xfa, nunchuck_id, sizeof(nunchuck_id));
g_MotionPlus[Slot] = 1;
}
g_MotionPlusReadError[Slot] = 0;
} else {
// Copy extension id and calibration to its register
if(WiiMapping[Slot].iExtensionConnected == EXT_NUNCHUK)
{
memcpy(g_RegExt[Slot] + 0x20, nunchuck_calibration, sizeof(nunchuck_calibration));
memcpy(g_RegExt[Slot] + 0x30, nunchuck_calibration, sizeof(nunchuck_calibration));
memcpy(g_RegExt[Slot] + 0xfa, nunchuck_id, sizeof(nunchuck_id));
}
else if(WiiMapping[Slot].iExtensionConnected == EXT_CLASSIC_CONTROLLER)
{
memcpy(g_RegExt[Slot] + 0x20, classic_calibration, sizeof(classic_calibration));
memcpy(g_RegExt[Slot] + 0x30, classic_calibration, sizeof(classic_calibration));
memcpy(g_RegExt[Slot] + 0xfa, classic_id, sizeof(classic_id));
}
else if(WiiMapping[Slot].iExtensionConnected == EXT_GUITARHERO)
{
// TODO get the correct values here
memcpy(g_RegExt[Slot] + 0x20, classic_calibration, sizeof(classic_calibration));
memcpy(g_RegExt[Slot] + 0x30, classic_calibration, sizeof(classic_calibration));
memcpy(g_RegExt[Slot] + 0xfa, gh3glp_id, sizeof(gh3glp_id));
}
else if(WiiMapping[Slot].iExtensionConnected == EXT_WBB)
{
// TODO
}
}
INFO_LOG(WIIMOTE, "UpdateExtRegisterBlocks()");
@ -546,6 +579,7 @@ void DoState(PointerWrap &p)
p.Do(g_IR[i]);
p.Do(g_Leds[i]);
p.Do(g_Speaker[i]);
p.Do(g_MotionPlus[i]);
//p.Do(g_SpeakerMute[i]);
p.Do(g_ExtKey[i]);
}

View File

@ -45,7 +45,6 @@ extern SWiimoteInitialize g_WiimoteInitialize;
namespace WiiMoteEmu
{
extern void PAD_Rumble(u8 _numPAD, unsigned int _uType);
/* Here we process the Output Reports that the Wii sends. Our response will be
@ -190,7 +189,7 @@ void WmReadData(u16 _channelID, wm_read_data* rd)
{
u32 address = convert24bit(rd->address);
u16 size = convert16bit(rd->size);
u8 addressHI = (address >> 16) & 0xFE;
INFO_LOG(WIIMOTE, "Read data");
DEBUG_LOG(WIIMOTE, " Read data Space: %x", rd->space);
DEBUG_LOG(WIIMOTE, " Read data Address: 0x%06x", address);
@ -205,7 +204,7 @@ void WmReadData(u16 _channelID, wm_read_data* rd)
PanicAlert("WmReadData: address + size out of bounds");
return;
}
SendReadDataReply(_channelID, g_Eeprom[g_ID] + address, address, (int)size);
SendReadDataReply(_channelID, g_Eeprom[g_ID] + address, address, addressHI, (int)size);
/*DEBUG_LOG(WIIMOTE, "Read RegEeprom: Size: %i, Address: %08x, Offset: %08x",
size, address, (address & 0xffff));*/
}
@ -213,7 +212,7 @@ void WmReadData(u16 _channelID, wm_read_data* rd)
{
u8* block;
u32 blockSize;
switch((address >> 16) & 0xFE)
switch(addressHI)
{
case 0xA2:
block = g_RegSpeaker[g_ID];
@ -230,10 +229,6 @@ void WmReadData(u16 _channelID, wm_read_data* rd)
// MotionPlus is pretty much just a dummy atm :p
case 0xA6:
block = g_RegMotionPlus[g_ID];
block[0xFC] = 0xA6;
block[0xFD] = 0x20;
block[0xFE] = 0x00;
block[0xFF] = 0x05;
blockSize = WIIMOTE_REG_EXT_SIZE;
DEBUG_LOG(WIIMOTE, " Case 0xa6: MotionPlusReg [%x]", address);
break;
@ -251,7 +246,7 @@ void WmReadData(u16 _channelID, wm_read_data* rd)
}
// Encrypt data that is read from the Wiimote Extension Register
if(((address >> 16) & 0xfe) == 0xa4)
if(addressHI == 0xa4)
{
// Check if encrypted reads is on
if(g_RegExt[g_ID][0xf0] == 0xaa)
@ -274,9 +269,13 @@ void WmReadData(u16 _channelID, wm_read_data* rd)
PanicAlert("WmReadData: address + size out of bounds! [%d %d %d]", address, size, blockSize);
return;
}
//3x read error due activated(or not present device, which is not the case), WII will await a status report after init and attempting to read from write-only area @A600FE/FF
if ((g_MotionPlusReadError[g_ID] == 2) && (g_RegExt[g_ID][0xFF]== 0x05)) { //if motionplus is active, its in the current ExtReg, 0xFF will be always 0x05
WmRequestStatus(_channelID, (wm_request_status*) rd, 1);
}
// Let this function process the message and send it to the Wii
SendReadDataReply(_channelID, block + address, address, (u8)size);
SendReadDataReply(_channelID, block + address, address, addressHI, (u8)size);
}
else
{
@ -295,7 +294,7 @@ void WmReadData(u16 _channelID, wm_read_data* rd)
_Address: The starting address inside the registry, this is used to check for out of bounds reading
_Size: The total size to send
*/
void SendReadDataReply(u16 _channelID, void* _Base, u16 _Address, int _Size)
void SendReadDataReply(u16 _channelID, void* _Base, u16 _Address, u8 _AddressHI, int _Size)
{
int dataOffset = 0;
const u8* data = (const u8*)_Base;
@ -340,6 +339,24 @@ void SendReadDataReply(u16 _channelID, void* _Base, u16 _Address, int _Size)
pReply->size = 0x0f;
pReply->error = 0x08;
}
if (WiiMapping[g_ID].bMotionPlusConnected)
{
//MP+ will try to read from this Registeraddress, expecting an error if a previous WM+ activation has been succesful
//it also returns an error if there was no WM+ present at all
if (((_Address == 0x00FE ) || (_Address == 0x00FF )) && (_AddressHI == 0xA6))
{
//Check if MP+ is activated, resp. if its in the current RegExt.
if ((g_RegExt[g_ID][0xFF] == 0x05) && (g_RegMotionPlus[g_ID][0xFF] != 0x05))
{
pReply->size = 0x0f;
pReply->error = 0x07; //error: write-only area when activated/or not present
// we use the read error at the same time as an indicator whether we need to send a faked 0x37 report or not
g_MotionPlusReadError[g_ID]++;
}
}
}
// Logging
DEBUG_LOG(WIIMOTE, "SendReadDataReply");
@ -369,12 +386,11 @@ void SendReadDataReply(u16 _channelID, void* _Base, u16 _Address, int _Size)
void WmWriteData(u16 _channelID, wm_write_data* wd)
{
u32 address = convert24bit(wd->address);
u8 addressHI = (address >> 16) & 0xFE;
INFO_LOG(WIIMOTE, "Write data");
DEBUG_LOG(WIIMOTE, " Space: %x", wd->space);
DEBUG_LOG(WIIMOTE, " Address: 0x%06x", address);
DEBUG_LOG(WIIMOTE, " Size: 0x%02x", wd->size);
// Write to EEPROM
if(wd->size <= 16 && wd->space == WM_SPACE_EEPROM)
{
@ -390,7 +406,7 @@ void WmWriteData(u16 _channelID, wm_write_data* wd)
{
u8* block;
u32 blockSize;
switch((address >> 16) & 0xFE)
switch(addressHI)
{
case 0xA2:
block = g_RegSpeaker[g_ID];
@ -422,7 +438,6 @@ void WmWriteData(u16 _channelID, wm_write_data* wd)
return;
}
// Remove for example 0xa40000 from the address
address &= 0xFFFF;
// Check if the address is within bounds
@ -430,7 +445,7 @@ void WmWriteData(u16 _channelID, wm_write_data* wd)
PanicAlert("WmWriteData: address + size out of bounds!");
return;
}
// Finally write the registers to the right structure
memcpy(block + address, wd->data, wd->size);
@ -442,7 +457,10 @@ void WmWriteData(u16 _channelID, wm_write_data* wd)
if(address >= 0x40 && address <= 0x4c)
wiimote_gen_key(&g_ExtKey[g_ID], &g_RegExt[g_ID][0x40]);
}
if (WiiMapping[g_ID].bMotionPlusConnected) {
HandlingMotionPlusWrites(wd->data, addressHI, address);
}
}
else
{
@ -487,7 +505,7 @@ void WmRequestStatus(u16 _channelID, wm_request_status* rs, int Extension)
if (Extension == -1)
{
// Read config value for the first time
pStatus->extension = (WiiMapping[g_ID].iExtensionConnected == EXT_NONE) ? 0 : 1;
pStatus->extension = ((g_MotionPlus[g_ID]) || (WiiMapping[g_ID].iExtensionConnected != EXT_NONE)) ? 1 : 0;
}
else
{
@ -501,10 +519,121 @@ void WmRequestStatus(u16 _channelID, wm_request_status* rs, int Extension)
DEBUG_LOG(WIIMOTE, " IR: %x", pStatus->ir);
DEBUG_LOG(WIIMOTE, " LEDs: %x", pStatus->leds);
g_WiimoteInitialize.pWiimoteInput(g_ID, _channelID, DataFrame, Offset);
// Debugging
//ReadDebugging(true, DataFrame, Offset);
}
void HandlingMotionPlusWrites(u8* data, u8 addressHI, u32 address){
switch (addressHI)
{
case 0xA4:
switch (address)
{
case 0x00FE:
if (data[0] == 0x00) {
if ((g_RegExt[g_ID][0xFF] == 0x05) && (g_RegMotionPlus[g_ID][0xFF] != 0x05))
{
SwapExtRegisters();
DEBUG_LOG(WIIMOTE, "Writing [0x%02x] to [0x%02x:%04x]: Disabling WM+ and swapping registers back", data[0], addressHI, address);
}
else {
DEBUG_LOG(WIIMOTE, "Writing [0x%02x] to [0x%02x:%04x]: WM+ already inactive", data[0], addressHI, address);
}
g_MotionPlus[g_ID] = 1;
}
break;
case 0x00FB:
//1. Initializing the extension: writing 0x55 ->0xA400F0 and then 0x00 to 0xA400FB.
//2. Disables an active wiimote; ext disconnect.
if (data[0] == 0x00) {
//1. connecting extension
if ((g_RegExt[g_ID][0xFF] != 0x05) && (g_RegMotionPlus[g_ID][0xFF] == 0x05))
{
g_RegMotionPlus[g_ID][0xFE] = 0x04;
g_RegMotionPlus[g_ID][0xF7] = 0x08; //control init byte, ingame check
SwapExtRegisters();
DEBUG_LOG(WIIMOTE, "Writing [0x%02x] to [0x%02x:%04x]: Enabling WM+ and swapping rgisters", data[0], addressHI, address);
g_MotionPlus[g_ID] = 0;
} //2. disconnecting extension
else if ((g_RegExt[g_ID][0xFF] == 0x05) && (g_RegMotionPlus[g_ID][0xFF] != 0x05)){
g_RegExt[g_ID][0xFE] = 0x04;
g_MotionPlus[g_ID] = 1;
DEBUG_LOG(WIIMOTE, "Writing [0x%02x] to [0x%02x:%04x]: Disabling WM+ and swapping registers back", data[0], addressHI, address);
}
}
break;
}
break;
case 0xA6:
switch (address)
{
case 0x00FE:
//Enabling WM+, swapping extension registers
if (data[0] == 0x05) {
if ((g_RegExt[g_ID][0xFF] != 0x05) && (g_RegMotionPlus[g_ID][0xFF] == 0x05) ) {
//The WII will try to read from the readprotected A6 WM+ register now, we need to reply with an error each time,
//plus sent an statusreport 0x20(depending on if nunchuk inserted or not)
g_MotionPlusReadError[g_ID] = 0;
g_MotionPlus[g_ID] = 1;
SwapExtRegisters();
DEBUG_LOG(WIIMOTE, "Writing [0x%02x] to [0x%02x:%04x]: Enabling WM+ and swapping rgisters back", data[0], addressHI, address);
}
else {
DEBUG_LOG(WIIMOTE, "Writing [0x%02x] to [0x%02x:%04x]: WM already enabled no register swapping", data[0], addressHI, address);
}
}
break;
case 0x00F0: //init() WM+, this will change some control bits in the WM+ register
if (data[0] == 0x55) { //enables passthroug, convert to 0405* A6 swap
if ((g_RegMotionPlus[g_ID][0xFF] == 0x05))
{
//control init byte, ingame check
g_RegMotionPlus[g_ID][0xF7] = 0x08;
//motion plus id
g_RegMotionPlus[g_ID][0xFE] = 0x05;
//we will swap the register on write to 0x00FE
}
else if (g_RegExt[g_ID][0xFF] == 0x05) { //if the wiimote is already active, we will init() the WM+ directly in the ExtReg
g_RegExt[g_ID][0xFE] = 0x05;
g_RegExt[g_ID][0xF7] = 0x08;
}
g_MotionPlus[g_ID] = 0;
}
break;
default:
DEBUG_LOG(WIIMOTE, "Writing [0x%02x] to [0x%02x:%04x]: unknown reason", data[0], addressHI, address);
break;
}
break;
}
}
//Swapping Ext/WM+-registers
void SwapExtRegisters(){
memset(g_RegExtTmp, 0, sizeof(g_RegExtTmp));
memcpy(g_RegExtTmp, g_RegExt[g_ID], sizeof(g_RegExt[0]));
memset(g_RegExt[0], 0, sizeof(g_RegExt[0]));
memcpy(g_RegExt[g_ID], g_RegMotionPlus[g_ID], sizeof(g_RegMotionPlus[0]));
memset(g_RegMotionPlus[0], 0, sizeof(g_RegMotionPlus[0]));
memcpy(g_RegMotionPlus[g_ID], g_RegExtTmp, sizeof(g_RegExtTmp));
if (g_RegMotionPlus[g_ID][0xFC]) {
g_RegMotionPlus[g_ID][0xFC] = 0xa6;
}
if (g_RegExt[g_ID][0xFC]) {
g_RegExt[g_ID][0xFC] = 0xa4;
}
}
} // WiiMoteEmu

View File

@ -51,7 +51,9 @@ void SendReportCoreAccelIr10Ext(u16 _channelID);
int WriteWmReportHdr(u8* dst, u8 wm);
void WmSendAck(u16 _channelID, u8 _reportID);
void SendReadDataReply(u16 _channelID, void* _Base, u16 _Address, int _Size);
void SendReadDataReply(u16 _channelID, void* _Base, u16 _Address, u8 _AddressHI, int _Size);
void SwapExtRegisters();
void HandlingMotionPlusWrites(u8* data, u8 addressHI, u32 address);
void FillReportAcc(wm_accel& _acc);
void FillReportInfo(wm_core& _core);
@ -60,7 +62,9 @@ void FillReportIRBasic(wm_ir_basic& _ir0, wm_ir_basic& _ir1);
void FillReportExtension(wm_extension& _ext);
void FillReportClassicExtension(wm_classic_extension& _ext);
void FillReportGuitarHero3Extension(wm_GH3_extension& _ext);
void FillReportMotionPlusNunchukExtension(wm_extension& _ext);
void FillReportMotionPlusNoExtension(wm_extension& _ext);
void FillReportMotionPlus(wm_extension& ext, bool extension);
} // namespace
#endif //_EMU_DECLARATIONS_

View File

@ -710,7 +710,8 @@ void FillReportIRBasic(wm_ir_basic& _ir0, wm_ir_basic& _ir1)
);*/
// ------------------
}
/* Generate the 6 byte extension report for the motionplus&nunchuk, encrypted. The bytes
void FillReportExtension(wm_extension& _ext)
/* Generate the 6 byte extension report for the Nunchuck, encrypted. The bytes
are JX JY AX AY AZ BT. */
@ -1247,4 +1248,201 @@ void FillReportGuitarHero3Extension(wm_GH3_extension& _ext)
memcpy(&_ext, Tmp, sizeof(_ext));
}
/* Generate the 6 byte extension report for the MotionPlus Controller.
pass-through mode supported for MotionPlus+Nunchuk */
void FillReportMotionPlus(wm_extension& ext, bool extension){
//sending initial control packet, this must be sent first, its some kind of verifiation, all control bits are set to 0!
if (g_MotionPlusReadError[g_ID]) {
memcpy(&ext, motionpluscheck_id, sizeof(motionpluscheck_id));
g_MotionPlus[g_ID] = (extension) ? 1 : 0;
g_MotionPlusReadError[g_ID] = 0;
} //nunchuk inserted
else if (extension == 1) {
switch (g_InterleavedData[g_ID])
{
case false://MPlus
FillReportMotionPlusNoExtension(ext);
break;
case true: //Nunchuk
FillReportMotionPlusNunchukExtension(ext);
break;
}
//alternate between nunchuk and wm+ interleaved reports
g_InterleavedData[g_ID] = g_InterleavedData[g_ID] ? false : true;
}//no additional extension inserted, no interleaving, always sending mp+ data
else if (extension == 0) {
FillReportMotionPlusNoExtension(ext);
g_InterleavedData[g_ID] = false;
}
}
void FillReportMotionPlusNoExtension(wm_extension& _ext)
{
wm_mp_nc_0 ext;
memset(&ext, 0, sizeof(wm_mp_nc_0));
ext.YawLeftLS = 0x00; //dummy data atm
ext.RollLeftLS = 0x00;
ext.PitchDownLS = 0x00;
ext.pitchfast = 0x00;
ext.yawfast = 0x00;
ext.YawLeftHI = 0x00;
ext.ExtCon = 0x00; // 0 (important, since we don't use a nunchuk here)
ext.rollfast = 0x00;
ext.RollLeftHI = 0x00;
ext.dummy = 0x00; // 0 (important)
ext.mpdata = 0x01; //1 (important, using MP+ report instead of NC report)
ext.PitchDownHI = 0x00;
memcpy(&_ext, &ext, sizeof(ext));
}
void FillReportMotionPlusNunchukExtension(wm_extension& _ext)
{
wm_mp_nc_1 ext;
memset(&ext, 0, sizeof(wm_mp_nc_1));
ext.jx = g_nu.jx.center;
ext.jy = g_nu.jy.center;
// Use the neutral values
ext.ax = g_nu.cal_zero.x;
ext.ay = g_nu.cal_zero.y;
ext.az = g_nu.cal_zero.z + g_nu.cal_g.z;
ext.axLS = 0x00;
ext.ayLS = 0x00;
ext.azLS = 0x00;
ext.bz = 0x01;
ext.bc = 0x01;
ext.dummy = 0; //0 (important)
ext.mpdata = 0; //0 NC report, interleaved data (important)
ext.ExtCon = 1; // must be 1 when in NC-MP+ Mode
if (IsFocus())
{
int acc_x = g_nu.cal_zero.x;
int acc_y = g_nu.cal_zero.y;
int acc_z = g_nu.cal_zero.z + g_nu.cal_g.z;
if (IsKey(ENC_SHAKE) && !WiiMapping[g_ID].Motion.TiltNC.Shake)
WiiMapping[g_ID].Motion.TiltNC.Shake = 1;
// Step the shake simulation one step
ShakeToAccelerometer(acc_x, acc_y, acc_z, WiiMapping[g_ID].Motion.TiltNC);
// Tilt Nunchuck, allow the shake function to interrupt it
if (!WiiMapping[g_ID].Motion.TiltNC.Shake)
TiltNunchuck(acc_x, acc_y, acc_z);
// Boundary check
if (acc_x > 0xFF) acc_x = 0xFF;
else if (acc_x < 0x00) acc_x = 0x00;
if (acc_y > 0xFF) acc_y = 0xFF;
else if (acc_y < 0x00) acc_y = 0x00;
if (acc_z > 0xFF) acc_z = 0xFF;
else if (acc_z < 0x00) acc_z = 0x00;
ext.ax = acc_x;
ext.ay = acc_y;
ext.az = acc_z>>1;
ext.azLS = acc_z<<1; //LS0=0
// Update the analog stick
if (WiiMapping[g_ID].Stick.NC == FROM_KEYBOARD)
{
// Set the max values to the current calibration values
if(IsKey(ENC_L)) // x
ext.jx = g_nu.jx.min;
if(IsKey(ENC_R))
ext.jx = g_nu.jx.max;
if(IsKey(ENC_D)) // y
ext.jy = g_nu.jy.min;
if(IsKey(ENC_U))
ext.jy = g_nu.jy.max;
// On a real stick, the initialization value of center is 0x80,
// but after a first time touch, the center value automatically changes to 0x7F
if(ext.jx != g_nu.jx.center)
g_nu.jx.center = 0x7F;
if(ext.jy != g_nu.jy.center)
g_nu.jy.center = 0x7F;
}
else
{
// Get adjusted pad state values
int _Lx = WiiMapping[g_ID].AxisState.Lx;
int _Ly = WiiMapping[g_ID].AxisState.Ly;
int _Rx = WiiMapping[g_ID].AxisState.Rx;
int _Ry = WiiMapping[g_ID].AxisState.Ry;
// The Y-axis is inverted
_Ly = 0xff - _Ly;
_Ry = 0xff - _Ry;
/* This is if we are also using a real Nunchuck that we are sharing the
calibration with. It's not needed if we are using our default
values. We adjust the values to the configured range, we even allow
the center to not be 0x80. */
if(g_nu.jx.max != 0xff || g_nu.jy.max != 0xff
|| g_nu.jx.min != 0 || g_nu.jy.min != 0
|| g_nu.jx.center != 0x80 || g_nu.jy.center != 0x80)
{
float Lx = (float)_Lx;
float Ly = (float)_Ly;
float Rx = (float)_Rx;
float Ry = (float)_Ry;
//float Tl = (float)_Tl;
//float Tr = (float)_Tr;
float XRangePos = (float) (g_nu.jx.max - g_nu.jx.center);
float XRangeNeg = (float) (g_nu.jx.center - g_nu.jx.min);
float YRangePos = (float) (g_nu.jy.max - g_nu.jy.center);
float YRangeNeg = (float) (g_nu.jy.center - g_nu.jy.min);
if (Lx > 0x80) Lx = Lx * (XRangePos / 128.0);
if (Lx < 0x80) Lx = Lx * (XRangeNeg / 128.0);
if (Lx == 0x80) Lx = (float)g_nu.jx.center;
if (Ly > 0x80) Ly = Ly * (YRangePos / 128.0);
if (Ly < 0x80) Ly = Ly * (YRangeNeg / 128.0);
if (Ly == 0x80) Lx = (float)g_nu.jy.center;
// Boundaries
_Lx = (int)Lx;
_Ly = (int)Ly;
_Rx = (int)Rx;
_Ry = (int)Ry;
if (_Lx > 0xff) _Lx = 0xff; if (_Lx < 0) _Lx = 0;
if (_Rx > 0xff) _Rx = 0xff; if (_Rx < 0) _Rx = 0;
if (_Ly > 0xff) _Ly = 0xff; if (_Ly < 0) _Ly = 0;
if (_Ry > 0xff) _Ry = 0xff; if (_Ry < 0) _Ry = 0;
}
if (WiiMapping[g_ID].Stick.NC == FROM_ANALOG1)
{
ext.jx = _Lx;
ext.jy = _Ly;
}
else // ANALOG2
{
ext.jx = _Rx;
ext.jy = _Ry;
}
}
if(IsKey(ENC_C)) ext.bc = 0x00; ///////////////
if(IsKey(ENC_Z)) ext.bz = 0x00;
}
memcpy(&_ext, &ext, sizeof(ext));
}
} // end of namespace

View File

@ -138,6 +138,39 @@ struct wm_classic_extension
wm_cc_5 b2; // byte 5
};
struct wm_mp_nc_0 //motionplus+nunchuk_pass-through
{
u8 YawLeftLS; //7e
u8 RollLeftLS; //82
u8 PitchDownLS; //83
u8 pitchfast : 1; //1
u8 yawfast : 1; //0
u8 YawLeftHI : 6; //01 1010 /1a
u8 ExtCon : 1; // 1 usually
u8 rollfast : 1; //0
u8 RollLeftHI : 6; //00 1010
u8 dummy : 1; // 0 usually. 1 in dem fall mhh
u8 mpdata : 1; //1 in this case, interleaved motion+ data
u8 PitchDownHI : 6;//01 1100
}; // default for yaw/roll/pitch around 0x1F7F
struct wm_mp_nc_1 //motionplus+nunchuk_pass-through
{
u8 jx;
u8 jy;
u8 ax;
u8 ay;
u8 ExtCon : 1; // 1 usually
u8 az : 7;
u8 dummy : 1; //0 always
u8 mpdata : 1; //0 when nunchuk interleaved data
u8 bz : 1;
u8 bc : 1;
u8 axLS : 1; // ls 1, ls0 = 0 by default,
u8 ayLS : 1;
u8 azLS : 2;
};
struct wm_GH3_extension
{
u8 SX : 6;