dolphin/Source/Plugins/Plugin_Wiimote/Src/main.cpp

1055 lines
32 KiB
C++
Raw Blame History

// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Current issues
/*
The real Wiimote fails to answer the core correctly sometmes. Leading to an
unwanted disconnection. And there is currenty no functions to reconnect with
the game. There are two ways to solve this:
1. Make a reconnect function in the IOS emulation
2. Detect failed answers in this plugin and solve it by replacing them with
emulated answers.
The first solution seems easier, if I knew a little better how the /dev/usb/oh1
and Wiimote functions worked.
*/
#include "Common.h" // Common
#include "LogManager.h"
#include "StringUtil.h"
#include "Timer.h"
#define EXCLUDEMAIN_H // Avoid certain declarations in main.h
#include "EmuDefinitions.h" // Local
#include "wiimote_hid.h"
#include "main.h"
#if defined(HAVE_WX) && HAVE_WX
#include "ConfigPadDlg.h"
#include "ConfigRecordingDlg.h"
#include "ConfigBasicDlg.h"
WiimotePadConfigDialog *m_PadConfigFrame = NULL;
WiimoteRecordingConfigDialog *m_RecordingConfigFrame = NULL;
WiimoteBasicConfigDialog *m_BasicConfigFrame = NULL;
#endif
#include "Config.h"
#include "pluginspecs_wiimote.h"
#include "EmuMain.h"
#if HAVE_WIIUSE
#include "wiimote_real.h"
#endif
#if defined(HAVE_X11) && HAVE_X11
Display* WMdisplay;
#endif
SWiimoteInitialize g_WiimoteInitialize;
PLUGIN_GLOBALS* globals = NULL;
// General
bool g_EmulatorRunning = false;
u32 g_ISOId = 0;
bool g_FrameOpen = false;
bool g_RealWiiMotePresent = false;
bool g_RealWiiMoteInitialized = false;
bool g_EmulatedWiiMoteInitialized = false;
bool g_WiimoteUnexpectedDisconnect = false;
// Settings
accel_cal g_wm;
nu_cal g_nu;
cc_cal g_ClassicContCalibration;
gh3_cal g_GH3Calibration;
// Debugging
bool g_DebugAccelerometer = false;
bool g_DebugData = false;
bool g_DebugComm = true;
bool g_DebugSoundData = true;
bool g_DebugCustom = false;
// Update speed
int g_UpdateCounter = 0;
double g_UpdateTime = 0;
int g_UpdateRate = 0;
int g_UpdateWriteScreen = 0;
std::vector<int> g_UpdateTimeList (5, 0);
// Movement recording
std::vector<SRecordingAll> VRecording(RECORDING_ROWS);
// Standard crap to make wxWidgets happy
#ifdef _WIN32
HINSTANCE g_hInstance;
#if defined(HAVE_WX) && HAVE_WX
class wxDLLApp : public wxApp
{
bool OnInit()
{
return true;
}
};
IMPLEMENT_APP_NO_MAIN(wxDLLApp)
WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst);
#endif
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, // DLL module handle
DWORD dwReason, // reason called
LPVOID lpvReserved) // reserved
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
{
#if defined(HAVE_WX) && HAVE_WX
wxSetInstance((HINSTANCE)hinstDLL);
int argc = 0;
char **argv = NULL;
wxEntryStart(argc, argv);
if (!wxTheApp || !wxTheApp->CallOnInit())
return FALSE;
#endif
}
break;
case DLL_PROCESS_DETACH:
#if defined(HAVE_WX) && HAVE_WX
wxEntryCleanup();
#endif
break;
default:
break;
}
g_hInstance = hinstDLL;
return TRUE;
}
#endif
#if defined(HAVE_WX) && HAVE_WX
wxWindow* GetParentedWxWindow(HWND Parent)
{
#ifdef _WIN32
wxSetInstance((HINSTANCE)g_hInstance);
#endif
wxWindow *win = new wxWindow();
#ifdef _WIN32
win->SetHWND((WXHWND)Parent);
win->AdoptAttributesFromHWND();
#endif
return win;
}
#endif
// Exports
void GetDllInfo(PLUGIN_INFO* _PluginInfo)
{
_PluginInfo->Version = 0x0100;
_PluginInfo->Type = PLUGIN_TYPE_WIIMOTE;
#ifdef DEBUGFAST
sprintf(_PluginInfo->Name, "Dolphin Wiimote Plugin (DebugFast)");
#else
#ifndef _DEBUG
sprintf(_PluginInfo->Name, "Dolphin Wiimote Plugin");
#else
sprintf(_PluginInfo->Name, "Dolphin Wiimote Plugin (Debug)");
#endif
#endif
}
void SetDllGlobals(PLUGIN_GLOBALS* _pPluginGlobals)
{
globals = _pPluginGlobals;
LogManager::SetInstance((LogManager *)globals->logManager);
}
void DllDebugger(HWND _hParent, bool Show) {}
void DllConfig(HWND _hParent)
{
#if defined(HAVE_WX) && HAVE_WX
DoInitialize();
if (!m_BasicConfigFrame)
m_BasicConfigFrame = new WiimoteBasicConfigDialog(GetParentedWxWindow(_hParent));
else if (!m_BasicConfigFrame->GetParent()->IsShown())
m_BasicConfigFrame->Close(true);
// Only allow one open at a time
if (!m_BasicConfigFrame->IsShown())
m_BasicConfigFrame->ShowModal();
else
m_BasicConfigFrame->Hide();
#endif
}
void Initialize(void *init)
{
// Declarations
SWiimoteInitialize _WiimoteInitialize = *(SWiimoteInitialize *)init;
g_WiimoteInitialize = _WiimoteInitialize;
g_EmulatorRunning = true;
// Update the GUI if the configuration window is already open
#if defined(HAVE_WX) && HAVE_WX
if (g_FrameOpen)
{
// Save the settings
g_Config.Save();
// Save the ISO Id
g_ISOId = g_WiimoteInitialize.ISOId;
// Load the settings
g_Config.Load();
if(m_BasicConfigFrame) m_BasicConfigFrame->UpdateGUI();
}
#endif
#if defined(HAVE_X11) && HAVE_X11
WMdisplay = (Display*)_WiimoteInitialize.hWnd;
#endif
// Save the ISO Id, again if we had a window open
g_ISOId = g_WiimoteInitialize.ISOId;
DoInitialize();
INFO_LOG(CONSOLE, "ISOId: %08x %s\n", g_WiimoteInitialize.ISOId, Hex2Ascii(g_WiimoteInitialize.ISOId).c_str());
}
// If a game is not running this is called by the Configuration window when it's closed
void Shutdown(void)
{
// Not running
g_EmulatorRunning = false;
// Reset the game ID in all cases
g_ISOId = 0;
// We will only shutdown when both a game and the m_ConfigFrame is closed
if (g_FrameOpen)
{
#if defined(HAVE_WX) && HAVE_WX
if(m_BasicConfigFrame) m_BasicConfigFrame->UpdateGUI();
#endif
// Reset the variables
WiiMoteEmu::ResetVariables();
/* Don't shut down the wiimote when we still have the config window open, we may still want
want to use the Wiimote in the config window. */
return;
}
#if HAVE_WIIUSE
if (g_RealWiiMoteInitialized) WiiMoteReal::Shutdown();
#endif
WiiMoteEmu::Shutdown();
}
void DoState(unsigned char **ptr, int mode)
{
PointerWrap p(ptr, mode);
return;
// TODO: Shorten the list
//p.Do(g_EmulatorRunning);
//p.Do(g_ISOId);
p.Do(g_FrameOpen);
p.Do(g_RealWiiMotePresent);
p.Do(g_RealWiiMoteInitialized);
p.Do(g_EmulatedWiiMoteInitialized);
p.Do(g_WiimoteUnexpectedDisconnect);
p.Do(g_UpdateCounter);
p.Do(g_UpdateTime);
p.Do(g_UpdateRate);
p.Do(g_UpdateWriteScreen);
p.Do(g_UpdateTimeList);
#if HAVE_WIIUSE
WiiMoteReal::DoState(p);
#endif
WiiMoteEmu::DoState(p);
}
/* This function produce Wiimote Input (reports from the Wiimote) in response
to Output from the Wii. It's called from WII_IPC_HLE_WiiMote.cpp.
Switch between real and emulated wiimote: We send all this Input to WiiMoteEmu::InterruptChannel()
so that it knows the channel ID and the data reporting mode at all times.
*/
void Wiimote_InterruptChannel(u16 _channelID, const void* _pData, u32 _Size)
{
const u8* data = (const u8*)_pData;
// Debugging
{
DEBUG_LOG(WII_IPC_WIIMOTE, "Wiimote_Input");
DEBUG_LOG(WII_IPC_WIIMOTE, " Channel ID: %04x", _channelID);
std::string Temp = ArrayToString(data, _Size);
DEBUG_LOG(WII_IPC_WIIMOTE, " Data: %s", Temp.c_str());
}
// Decice where to send the message
//if (!g_RealWiiMotePresent)
WiiMoteEmu::InterruptChannel(_channelID, _pData, _Size);
#if HAVE_WIIUSE
if (g_RealWiiMotePresent)
WiiMoteReal::InterruptChannel(_channelID, _pData, _Size);
#endif
}
// Function: Used for the initial Bluetooth HID handshake.
void Wiimote_ControlChannel(u16 _channelID, const void* _pData, u32 _Size)
{
const u8* data = (const u8*)_pData;
// Check for custom communication
if(_channelID == 99 && data[0] == WIIMOTE_RECONNECT)
{
INFO_LOG(CONSOLE, "\n\nWiimote Disconnected\n\n");
g_EmulatorRunning = false;
g_WiimoteUnexpectedDisconnect = true;
#if defined(HAVE_WX) && HAVE_WX
if (m_BasicConfigFrame) m_BasicConfigFrame->UpdateGUI();
#endif
return;
}
// Debugging
{
DEBUG_LOG(WII_IPC_WIIMOTE, "Wiimote_ControlChannel");
std::string Temp = ArrayToString(data, _Size);
DEBUG_LOG(WII_IPC_WIIMOTE, " Data: %s", Temp.c_str());
//PanicAlert("Wiimote_ControlChannel");
}
//if (!g_RealWiiMotePresent)
WiiMoteEmu::ControlChannel(_channelID, _pData, _Size);
#if HAVE_WIIUSE
if (g_RealWiiMotePresent)
WiiMoteReal::ControlChannel(_channelID, _pData, _Size);
#endif
}
/* This sends a Data Report from the Wiimote. See SystemTimers.cpp for the documentation of this
update. */
void Wiimote_Update()
{
// Tell us about the update rate, but only about once every second to avoid a major slowdown
#if defined(HAVE_WX) && HAVE_WX
if (m_RecordingConfigFrame)
{
GetUpdateRate();
if (g_UpdateWriteScreen > g_UpdateRate)
{
m_RecordingConfigFrame->m_TextUpdateRate->SetLabel(wxString::Format(wxT("Update rate: %03i times/s"), g_UpdateRate));
g_UpdateWriteScreen = 0;
}
g_UpdateWriteScreen++;
}
#endif
// This functions will send:
// Emulated Wiimote: Only data reports 0x30-0x37
// Real Wiimote: Both data reports 0x30-0x37 and all other read reports
if (!g_Config.bUseRealWiimote || !g_RealWiiMotePresent)
WiiMoteEmu::Update();
#if HAVE_WIIUSE
else if (g_RealWiiMotePresent)
WiiMoteReal::Update();
#endif
// Debugging
#ifdef _WIN32
if( GetAsyncKeyState(VK_HOME) && g_DebugComm ) g_DebugComm = false; // Page Down
else if (GetAsyncKeyState(VK_HOME) && !g_DebugComm ) g_DebugComm = true;
if( GetAsyncKeyState(VK_PRIOR) && g_DebugData ) g_DebugData = false; // Page Up
else if (GetAsyncKeyState(VK_PRIOR) && !g_DebugData ) g_DebugData = true;
if( GetAsyncKeyState(VK_NEXT) && g_DebugAccelerometer ) g_DebugAccelerometer = false; // Home
else if (GetAsyncKeyState(VK_NEXT) && !g_DebugAccelerometer ) g_DebugAccelerometer = true;
if( GetAsyncKeyState(VK_END) && g_DebugCustom ) { g_DebugCustom = false; INFO_LOG(CONSOLE, "Custom Debug: Off\n");} // End
else if (GetAsyncKeyState(VK_END) && !g_DebugCustom ) {g_DebugCustom = true; INFO_LOG(CONSOLE, "Custom Debug: Off\n");}
#endif
}
unsigned int Wiimote_GetAttachedControllers()
{
return 1;
}
// Supporting functions
// Check if Dolphin is in focus
bool IsFocus()
{
#ifdef _WIN32
HWND RenderingWindow = g_WiimoteInitialize.hWnd;
HWND Parent = GetParent(RenderingWindow);
HWND TopLevel = GetParent(Parent);
// Allow updates when the config window is in focus to
HWND Config = NULL;
if (m_BasicConfigFrame)
Config = (HWND)m_BasicConfigFrame->GetHWND();
// Support both rendering to main window and not
if (GetForegroundWindow() == TopLevel || GetForegroundWindow() == RenderingWindow || GetForegroundWindow() == Config)
return true;
else
return false;
#else
return true;
#endif
}
// Turn off all extensions
void DisableExtensions()
{
g_Config.iExtensionConnected = EXT_NONE;
}
void ReadDebugging(bool Emu, const void* _pData, int Size)
{
//
//const u8* data = (const u8*)_pData;
//u8* data = (u8*)_pData;
// Copy the data to a new location that we know are the right size
u8 data[32];
memset(data, 0, sizeof(data));
memcpy(data, _pData, Size);
int size;
bool DataReport = false;
std::string Name, TmpData;
switch(data[1])
{
case WM_STATUS_REPORT: // 0x20
size = sizeof(wm_status_report);
Name = "WM_STATUS_REPORT";
{
wm_status_report* pStatus = (wm_status_report*)(data + 2);
INFO_LOG(CONSOLE, "\n"
"Extension Controller: %i\n"
//"Speaker enabled: %i\n"
//"IR camera enabled: %i\n"
//"LED 1: %i\n"
//"LED 2: %i\n"
//"LED 3: %i\n"
//"LED 4: %i\n"
"Battery low: %i\n\n",
pStatus->extension,
//pStatus->speaker,
//pStatus->ir,
//(pStatus->leds >> 0),
//(pStatus->leds >> 1),
//(pStatus->leds >> 2),
//(pStatus->leds >> 3),
pStatus->battery_low
);
/* Update the global (for both the real and emulated) extension settings from whatever
the real Wiimote use. We will enable the extension from the 0x21 report. */
if(!Emu && !pStatus->extension)
{
DisableExtensions();
#if defined(HAVE_WX) && HAVE_WX
if (m_BasicConfigFrame) m_BasicConfigFrame->UpdateGUI();
#endif
}
}
break;
case WM_READ_DATA_REPLY: // 0x21
size = sizeof(wm_read_data_reply);
Name = "REPLY";
// data[4]: Size and error
// data[5, 6]: The registry offset
// Show the extension ID
if ((data[4] == 0x10 || data[4] == 0x20 || data[4] == 0x50) && data[5] == 0x00 && (data[6] == 0xfa || data[6] == 0xfe))
{
if(data[4] == 0x10)
TmpData.append(StringFromFormat("Game got the encrypted extension ID: %02x%02x\n", data[7], data[8]));
else if(data[4] == 0x50)
TmpData.append(StringFromFormat("Game got the encrypted extension ID: %02x%02x%02x%02x%02x%02x\n", data[7], data[8], data[9], data[10], data[11], data[12]));
// We have already sent the data report so we can safely decrypt it now
if(WiiMoteEmu::g_Encryption)
{
if(data[4] == 0x10)
wiimote_decrypt(&WiiMoteEmu::g_ExtKey, &data[0x07], 0x06, (data[4] >> 0x04) + 1);
if(data[4] == 0x50)
wiimote_decrypt(&WiiMoteEmu::g_ExtKey, &data[0x07], 0x02, (data[4] >> 0x04) + 1);
}
/* Update the global extension settings. Enable the emulated extension from reading
what the real Wiimote has connected. To keep the emulated and real Wiimote in sync. */
if(data[4] == 0x10)
{
if (!Emu) DisableExtensions();
if (!Emu && data[7] == 0x00 && data[8] == 0x00)
g_Config.iExtensionConnected = EXT_NUNCHUCK;
if (!Emu && data[7] == 0x01 && data[8] == 0x01)
g_Config.iExtensionConnected = EXT_CLASSIC_CONTROLLER;
g_Config.Save();
WiiMoteEmu::UpdateEeprom();
#if defined(HAVE_WX) && HAVE_WX
if (m_BasicConfigFrame) m_BasicConfigFrame->UpdateGUI();
#endif
INFO_LOG(CONSOLE, "%s", TmpData.c_str());
INFO_LOG(CONSOLE, "Game got the decrypted extension ID: %02x%02x\n\n", data[7], data[8]);
}
else if(data[4] == 0x50)
{
if (!Emu) DisableExtensions();
if (!Emu && data[11] == 0x00 && data[12] == 0x00)
g_Config.iExtensionConnected = EXT_NUNCHUCK;
if (!Emu && data[11] == 0x01 && data[12] == 0x01)
g_Config.iExtensionConnected = EXT_CLASSIC_CONTROLLER;
g_Config.Save();
WiiMoteEmu::UpdateEeprom();
#if defined(HAVE_WX) && HAVE_WX
if (m_BasicConfigFrame) m_BasicConfigFrame->UpdateGUI();
#endif
INFO_LOG(CONSOLE, "%s", TmpData.c_str());
INFO_LOG(CONSOLE, "Game got the decrypted extension ID: %02x%02x%02x%02x%02x%02x\n\n", data[7], data[8], data[9], data[10], data[11], data[12]);
}
}
// Show the Wiimote neutral values
/* The only difference between the Nunchuck and Wiimote that we go
after is calibration here is the offset in memory. If needed we can
check the preceding 0x17 request to. */
if(data[4] == 0xf0 && data[5] == 0x00 && data[6] == 0x10)
{
if(data[6] == 0x10)
{
INFO_LOG(CONSOLE, "\nGame got the Wiimote calibration:\n");
INFO_LOG(CONSOLE, "Cal_zero.x: %i\n", data[7 + 6]);
INFO_LOG(CONSOLE, "Cal_zero.y: %i\n", data[7 + 7]);
INFO_LOG(CONSOLE, "Cal_zero.z: %i\n", data[7 + 8]);
INFO_LOG(CONSOLE, "Cal_g.x: %i\n", data[7 + 10]);
INFO_LOG(CONSOLE, "Cal_g.y: %i\n", data[7 + 11]);
INFO_LOG(CONSOLE, "Cal_g.z: %i\n", data[7 +12]);
}
}
// Show the Nunchuck neutral values
if(data[4] == 0xf0 && data[5] == 0x00 && (data[6] == 0x20 || data[6] == 0x30))
{
// Save the encrypted data
TmpData = StringFromFormat("Read[%s] (enc): %s\n", (Emu ? "Emu" : "Real"), ArrayToString(data, size + 2, 0, 30).c_str());
// We have already sent the data report so we can safely decrypt it now
if(WiiMoteEmu::g_Encryption)
wiimote_decrypt(&WiiMoteEmu::g_ExtKey, &data[0x07], 0x00, (data[4] >> 0x04) + 1);
if (g_Config.iExtensionConnected == EXT_NUNCHUCK)
{
INFO_LOG(CONSOLE, "\nGame got the Nunchuck calibration:\n");
INFO_LOG(CONSOLE, "Cal_zero.x: %i\n", data[7 + 0]);
INFO_LOG(CONSOLE, "Cal_zero.y: %i\n", data[7 + 1]);
INFO_LOG(CONSOLE, "Cal_zero.z: %i\n", data[7 + 2]);
INFO_LOG(CONSOLE, "Cal_g.x: %i\n", data[7 + 4]);
INFO_LOG(CONSOLE, "Cal_g.y: %i\n", data[7 + 5]);
INFO_LOG(CONSOLE, "Cal_g.z: %i\n", data[7 + 6]);
INFO_LOG(CONSOLE, "Js.Max.x: %i\n", data[7 + 8]);
INFO_LOG(CONSOLE, "Js.Min.x: %i\n", data[7 + 9]);
INFO_LOG(CONSOLE, "Js.Center.x: %i\n", data[7 + 10]);
INFO_LOG(CONSOLE, "Js.Max.y: %i\n", data[7 + 11]);
INFO_LOG(CONSOLE, "Js.Min.y: %i\n", data[7 + 12]);
INFO_LOG(CONSOLE, "JS.Center.y: %i\n\n", data[7 + 13]);
}
else // g_Config.bClassicControllerConnected
{
INFO_LOG(CONSOLE, "\nGame got the Classic Controller calibration:\n");
INFO_LOG(CONSOLE, "Lx.Max: %i\n", data[7 + 0]);
INFO_LOG(CONSOLE, "Lx.Min: %i\n", data[7 + 1]);
INFO_LOG(CONSOLE, "Lx.Center: %i\n", data[7 + 2]);
INFO_LOG(CONSOLE, "Ly.Max: %i\n", data[7 + 3]);
INFO_LOG(CONSOLE, "Ly.Min: %i\n", data[7 + 4]);
INFO_LOG(CONSOLE, "Ly.Center: %i\n", data[7 + 5]);
INFO_LOG(CONSOLE, "Rx.Max.x: %i\n", data[7 + 6]);
INFO_LOG(CONSOLE, "Rx.Min.x: %i\n", data[7 + 7]);
INFO_LOG(CONSOLE, "Rx.Center.x: %i\n", data[7 + 8]);
INFO_LOG(CONSOLE, "Ry.Max.y: %i\n", data[7 + 9]);
INFO_LOG(CONSOLE, "Ry.Min: %i\n", data[7 + 10]);
INFO_LOG(CONSOLE, "Ry.Center: %i\n\n", data[7 + 11]);
INFO_LOG(CONSOLE, "Lt.Neutral: %i\n", data[7 + 12]);
INFO_LOG(CONSOLE, "Rt.Neutral %i\n\n", data[7 + 13]);
}
// Save the values if they come from the real Wiimote
if (!Emu)
{
// Save the values from the Nunchuck
if(data[7 + 0] != 0xff)
{
memcpy(WiiMoteEmu::g_RegExt + 0x20, &data[7], 0x10);
memcpy(WiiMoteEmu::g_RegExt + 0x30, &data[7], 0x10);
}
// Save the default values that should work with Wireless Nunchucks
else
{
WiiMoteEmu::SetDefaultExtensionRegistry();
}
WiiMoteEmu::UpdateEeprom();
}
// We got a third party nunchuck
else if(data[7 + 0] == 0xff)
{
memcpy(WiiMoteEmu::g_RegExt + 0x20, WiiMoteEmu::wireless_nunchuck_calibration, sizeof(WiiMoteEmu::wireless_nunchuck_calibration));
memcpy(WiiMoteEmu::g_RegExt + 0x30, WiiMoteEmu::wireless_nunchuck_calibration, sizeof(WiiMoteEmu::wireless_nunchuck_calibration));
}
// Show the encrypted data
INFO_LOG(CONSOLE, "%s", TmpData.c_str());
}
break;
case WM_WRITE_DATA_REPLY: // 0x22
size = sizeof(wm_acknowledge) - 1;
Name = "REPLY";
break;
case WM_REPORT_CORE: // 0x30-0x37
size = sizeof(wm_report_core);
DataReport = true;
break;
case WM_REPORT_CORE_ACCEL:
size = sizeof(wm_report_core_accel);
DataReport = true;
break;
case WM_REPORT_CORE_EXT8:
size = sizeof(wm_report_core_accel_ir12);
DataReport = true;
break;
case WM_REPORT_CORE_ACCEL_IR12:
size = sizeof(wm_report_core_accel_ir12);
DataReport = true;
break;
case WM_REPORT_CORE_EXT19:
size = sizeof(wm_report_core_accel_ext16);
DataReport = true;
break;
case WM_REPORT_CORE_ACCEL_EXT16:
size = sizeof(wm_report_core_accel_ext16);
DataReport = true;
break;
case WM_REPORT_CORE_IR10_EXT9:
size = sizeof(wm_report_core_accel_ir10_ext6);
DataReport = true;
break;
case WM_REPORT_CORE_ACCEL_IR10_EXT6:
size = sizeof(wm_report_core_accel_ir10_ext6);
DataReport = true;
break;
default:
//PanicAlert("%s ReadDebugging: Unknown channel 0x%02x", (Emu ? "Emu" : "Real"), data[1]);
INFO_LOG(CONSOLE, "%s ReadDebugging: Unknown channel 0x%02x", (Emu ? "Emu" : "Real"), data[1]);
return;
}
if (!DataReport && g_DebugComm)
{
std::string tmpData = ArrayToString(data, size + 2, 0, 30);
//LOGV(WII_IPC_WIIMOTE, 3, " Data: %s", Temp.c_str());
INFO_LOG(CONSOLE, "Read[%s] %s: %s\n", (Emu ? "Emu" : "Real"), Name.c_str(), tmpData.c_str()); // No timestamp
//INFO_LOG(CONSOLE, " (%s): %s\n", Tm(true).c_str(), Temp.c_str()); // Timestamp
}
if (DataReport && g_DebugData)
{
// Decrypt extension data
if(WiiMoteEmu::g_ReportingMode == 0x37)
wiimote_decrypt(&WiiMoteEmu::g_ExtKey, &data[17], 0x00, 0x06);
if(WiiMoteEmu::g_ReportingMode == 0x35)
wiimote_decrypt(&WiiMoteEmu::g_ExtKey, &data[7], 0x00, 0x06);
// Produce string
//std::string TmpData = ArrayToString(data, size + 2, 0, 30);
//LOGV(WII_IPC_WIIMOTE, 3, " Data: %s", Temp.c_str());
std::string TmpCore = "", TmpAccel = "", TmpIR = "", TmpExt = "", CCData = "";
TmpCore = StringFromFormat(
"%02x %02x %02x %02x",
data[0], data[1], data[2], data[3]); // Header and core buttons
TmpAccel = StringFromFormat(
"%03i %03i %03i",
data[4], data[5], data[6]); // Wiimote accelerometer
if (data[1] == 0x33) // WM_REPORT_CORE_ACCEL_IR12
{
TmpIR = StringFromFormat(
"%02x %02x %02x %02x %02x %02x"
" %02x %02x %02x %02x %02x %02x",
data[7], data[8], data[9], data[10], data[11], data[12],
data[13], data[14], data[15], data[16], data[17], data[18]);
}
if (data[1] == 0x35) // WM_REPORT_CORE_ACCEL_EXT16
{
TmpExt = StringFromFormat(
"%02x %02x %02x %02x %02x %02x",
data[7], data[8], // Nunchuck stick
data[9], data[10], data[11], // Nunchuck Accelerometer
data[12]); // Nunchuck buttons
CCData = WiiMoteEmu::CCData2Values(&data[7]);
}
if (data[1] == 0x37) // WM_REPORT_CORE_ACCEL_IR10_EXT6
{
TmpIR = StringFromFormat(
"%02x %02x %02x %02x %02x"
" %02x %02x %02x %02x %02x",
data[7], data[8], data[9], data[10], data[11],
data[12], data[13], data[14], data[15], data[16]);
TmpExt = StringFromFormat(
"%02x %02x %02x %02x %02x %02x",
data[17], data[18], // Nunchuck stick
data[19], data[20], data[21], // Nunchuck Accelerometer
data[22]); // Nunchuck buttons
CCData = WiiMoteEmu::CCData2Values(&data[17]);
}
// ---------------------------------------------
// Calculate the Wiimote roll and pitch in degrees
// -----------
int Roll, Pitch, RollAdj, PitchAdj;
WiiMoteEmu::PitchAccelerometerToDegree(data[4], data[5], data[6], Roll, Pitch, RollAdj, PitchAdj);
std::string RollPitch = StringFromFormat("%s %s %s %s",
(Roll >= 0) ? StringFromFormat(" %03i", Roll).c_str() : StringFromFormat("%04i", Roll).c_str(),
(Pitch >= 0) ? StringFromFormat(" %03i", Pitch).c_str() : StringFromFormat("%04i", Pitch).c_str(),
(RollAdj == Roll) ? " " : StringFromFormat("%04i*", RollAdj).c_str(),
(PitchAdj == Pitch) ? " " : StringFromFormat("%04i*", PitchAdj).c_str());
// -------------------------
// ---------------------------------------------
// Test the angles to x, y, z values formula by calculating the values back and forth
// -----------
/* //Console::ClearScreen();
// Show a test of our calculations
WiiMoteEmu::TiltTest(data[4], data[5], data[6]);
u8 x, y, z;
WiiMoteEmu::Tilt(x, y, z);
WiiMoteEmu::TiltTest(x, y, z);*/
// -------------------------
// ---------------------------------------------
// Show the number of g forces on the axes
// -----------
float Gx = WiiMoteEmu::AccelerometerToG((float)data[4], (float)g_wm.cal_zero.x, (float)g_wm.cal_g.x);
float Gy = WiiMoteEmu::AccelerometerToG((float)data[5], (float)g_wm.cal_zero.y, (float)g_wm.cal_g.y);
float Gz = WiiMoteEmu::AccelerometerToG((float)data[6], (float)g_wm.cal_zero.z, (float)g_wm.cal_g.z);
std::string GForce = StringFromFormat("%s %s %s",
((int)Gx >= 0) ? StringFromFormat(" %i", (int)Gx).c_str() : StringFromFormat("%i", (int)Gx).c_str(),
((int)Gy >= 0) ? StringFromFormat(" %i", (int)Gy).c_str() : StringFromFormat("%i", (int)Gy).c_str(),
((int)Gz >= 0) ? StringFromFormat(" %i", (int)Gz).c_str() : StringFromFormat("%i", (int)Gz).c_str());
// -------------------------
// ---------------------------------------------
// Calculate the IR data
// -----------
if (data[1] == WM_REPORT_CORE_ACCEL_IR10_EXT6) WiiMoteEmu::IRData2DotsBasic(&data[7]); else WiiMoteEmu::IRData2Dots(&data[7]);
std::string IRData;
// Create a shortcut
struct WiiMoteEmu::SDot* Dot = WiiMoteEmu::g_Wiimote_kbd.IR.Dot;
for (int i = 0; i < 4; ++i)
{
if(Dot[i].Visible)
IRData += StringFromFormat("[%i] X:%04i Y:%04i Size:%i ", Dot[i].Order, Dot[i].Rx, Dot[i].Ry, Dot[i].Size);
else
IRData += StringFromFormat("[%i]", Dot[i].Order);
}
// Dot distance
IRData += StringFromFormat(" | Distance:%i", WiiMoteEmu::g_Wiimote_kbd.IR.Distance);
// -------------------------
// Classic Controller data
INFO_LOG(CONSOLE, "Read[%s]: %s | %s | %s | %s | %s\n", (Emu ? "Emu" : "Real"),
TmpCore.c_str(), TmpAccel.c_str(), TmpIR.c_str(), TmpExt.c_str(), CCData.c_str());
// Formatted data only
//INFO_LOG(CONSOLE, "Read[%s]: 0x%02x | %s | %s | %s\n", (Emu ? "Emu" : "Real"), data[1], RollPitch.c_str(), GForce.c_str(), IRData.c_str());
// IR data
//INFO_LOG(CONSOLE, "Read[%s]: %s | %s\n", (Emu ? "Emu" : "Real"), TmpData.c_str(), IRData.c_str());
// Accelerometer data
//INFO_LOG(CONSOLE, "Read[%s]: %s | %s | %s | %s | %s | %s | %s\n", (Emu ? "Emu" : "Real"),
// TmpCore.c_str(), TmpAccel.c_str(), TmpIR.c_str(), TmpExt.c_str(), RollPitch.c_str(), GForce.c_str(), CCData.c_str());
// Timestamp
//INFO_LOG(CONSOLE, " (%s): %s\n", Tm(true).c_str(), Temp.c_str());
}
if(g_DebugAccelerometer)
{
// Accelerometer only
// Console::ClearScreen();
INFO_LOG(CONSOLE, "Accel x, y, z: %03u %03u %03u\n", data[4], data[5], data[6]);
}
}
void InterruptDebugging(bool Emu, const void* _pData)
{
//
const u8* data = (const u8*)_pData;
std::string Name;
int size;
u16 SampleValue;
bool SoundData = false;
if (g_DebugComm) Name += StringFromFormat("Write[%s] ", (Emu ? "Emu" : "Real"));
switch(data[1])
{
case 0x10:
size = 4; // I don't know the size
if (g_DebugComm) Name.append("0x10");
break;
case WM_LEDS: // 0x11
size = sizeof(wm_leds);
if (g_DebugComm) Name.append("WM_LEDS");
break;
case WM_DATA_REPORTING: // 0x12
size = sizeof(wm_data_reporting);
if (g_DebugComm) Name.append("WM_DATA_REPORTING");
break;
case WM_REQUEST_STATUS: // 0x15
size = sizeof(wm_request_status);
if (g_DebugComm) Name.append("WM_REQUEST_STATUS");
break;
case WM_WRITE_DATA: // 0x16
if (g_DebugComm) Name.append("WM_WRITE_DATA");
size = sizeof(wm_write_data);
// data[2]: The address space 0, 1 or 2
// data[3]: The registry type
// data[5]: The registry offset
// data[6]: The number of bytes
switch(data[2] >> 0x01)
{
case WM_SPACE_EEPROM:
if (g_DebugComm) Name.append(" REG_EEPROM"); break;
case WM_SPACE_REGS1:
case WM_SPACE_REGS2:
switch(data[3])
{
case 0xa2:
// data[8]: FF, 0x00 or 0x40
// data[9, 10]: RR RR, 0xd007 or 0x401f
// data[11]: VV, 0x00 to 0xff or 0x00 to 0x40
if (g_DebugComm)
{
Name.append(" REG_SPEAKER");
if(data[6] == 7)
{
INFO_LOG(CONSOLE, "\nSound configuration:\n");
if(data[8] == 0x00)
{
memcpy(&SampleValue, &data[9], 2);
INFO_LOG(CONSOLE, " Data format: 4-bit ADPCM (%i Hz)\n", 6000000 / SampleValue);
INFO_LOG(CONSOLE, " Volume: %02i%%\n\n", (data[11] / 0x40) * 100);
}
else if (data[8] == 0x40)
{
memcpy(&SampleValue, &data[9], 2);
INFO_LOG(CONSOLE, " Data format: 8-bit PCM (%i Hz)\n", 12000000 / SampleValue);
INFO_LOG(CONSOLE, " Volume: %02i%%\n\n", (data[11] / 0xff) * 100);
}
}
}
break;
case 0xa4:
if (g_DebugComm) Name.append(" REG_EXT");
// Update the encryption mode
if (data[3] == 0xa4 && data[5] == 0xf0)
{
if (data[7] == 0xaa)
WiiMoteEmu::g_Encryption = true;
else if (data[7] == 0x55)
WiiMoteEmu::g_Encryption = false;
INFO_LOG(CONSOLE, "\nExtension enryption turned %s\n\n", WiiMoteEmu::g_Encryption ? "On" : "Off");
}
break;
case 0xb0:
if (g_DebugComm) Name.append(" REG_IR"); break;
}
break;
}
break;
case WM_READ_DATA: // 0x17
size = sizeof(wm_read_data);
// data[2]: The address space 0, 1 or 2
// data[3]: The registry type
// data[5]: The registry offset
// data[7]: The number of bytes, 6 and 7 together
if (g_DebugComm) Name.append("WM_READ_DATA");
switch(data[2] >> 0x01)
{
case WM_SPACE_EEPROM:
if (g_DebugComm) Name.append(" REG_EEPROM"); break;
case WM_SPACE_REGS1:
case WM_SPACE_REGS2:
switch(data[3])
{
case 0xa2:
if (g_DebugComm) Name.append(" REG_SPEAKER"); break;
case 0xa4:
if (g_DebugComm) Name.append(" REG_EXT"); break;
case 0xb0:
if (g_DebugComm) Name.append(" REG_IR"); break;
}
break;
}
break;
case WM_IR_PIXEL_CLOCK: // 0x13
case WM_IR_LOGIC: // 0x1a
if (g_DebugComm) Name.append("WM_IR");
size = 1;
break;
case WM_SPEAKER_ENABLE: // 0x14
case WM_SPEAKER_MUTE: // 0x19
if (g_DebugComm) Name.append("WM_SPEAKER");
size = 1;
if(data[1] == 0x14) {
INFO_LOG(CONSOLE, "\nSpeaker %s\n\n", (data[2] == 0x06) ? "On" : "Off");
} else if(data[1] == 0x19) {
INFO_LOG(CONSOLE, "\nSpeaker %s\n\n", (data[2] == 0x06) ? "Muted" : "Unmuted");
}
break;
case WM_WRITE_SPEAKER_DATA: // 0x18
if (g_DebugComm) Name.append("WM_SPEAKER_DATA");
size = 21;
break;
default:
size = 15;
INFO_LOG(CONSOLE, "%s InterruptDebugging: Unknown channel 0x%02x", (Emu ? "Emu" : "Real"), data[1]);
break;
}
if (g_DebugComm && !SoundData)
{
std::string Temp = ArrayToString(data, size + 2, 0, 30);
//LOGV(WII_IPC_WIIMOTE, 3, " Data: %s", Temp.c_str());
INFO_LOG(CONSOLE, "%s: %s\n", Name.c_str(), Temp.c_str()); // No timestamp
//INFO_LOG(CONSOLE, " (%s): %s\n", Tm(true).c_str(), Temp.c_str()); // Timestamp
}
if (g_DebugSoundData && SoundData)
{
std::string Temp = ArrayToString(data, size + 2, 0, 30);
//LOGV(WII_IPC_WIIMOTE, 3, " Data: %s", Temp.c_str());
INFO_LOG(CONSOLE, "%s: %s\n", Name.c_str(), Temp.c_str()); // No timestamp
//INFO_LOG(CONSOLE, " (%s): %s\n", Tm(true).c_str(), Temp.c_str()); // Timestamp
}
}
/* Returns a timestamp with three decimals for precise time comparisons. The return format is
of the form seconds.milleseconds for example 1234.123. The leding seconds have no particular meaning
but are just there to enable use to tell if we have entered a new second or now. */
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
double GetDoubleTime()
{
#if defined(HAVE_WX) && HAVE_WX
wxDateTime datetime = wxDateTime::UNow(); // Get timestamp
u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970(); // Get continous timestamp
/* Remove a few years. We only really want enough seconds to make sure that we are
detecting actual actions, perhaps 60 seconds is enough really, but I leave a
year of seconds anyway, in case the user's clock is incorrect or something like that */
TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60);
//if (TmpSeconds < 0) return 0; // Check the the user's clock is working somewhat
u32 Seconds = (u32)TmpSeconds; // Make a smaller integer that fits in the double
double ms = datetime.GetMillisecond() / 1000.0;
double TmpTime = Seconds + ms;
return TmpTime;
#endif
}
/* Calculate the current update frequency. Calculate the time between ten updates, and average
five such rates. If we assume there are 60 updates per second if the game is running at full
speed then we get this measure on average once every second. The reason to have a few updates
between each measurement is becase the milliseconds may not be perfectly accurate and may return
the same time even when a milliseconds has actually passed, for example.*/
int GetUpdateRate()
{
#if defined(HAVE_WX) && HAVE_WX
if(g_UpdateCounter == 10)
{
// Erase the old ones
if(g_UpdateTimeList.size() == 5) g_UpdateTimeList.erase(g_UpdateTimeList.begin() + 0);
// Calculate the time and save it
int Time = (int)(10 / (GetDoubleTime() - g_UpdateTime));
g_UpdateTimeList.push_back(Time);
//INFO_LOG(CONSOLE, "Time: %i %f\n", Time, GetDoubleTime());
int TotalTime = 0;
for (int i = 0; i < (int)g_UpdateTimeList.size(); i++)
TotalTime += g_UpdateTimeList.at(i);
g_UpdateRate = TotalTime / 5;
// Write the new update time
g_UpdateTime = GetDoubleTime();
g_UpdateCounter = 0;
}
g_UpdateCounter++;
return g_UpdateRate;
#else
return 0;
#endif
}
void DoInitialize()
{
// Run this first so that WiiMoteReal::Initialize() overwrites g_Eeprom
WiiMoteEmu::Initialize();
/* We will run WiiMoteReal::Initialize() even if we are not using a real
wiimote, to check if there is a real wiimote connected. We will initiate
wiiuse.dll, but we will return before creating a new thread for it if we
find no real Wiimotes. Then g_RealWiiMotePresent will also be
false. This function call will be done instantly whether there is a real
Wiimote connected or not. It takes no time for Wiiuse to check for
connected Wiimotes. */
#if HAVE_WIIUSE
if (g_Config.bConnectRealWiimote) WiiMoteReal::Initialize();
#endif
}