New Wiimote Plugin: Added a "Hybrid Wiimote" input source type. This allows a real wiimote to be used with an emulated extension.(and in the future, real wiimote with emulated motion plus) If the emulated extension is set to "None", a real extension can be used. Real/Emulated buttons are combined. Real acceleration data is used. Currently, emulated IR data is used.(might change this to allow both) Switching between an emulated and a real extension in-game is a bit testy right now, but if you switch the emu-extension to "None" before connecting a real extension, it usually works.
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6082 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
a1daa636c2
commit
2b45e87b3e
|
@ -5,6 +5,8 @@
|
|||
// a simple lockless thread-safe,
|
||||
// single reader, single writer queue
|
||||
|
||||
#include "Atomic.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
|
||||
|
@ -12,7 +14,7 @@ template <typename T>
|
|||
class FifoQueue
|
||||
{
|
||||
public:
|
||||
FifoQueue()
|
||||
FifoQueue() : m_size(0)
|
||||
{
|
||||
m_write_ptr = m_read_ptr = new ElementPtr();
|
||||
}
|
||||
|
@ -23,9 +25,20 @@ public:
|
|||
delete m_read_ptr;
|
||||
}
|
||||
|
||||
bool Empty() const // true if the queue is empty
|
||||
u32 Size() const
|
||||
{
|
||||
return (m_read_ptr == m_write_ptr);
|
||||
return m_size;
|
||||
}
|
||||
|
||||
bool Empty() const
|
||||
{
|
||||
//return (m_read_ptr == m_write_ptr);
|
||||
return (0 == m_size);
|
||||
}
|
||||
|
||||
const T& Front() const
|
||||
{
|
||||
return *m_read_ptr->current;
|
||||
}
|
||||
|
||||
void Push(const T& t)
|
||||
|
@ -35,42 +48,41 @@ public:
|
|||
// set the next pointer to a new element ptr
|
||||
// then advance the write pointer
|
||||
m_write_ptr = m_write_ptr->next = new ElementPtr();
|
||||
Common::AtomicIncrement(m_size);
|
||||
}
|
||||
|
||||
void Pop()
|
||||
{
|
||||
Common::AtomicDecrement(m_size);
|
||||
ElementPtr *const tmpptr = m_read_ptr;
|
||||
// advance the read pointer
|
||||
m_read_ptr = m_read_ptr->next;
|
||||
// set the next element to NULL to stop the recursive deletion
|
||||
tmpptr->next = NULL;
|
||||
delete tmpptr; // this also deletes the element
|
||||
}
|
||||
|
||||
bool Pop(T& t)
|
||||
{
|
||||
// if write pointer points to the same element, queue is empty
|
||||
if (m_read_ptr == m_write_ptr)
|
||||
if (Empty())
|
||||
return false;
|
||||
|
||||
// get the element from out of the queue
|
||||
t = *m_read_ptr->current;
|
||||
|
||||
ElementPtr *const tmpptr = m_read_ptr;
|
||||
// advance the read pointer
|
||||
m_read_ptr = m_read_ptr->next;
|
||||
|
||||
// set the next element to NULL to stop the recursive deletion
|
||||
tmpptr->next = NULL;
|
||||
delete tmpptr; // this also deletes the element
|
||||
t = Front();
|
||||
Pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Peek(T& t)
|
||||
// not thread-safe
|
||||
void Clear()
|
||||
{
|
||||
// if write pointer points to the same element, queue is empty
|
||||
if (m_read_ptr == m_write_ptr)
|
||||
return false;
|
||||
|
||||
// get the element from out of the queue
|
||||
t = *m_read_ptr->current;
|
||||
|
||||
return true;
|
||||
m_size = 0;
|
||||
delete m_read_ptr;
|
||||
m_write_ptr = m_read_ptr = new ElementPtr();
|
||||
}
|
||||
|
||||
private:
|
||||
// stores a pointer to element at front of queue
|
||||
// stores a pointer to element
|
||||
// and a pointer to the next ElementPtr
|
||||
class ElementPtr
|
||||
{
|
||||
|
@ -94,6 +106,7 @@ private:
|
|||
|
||||
ElementPtr *volatile m_write_ptr;
|
||||
ElementPtr *volatile m_read_ptr;
|
||||
volatile u32 m_size;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ WiimoteConfigPage::WiimoteConfigPage(wxWindow* const parent, const int index)
|
|||
m_input_src_choice->Append(wxT("None"));
|
||||
m_input_src_choice->Append(wxT("Emulated Wiimote"));
|
||||
m_input_src_choice->Append(wxT("Real Wiimote"));
|
||||
m_input_src_choice->Append(wxT("Hybrid Wiimote"));
|
||||
m_input_src_choice->Select(g_wiimote_sources[m_index]);
|
||||
_connect_macro_(m_input_src_choice, WiimoteConfigPage::SelectSource, wxEVT_COMMAND_CHOICE_SELECTED, this);
|
||||
|
||||
|
|
|
@ -38,23 +38,21 @@
|
|||
#include "pluginspecs_wiimote.h"
|
||||
|
||||
#include "WiimoteEmu.h"
|
||||
#include "WiimoteHid.h"
|
||||
#include "../WiimoteReal/WiimoteReal.h"
|
||||
|
||||
#include "Attachment/Attachment.h"
|
||||
|
||||
/* Bit shift conversions */
|
||||
u32 convert24bit(const u8* src)
|
||||
u32 swap24(const u8* src)
|
||||
{
|
||||
return (src[0] << 16) | (src[1] << 8) | src[2];
|
||||
}
|
||||
|
||||
u16 convert16bit(const u8* src)
|
||||
{
|
||||
return (src[0] << 8) | src[1];
|
||||
}
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
|
||||
void Wiimote::ReportMode(const u16 _channelID, wm_report_mode* dr)
|
||||
void Wiimote::ReportMode(const wm_report_mode* const dr)
|
||||
{
|
||||
//INFO_LOG(WIIMOTE, "Set data report mode");
|
||||
//DEBUG_LOG(WIIMOTE, " Rumble: %x", dr->rumble);
|
||||
|
@ -92,9 +90,9 @@ void Wiimote::ReportMode(const u16 _channelID, wm_report_mode* dr)
|
|||
The IR enable/disable and speaker enable/disable and mute/unmute values are
|
||||
bit2: 0 = Disable (0x02), 1 = Enable (0x06)
|
||||
*/
|
||||
void Wiimote::HidOutputReport(const u16 _channelID, wm_report* sr)
|
||||
void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack)
|
||||
{
|
||||
INFO_LOG(WIIMOTE, "HidOutputReport (page: %i, cid: 0x%02x, wm: 0x%02x)", m_index, _channelID, sr->wm);
|
||||
INFO_LOG(WIIMOTE, "HidOutputReport (page: %i, cid: 0x%02x, wm: 0x%02x)", m_index, m_reporting_channel, sr->wm);
|
||||
|
||||
// wiibrew:
|
||||
// In every single Output Report, bit 0 (0x01) of the first byte controls the Rumble feature.
|
||||
|
@ -113,7 +111,7 @@ void Wiimote::HidOutputReport(const u16 _channelID, wm_report* sr)
|
|||
break;
|
||||
|
||||
case WM_REPORT_MODE : // 0x12
|
||||
ReportMode(_channelID, (wm_report_mode*)sr->data);
|
||||
ReportMode((wm_report_mode*)sr->data);
|
||||
break;
|
||||
|
||||
case WM_IR_PIXEL_CLOCK : // 0x13
|
||||
|
@ -128,16 +126,16 @@ void Wiimote::HidOutputReport(const u16 _channelID, wm_report* sr)
|
|||
break;
|
||||
|
||||
case WM_REQUEST_STATUS : // 0x15
|
||||
RequestStatus(_channelID, (wm_request_status*)sr->data);
|
||||
RequestStatus((wm_request_status*)sr->data);
|
||||
return; // sends its own ack
|
||||
break;
|
||||
|
||||
case WM_WRITE_DATA : // 0x16
|
||||
WriteData(_channelID, (wm_write_data*)sr->data);
|
||||
WriteData((wm_write_data*)sr->data);
|
||||
break;
|
||||
|
||||
case WM_READ_DATA : // 0x17
|
||||
ReadData(_channelID, (wm_read_data*)sr->data);
|
||||
ReadData((wm_read_data*)sr->data);
|
||||
return; // sends its own ack
|
||||
break;
|
||||
|
||||
|
@ -175,7 +173,8 @@ void Wiimote::HidOutputReport(const u16 _channelID, wm_report* sr)
|
|||
}
|
||||
|
||||
// send ack
|
||||
SendAck(_channelID, sr->wm);
|
||||
if (send_ack)
|
||||
SendAck(sr->wm);
|
||||
}
|
||||
|
||||
/* This will generate the 0x22 acknowledgement for most Input reports.
|
||||
|
@ -183,7 +182,7 @@ void Wiimote::HidOutputReport(const u16 _channelID, wm_report* sr)
|
|||
The first two bytes are the core buttons data,
|
||||
00 00 means nothing is pressed.
|
||||
The last byte is the success code 00. */
|
||||
void Wiimote::SendAck(const u16 _channelID, u8 _reportID)
|
||||
void Wiimote::SendAck(u8 _reportID)
|
||||
{
|
||||
u8 data[6];
|
||||
|
||||
|
@ -196,23 +195,16 @@ void Wiimote::SendAck(const u16 _channelID, u8 _reportID)
|
|||
ack->reportID = _reportID;
|
||||
ack->errorID = 0;
|
||||
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel( m_index, _channelID, data, sizeof(data));
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel( m_index, m_reporting_channel, data, sizeof(data));
|
||||
}
|
||||
|
||||
// old comment
|
||||
/* Here we produce a 0x20 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 Wiimote::RequestStatus(const u16 _channelID, wm_request_status* rs)
|
||||
void Wiimote::HandleExtensionSwap()
|
||||
{
|
||||
//if (rs)
|
||||
|
||||
// handle switch extension
|
||||
if ( m_extension->active_extension != m_extension->switch_extension )
|
||||
if (m_extension->active_extension != m_extension->switch_extension)
|
||||
{
|
||||
// if an extension is currently connected and we want to switch to a different extension
|
||||
if ( (m_extension->active_extension > 0) && m_extension->switch_extension )
|
||||
if ((m_extension->active_extension > 0) && m_extension->switch_extension)
|
||||
// detach extension first, wait til next Update() or RequestStatus() call to change to the new extension
|
||||
m_extension->active_extension = 0;
|
||||
else
|
||||
|
@ -223,8 +215,18 @@ void Wiimote::RequestStatus(const u16 _channelID, wm_request_status* rs)
|
|||
m_status.extension = m_extension->active_extension ? 1 : 0;
|
||||
|
||||
// set register, I hate this line
|
||||
m_register[ 0xa40000 ] = ((WiimoteEmu::Attachment*)m_extension->attachments[ m_extension->active_extension ])->reg;
|
||||
m_register[0xa40000] = ((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension])->reg;
|
||||
}
|
||||
}
|
||||
|
||||
// old comment
|
||||
/* Here we produce a 0x20 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 Wiimote::RequestStatus(const wm_request_status* const rs)
|
||||
{
|
||||
HandleExtensionSwap();
|
||||
|
||||
// set up report
|
||||
u8 data[8];
|
||||
|
@ -234,14 +236,31 @@ void Wiimote::RequestStatus(const u16 _channelID, wm_request_status* rs)
|
|||
// status values
|
||||
*(wm_status_report*)(data + 2) = m_status;
|
||||
|
||||
// hybrid wiimote stuff
|
||||
if (WIIMOTE_SRC_HYBRID == g_wiimote_sources[m_index] && (m_extension->switch_extension <= 0))
|
||||
{
|
||||
using namespace WiimoteReal;
|
||||
|
||||
g_refresh_critsec.Enter();
|
||||
if (g_wiimotes[m_index])
|
||||
{
|
||||
wm_request_status rpt;
|
||||
rpt.rumble = 0;
|
||||
g_wiimotes[m_index]->SendPacket(WM_REQUEST_STATUS, &rpt, sizeof(rpt));
|
||||
}
|
||||
g_refresh_critsec.Leave();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// send report
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, _channelID, data, sizeof(data));
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data));
|
||||
}
|
||||
|
||||
/* Write data to Wiimote and Extensions registers. */
|
||||
void Wiimote::WriteData(const u16 _channelID, wm_write_data* wd)
|
||||
void Wiimote::WriteData(const wm_write_data* const wd)
|
||||
{
|
||||
u32 address = convert24bit(wd->address);
|
||||
u32 address = swap24(wd->address);
|
||||
|
||||
// ignore the 0x010000 bit
|
||||
address &= 0xFEFFFF;
|
||||
|
@ -324,14 +343,24 @@ void Wiimote::WriteData(const u16 _channelID, wm_write_data* wd)
|
|||
}
|
||||
|
||||
/* Read data from Wiimote and Extensions registers. */
|
||||
void Wiimote::ReadData(const u16 _channelID, wm_read_data* rd)
|
||||
void Wiimote::ReadData(const wm_read_data* const rd)
|
||||
{
|
||||
u32 address = convert24bit(rd->address);
|
||||
u16 size = convert16bit(rd->size);
|
||||
u32 address = swap24(rd->address);
|
||||
u16 size = Common::swap16(rd->size);
|
||||
|
||||
// ignore the 0x010000 bit
|
||||
address &= 0xFEFFFF;
|
||||
|
||||
// hybrid wiimote stuff
|
||||
// relay the read data request to real-wiimote
|
||||
if (WIIMOTE_SRC_HYBRID == g_wiimote_sources[m_index] && ((0xA4 != (address >> 16)) || (m_extension->switch_extension <= 0)))
|
||||
{
|
||||
WiimoteReal::InterruptChannel(m_index, m_reporting_channel, ((u8*)rd) - 2, sizeof(wm_read_data) + 2); // hacky
|
||||
|
||||
// don't want emu-wiimote to send reply
|
||||
return;
|
||||
}
|
||||
|
||||
ReadRequest rr;
|
||||
u8* block = new u8[size];
|
||||
|
||||
|
@ -416,14 +445,14 @@ void Wiimote::ReadData(const u16 _channelID, wm_read_data* rd)
|
|||
}
|
||||
|
||||
// want the requested address, not the above modified one
|
||||
rr.address = convert24bit(rd->address);
|
||||
rr.address = swap24(rd->address);
|
||||
rr.size = size;
|
||||
//rr.channel = _channelID;
|
||||
rr.position = 0;
|
||||
rr.data = block;
|
||||
|
||||
// send up to 16 bytes
|
||||
SendReadDataReply( _channelID, rr );
|
||||
SendReadDataReply(rr);
|
||||
|
||||
// if there is more data to be sent, add it to the queue
|
||||
if (rr.size)
|
||||
|
@ -440,7 +469,7 @@ void Wiimote::ReadData(const u16 _channelID, wm_read_data* rd)
|
|||
bytes in the message, the 0 means no error, the 00 20 means that the message
|
||||
is at the 00 20 offest in the registry that was read.
|
||||
*/
|
||||
void Wiimote::SendReadDataReply(const u16 _channelID, ReadRequest& _request)
|
||||
void Wiimote::SendReadDataReply(ReadRequest& _request)
|
||||
{
|
||||
u8 data[23];
|
||||
data[0] = 0xA1;
|
||||
|
@ -488,7 +517,7 @@ void Wiimote::SendReadDataReply(const u16 _channelID, ReadRequest& _request)
|
|||
}
|
||||
|
||||
// Send a piece
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, _channelID, data, sizeof(data));
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data));
|
||||
}
|
||||
|
||||
void Wiimote::DoState(PointerWrap& p)
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
#include "WiimoteEmu.h"
|
||||
#include "WiimoteHid.h"
|
||||
|
||||
#include <Timer.h>
|
||||
#include <Common.h>
|
||||
#include "../WiimoteReal/WiimoteReal.h"
|
||||
|
||||
#include "Timer.h"
|
||||
#include "Common.h"
|
||||
|
||||
#include "UDPTLayer.h"
|
||||
|
||||
|
@ -38,10 +40,7 @@ static const u8 eeprom_data_16D0[] = {
|
|||
|
||||
#define SWING_INTENSITY 0x40
|
||||
|
||||
static struct ReportFeatures
|
||||
{
|
||||
u8 core, accel, ir, ext, size;
|
||||
} const reporting_mode_features[] =
|
||||
const ReportFeatures reporting_mode_features[] =
|
||||
{
|
||||
//0x30: Core Buttons
|
||||
{ 2, 0, 0, 0, 4 },
|
||||
|
@ -324,19 +323,215 @@ std::string Wiimote::GetName() const
|
|||
return std::string("Wiimote") + char('1'+m_index);
|
||||
}
|
||||
|
||||
void Wiimote::Update()
|
||||
{
|
||||
const bool is_sideways = m_options->settings[1]->value > 0;
|
||||
const bool is_upright = m_options->settings[2]->value > 0;
|
||||
// if windows is focused or background input is enabled
|
||||
#define HAS_FOCUS (g_WiimoteInitialize.pRendererHasFocus() || (m_options->settings[0]->value != 0))
|
||||
|
||||
// if windows is focused or background input is enabled
|
||||
const bool is_focus = g_WiimoteInitialize.pRendererHasFocus() || (m_options->settings[0]->value != 0);
|
||||
bool Wiimote::Step()
|
||||
{
|
||||
const bool has_focus = HAS_FOCUS;
|
||||
const bool is_sideways = m_options->settings[1]->value != 0;
|
||||
|
||||
// no rumble if no focus
|
||||
if (false == is_focus)
|
||||
if (false == has_focus)
|
||||
m_rumble_on = false;
|
||||
|
||||
m_rumble->controls[0]->control_ref->State(m_rumble_on);
|
||||
|
||||
// update buttons in status struct
|
||||
m_status.buttons = 0;
|
||||
if (has_focus)
|
||||
{
|
||||
m_buttons->GetState(&m_status.buttons, button_bitmasks);
|
||||
m_dpad->GetState(&m_status.buttons, is_sideways ? dpad_sideways_bitmasks : dpad_bitmasks);
|
||||
UDPTLayer::GetButtons(m_udp, &m_status.buttons);
|
||||
}
|
||||
|
||||
// check if there is a read data request
|
||||
if (m_read_requests.size())
|
||||
{
|
||||
ReadRequest& rr = m_read_requests.front();
|
||||
// send up to 16 bytes to the wii
|
||||
SendReadDataReply(rr);
|
||||
//SendReadDataReply(rr.channel, rr);
|
||||
|
||||
// if there is no more data, remove from queue
|
||||
if (0 == rr.size)
|
||||
{
|
||||
delete[] rr.data;
|
||||
m_read_requests.pop();
|
||||
}
|
||||
|
||||
// dont send any other reports
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if a status report needs to be sent
|
||||
// this happens on wiimote sync and when extensions are switched
|
||||
if (m_extension->active_extension != m_extension->switch_extension)
|
||||
{
|
||||
RequestStatus();
|
||||
|
||||
// Wiibrew: Following a connection or disconnection event on the Extension Port,
|
||||
// data reporting is disabled and the Data Reporting Mode must be reset before new data can arrive.
|
||||
// after a game receives an unrequested status report,
|
||||
// it expects data reports to stop until it sets the reporting mode again
|
||||
m_reporting_auto = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Wiimote::GetCoreData(u8* const data)
|
||||
{
|
||||
*(wm_core*)data |= m_status.buttons;
|
||||
}
|
||||
|
||||
void Wiimote::GetAccelData(u8* const data)
|
||||
{
|
||||
const bool has_focus = HAS_FOCUS;
|
||||
const bool is_sideways = m_options->settings[1]->value != 0;
|
||||
const bool is_upright = m_options->settings[2]->value != 0;
|
||||
|
||||
// ----TILT----
|
||||
EmulateTilt((wm_accel*)data, m_tilt, (accel_cal*)&m_eeprom[0x16], has_focus, is_sideways, is_upright);
|
||||
|
||||
// ----SWING----
|
||||
// ----SHAKE----
|
||||
if (has_focus)
|
||||
{
|
||||
EmulateSwing((wm_accel*)data, m_swing, (accel_cal*)&m_eeprom[0x16], is_sideways, is_upright);
|
||||
EmulateShake(data, m_shake, m_shake_step);
|
||||
// UDP Wiimote
|
||||
UDPTLayer::GetAcceleration(m_udp, (wm_accel*)data, (accel_cal*)&m_eeprom[0x16]);
|
||||
}
|
||||
}
|
||||
|
||||
void Wiimote::GetIRData(u8* const data)
|
||||
{
|
||||
const bool has_focus = HAS_FOCUS;
|
||||
|
||||
u16 x[4], y[4];
|
||||
memset(x, 0xFF, sizeof(x));
|
||||
|
||||
if (has_focus)
|
||||
{
|
||||
float xx = 10000, yy = 0, zz = 0;
|
||||
float tx, ty;
|
||||
|
||||
m_ir->GetState(&xx, &yy, &zz, true);
|
||||
UDPTLayer::GetIR(m_udp, &xx, &yy, &zz);
|
||||
|
||||
m_tilt->GetState(&tx, &ty, 0, PI/2, false);
|
||||
|
||||
// disabled cause my math still fails
|
||||
const float rsin = 0;//sin(tx);
|
||||
const float rcos = 1;//cos(tx);
|
||||
|
||||
{
|
||||
const float xxx = (xx * -256 * 0.95f);
|
||||
const float yyy = (yy * -256 * 0.90f);
|
||||
|
||||
xx = 512 + xxx * rcos + (144 + yyy) * rsin;
|
||||
yy = 384 + (108 + yyy) * rcos - xxx * rsin;
|
||||
}
|
||||
|
||||
// dot distance of 25 is too little
|
||||
const unsigned int distance = (unsigned int)(150 + 100 * zz);
|
||||
|
||||
x[0] = (unsigned int)(xx - distance * rcos);
|
||||
x[1] = (unsigned int)(xx + distance * rcos);
|
||||
x[2] = (unsigned int)(xx - 1.2f * distance * rcos);
|
||||
x[3] = (unsigned int)(xx + 1.2f * distance * rcos);
|
||||
|
||||
y[0] = (unsigned int)(yy - 0.75 * distance * rsin);
|
||||
y[1] = (unsigned int)(yy + 0.75 * distance * rsin);
|
||||
y[2] = (unsigned int)(yy - 0.75 * 1.2f * distance * rsin);
|
||||
y[3] = (unsigned int)(yy + 0.75 * 1.2f * distance * rsin);
|
||||
|
||||
}
|
||||
|
||||
// Fill report with valid data when full handshake was done
|
||||
if (m_reg_ir->data[0x30])
|
||||
// ir mode
|
||||
switch (m_reg_ir->mode)
|
||||
{
|
||||
// basic
|
||||
case 1 :
|
||||
{
|
||||
memset(data, 0xFF, 10);
|
||||
wm_ir_basic* const irdata = (wm_ir_basic*)data;
|
||||
for (unsigned int i=0; i<2; ++i)
|
||||
{
|
||||
if (x[i*2] < 1024 && y[i*2] < 768)
|
||||
{
|
||||
irdata[i].x1 = u8(x[i*2]);
|
||||
irdata[i].x1hi = x[i*2] >> 8;
|
||||
|
||||
irdata[i].y1 = u8(y[i*2]);
|
||||
irdata[i].y1hi = y[i*2] >> 8;
|
||||
}
|
||||
if (x[i*2+1] < 1024 && y[i*2+1] < 768)
|
||||
{
|
||||
irdata[i].x2 = u8(x[i*2+1]);
|
||||
irdata[i].x2hi = x[i*2+1] >> 8;
|
||||
|
||||
irdata[i].y2 = u8(y[i*2+1]);
|
||||
irdata[i].y2hi = y[i*2+1] >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
// extended
|
||||
case 3 :
|
||||
{
|
||||
memset(data, 0xFF, 12);
|
||||
wm_ir_extended* const irdata = (wm_ir_extended*)data;
|
||||
for (unsigned int i=0; i<4; ++i)
|
||||
if (x[i] < 1024 && y[i] < 768)
|
||||
{
|
||||
irdata[i].x = u8(x[i]);
|
||||
irdata[i].xhi = x[i] >> 8;
|
||||
|
||||
irdata[i].y = u8(y[i]);
|
||||
irdata[i].yhi = y[i] >> 8;
|
||||
|
||||
irdata[i].size = 10;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// full
|
||||
case 5 :
|
||||
// UNSUPPORTED
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Wiimote::GetExtData(u8* const data)
|
||||
{
|
||||
const bool has_focus = HAS_FOCUS;
|
||||
|
||||
m_extension->GetState(data, HAS_FOCUS);
|
||||
|
||||
// i dont think anything accesses the extension data like this, but ill support it. Indeed, commercial games don't do this.
|
||||
// i think it should be unencrpyted in the register, encrypted when read.
|
||||
memcpy(m_reg_ext->controller_data, data, sizeof(wm_extension));
|
||||
|
||||
if (0xAA == m_reg_ext->encryption)
|
||||
wiimote_encrypt(&m_ext_key, data, 0x00, sizeof(wm_extension));
|
||||
}
|
||||
|
||||
void Wiimote::Update()
|
||||
{
|
||||
// no channel == not connected i guess
|
||||
if (0 == m_reporting_channel)
|
||||
return;
|
||||
|
||||
// returns true if a report was sent
|
||||
if (Step())
|
||||
return;
|
||||
|
||||
// ----speaker----
|
||||
#ifdef USE_WIIMOTE_EMU_SPEAKER
|
||||
|
||||
|
@ -367,208 +562,119 @@ void Wiimote::Update()
|
|||
//}
|
||||
#endif
|
||||
|
||||
// update buttons in status struct
|
||||
m_status.buttons = 0;
|
||||
if (is_focus)
|
||||
{
|
||||
m_buttons->GetState(&m_status.buttons, button_bitmasks);
|
||||
m_dpad->GetState(&m_status.buttons, is_sideways ? dpad_sideways_bitmasks : dpad_bitmasks);
|
||||
UDPTLayer::GetButtons(m_udp, &m_status.buttons);
|
||||
}
|
||||
|
||||
// no channel == not connected i guess
|
||||
if (0 == m_reporting_channel)
|
||||
return;
|
||||
|
||||
// check if there is a read data request
|
||||
if (m_read_requests.size())
|
||||
{
|
||||
ReadRequest& rr = m_read_requests.front();
|
||||
// send up to 16 bytes to the wii
|
||||
SendReadDataReply(m_reporting_channel, rr);
|
||||
//SendReadDataReply(rr.channel, rr);
|
||||
|
||||
// if there is no more data, remove from queue
|
||||
if (0 == rr.size)
|
||||
{
|
||||
delete[] rr.data;
|
||||
m_read_requests.pop();
|
||||
}
|
||||
|
||||
// dont send any other reports
|
||||
return;
|
||||
}
|
||||
|
||||
// -- maybe this should happen before the read request stuff?
|
||||
// check if a status report needs to be sent
|
||||
// this happens on wiimote sync and when extensions are switched
|
||||
if (m_extension->active_extension != m_extension->switch_extension)
|
||||
{
|
||||
RequestStatus(m_reporting_channel);
|
||||
|
||||
// Wiibrew: Following a connection or disconnection event on the Extension Port,
|
||||
// data reporting is disabled and the Data Reporting Mode must be reset before new data can arrive.
|
||||
// after a game receives an unrequested status report,
|
||||
// it expects data reports to stop until it sets the reporting mode again
|
||||
m_reporting_auto = false;
|
||||
}
|
||||
|
||||
if (false == m_reporting_auto)
|
||||
return;
|
||||
|
||||
// figure out what data we need
|
||||
const ReportFeatures& rpt = reporting_mode_features[m_reporting_mode - WM_REPORT_CORE];
|
||||
|
||||
// what does the real wiimote do when put in a reporting mode with extension data,
|
||||
// but with no extension attached? should i just send zeros? sure
|
||||
//if (rpt.ext && (m_extension->active_extension <= 0))
|
||||
//{
|
||||
// m_reporting_auto = false;
|
||||
// return;
|
||||
//}
|
||||
|
||||
// set up output report
|
||||
// made data bigger than needed in case the wii specifies the wrong ir mode for a reporting mode
|
||||
u8 data[46];
|
||||
memset( data, 0, sizeof(data) );
|
||||
|
||||
u8 data[MAX_PAYLOAD];
|
||||
memset(data, 0, sizeof(data));
|
||||
data[0] = 0xA1;
|
||||
data[1] = m_reporting_mode;
|
||||
|
||||
// figure out what data we need
|
||||
const ReportFeatures& rptf = reporting_mode_features[m_reporting_mode - WM_REPORT_CORE];
|
||||
s8 rptf_size = rptf.size;
|
||||
|
||||
// core buttons
|
||||
if (rpt.core)
|
||||
*(wm_core*)(data + rpt.core) = m_status.buttons;
|
||||
if (rptf.core)
|
||||
GetCoreData(data + rptf.core);
|
||||
|
||||
// ----accelerometer----
|
||||
if (rpt.accel)
|
||||
// acceleration
|
||||
if (rptf.accel)
|
||||
GetAccelData(data + rptf.accel);
|
||||
|
||||
// IR
|
||||
if (rptf.ir)
|
||||
GetIRData(data + rptf.ir);
|
||||
|
||||
// extension
|
||||
if (rptf.ext)
|
||||
GetExtData(data + rptf.ext);
|
||||
|
||||
// hybrid wiimote stuff
|
||||
if (WIIMOTE_SRC_HYBRID == g_wiimote_sources[m_index])
|
||||
{
|
||||
// ----TILT----
|
||||
EmulateTilt((wm_accel*)&data[rpt.accel], m_tilt, (accel_cal*)&m_eeprom[0x16], is_focus, is_sideways, is_upright);
|
||||
using namespace WiimoteReal;
|
||||
|
||||
// ----SWING----
|
||||
// ----SHAKE----
|
||||
if (is_focus)
|
||||
g_refresh_critsec.Enter();
|
||||
if (g_wiimotes[m_index])
|
||||
{
|
||||
EmulateSwing((wm_accel*)&data[rpt.accel], m_swing, (accel_cal*)&m_eeprom[0x16], is_sideways, is_upright);
|
||||
EmulateShake(data + rpt.accel, m_shake, m_shake_step);
|
||||
// UDP Wiimote
|
||||
UDPTLayer::GetAcceleration(m_udp, (wm_accel*)&data[rpt.accel], (accel_cal*)&m_eeprom[0x16]);
|
||||
u8* const real_data = g_wiimotes[m_index]->ProcessReadQueue();
|
||||
if (real_data)
|
||||
{
|
||||
switch (real_data[1])
|
||||
{
|
||||
// use data reports
|
||||
default:
|
||||
if (real_data[1] >= WM_REPORT_CORE)
|
||||
{
|
||||
const ReportFeatures& real_rptf = reporting_mode_features[real_data[1] - WM_REPORT_CORE];
|
||||
|
||||
// force same report type from real-wiimote
|
||||
if (&real_rptf != &rptf)
|
||||
rptf_size = 0;
|
||||
|
||||
// core
|
||||
// mix real-buttons with emu-buttons in the status struct, and in the report
|
||||
if (real_rptf.core && rptf.core)
|
||||
{
|
||||
m_status.buttons |= *(wm_core*)(real_data + real_rptf.core);
|
||||
*(wm_core*)(data + rptf.core) = m_status.buttons;
|
||||
}
|
||||
|
||||
// accel
|
||||
// use real-accel data always i guess
|
||||
if (real_rptf.accel && rptf.accel)
|
||||
memcpy(data + rptf.accel, real_data + real_rptf.accel, sizeof(wm_accel));
|
||||
|
||||
// ir
|
||||
// TODO
|
||||
|
||||
// ext
|
||||
// use real-ext data if an emu-extention isn't chosen
|
||||
if (real_rptf.ext && rptf.ext && (0 == m_extension->switch_extension))
|
||||
memcpy(data + rptf.ext, real_data + real_rptf.ext, sizeof(wm_extension));
|
||||
}
|
||||
else if (WM_ACK_DATA != real_data[1] || m_extension->active_extension > 0)
|
||||
rptf_size = 0;
|
||||
else
|
||||
// use real-acks if an emu-extension isn't chosen
|
||||
rptf_size = -1;
|
||||
break;
|
||||
|
||||
// use all status reports, after modification of the extension bit
|
||||
case WM_STATUS_REPORT :
|
||||
//if (m_extension->switch_extension)
|
||||
//((wm_status_report*)(real_data + 2))->extension = (m_extension->active_extension > 0);
|
||||
if (m_extension->active_extension)
|
||||
((wm_status_report*)(real_data + 2))->extension = 1;
|
||||
rptf_size = -1;
|
||||
break;
|
||||
|
||||
// use all read-data replies
|
||||
case WM_READ_DATA_REPLY:
|
||||
rptf_size = -1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// copy over report from real-wiimote
|
||||
if (-1 == rptf_size)
|
||||
{
|
||||
memcpy(data, real_data, MAX_PAYLOAD);
|
||||
rptf_size = MAX_PAYLOAD;
|
||||
}
|
||||
|
||||
if (real_data != g_wiimotes[m_index]->m_last_data_report)
|
||||
delete[] real_data;
|
||||
}
|
||||
}
|
||||
g_refresh_critsec.Leave();
|
||||
}
|
||||
|
||||
// ----ir----
|
||||
if (rpt.ir)
|
||||
{
|
||||
float xx = 10000, yy = 0, zz = 0;
|
||||
unsigned int x[4], y[4];
|
||||
// don't send a data report if auto reporting is off
|
||||
if (false == m_reporting_auto && data[2] >= WM_REPORT_CORE)
|
||||
return;
|
||||
|
||||
if (is_focus)
|
||||
{
|
||||
m_ir->GetState(&xx, &yy, &zz, true);
|
||||
UDPTLayer::GetIR(m_udp, &xx, &yy, &zz);
|
||||
|
||||
float tx, ty;
|
||||
m_tilt->GetState(&tx, &ty, 0, 1, false);
|
||||
|
||||
// TODO: fix tilt math stuff
|
||||
|
||||
const float rtan = tan(0.0f/*tx*/); // disabled cause my math fails
|
||||
const float rsin = sin(rtan);
|
||||
const float rcos = cos(rtan);
|
||||
|
||||
{
|
||||
const float xxx = (xx * -256 * 0.95f);
|
||||
const float yyy = (yy * -256 * 0.90f);
|
||||
|
||||
xx = 512 + xxx * rcos + yyy * rsin;
|
||||
yy = 490 + yyy * rcos + xxx * rsin;
|
||||
}
|
||||
|
||||
const unsigned int distance = (unsigned int)(200 + 100 * zz);
|
||||
|
||||
x[0] = (unsigned int)(xx - distance * rcos);
|
||||
x[1] = (unsigned int)(xx + distance * rcos);
|
||||
x[2] = (unsigned int)(xx - 1.2f * distance * rcos);
|
||||
x[3] = (unsigned int)(xx + 1.2f * distance * rcos);
|
||||
|
||||
y[0] = (unsigned int)(yy - 0.75 * distance * rsin);
|
||||
y[1] = (unsigned int)(yy + 0.75 * distance * rsin);
|
||||
y[2] = (unsigned int)(yy - 0.75 * 1.2f * distance * rsin);
|
||||
y[3] = (unsigned int)(yy + 0.75 * 1.2f * distance * rsin);
|
||||
|
||||
}
|
||||
|
||||
// Fill report with valid data when full handshake was done
|
||||
if (m_reg_ir->data[0x30])
|
||||
// ir mode
|
||||
switch (m_reg_ir->mode)
|
||||
{
|
||||
// basic
|
||||
case 1 :
|
||||
{
|
||||
memset(data + rpt.ir, 0xFF, 10);
|
||||
wm_ir_basic* const irdata = (wm_ir_basic*)(data + rpt.ir);
|
||||
for (unsigned int i=0; i<2; ++i)
|
||||
{
|
||||
if (x[i*2] < 1024 && y[i*2] < 768)
|
||||
{
|
||||
irdata[i].x1 = u8(x[i*2]);
|
||||
irdata[i].x1hi = x[i*2] >> 8;
|
||||
|
||||
irdata[i].y1 = u8(y[i*2]);
|
||||
irdata[i].y1hi = y[i*2] >> 8;
|
||||
}
|
||||
if (x[i*2+1] < 1024 && y[i*2+1] < 768)
|
||||
{
|
||||
irdata[i].x2 = u8(x[i*2+1]);
|
||||
irdata[i].x2hi = x[i*2+1] >> 8;
|
||||
|
||||
irdata[i].y2 = u8(y[i*2+1]);
|
||||
irdata[i].y2hi = y[i*2+1] >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
// extended
|
||||
case 3 :
|
||||
{
|
||||
memset(data + rpt.ir, 0xFF, 12);
|
||||
wm_ir_extended* const irdata = (wm_ir_extended*)(data + rpt.ir);
|
||||
for (unsigned int i=0; i<4; ++i)
|
||||
if (x[i] < 1024 && y[i] < 768)
|
||||
{
|
||||
irdata[i].x = u8(x[i]);
|
||||
irdata[i].xhi = x[i] >> 8;
|
||||
|
||||
irdata[i].y = u8(y[i]);
|
||||
irdata[i].yhi = y[i] >> 8;
|
||||
|
||||
irdata[i].size = 10;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// full
|
||||
case 5 :
|
||||
// UNSUPPORTED
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----extension----
|
||||
if (rpt.ext)
|
||||
{
|
||||
m_extension->GetState(data + rpt.ext, is_focus);
|
||||
|
||||
// i dont think anything accesses the extension data like this, but ill support it. Indeed, commercial games don't do this.
|
||||
// i think it should be unencrpyted in the register, encrypted when read.
|
||||
memcpy(m_reg_ext->controller_data, data + rpt.ext, sizeof(wm_extension));
|
||||
|
||||
if (0xAA == m_reg_ext->encryption) {
|
||||
wiimote_encrypt(&m_ext_key, data + rpt.ext, 0x00, sizeof(wm_extension));
|
||||
}
|
||||
}
|
||||
// send data report
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel( m_index, m_reporting_channel, data, rpt.size );
|
||||
if (rptf_size)
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_reporting_channel, data, rptf_size);
|
||||
}
|
||||
|
||||
void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size)
|
||||
|
@ -587,7 +693,7 @@ void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size
|
|||
// this all good?
|
||||
m_reporting_channel = _channelID;
|
||||
|
||||
hid_packet* hidp = (hid_packet*)_pData;
|
||||
const hid_packet* const hidp = (hid_packet*)_pData;
|
||||
|
||||
INFO_LOG(WIIMOTE, "Emu ControlChannel (page: %i, type: 0x%02x, param: 0x%02x)", m_index, hidp->type, hidp->param);
|
||||
|
||||
|
@ -606,7 +712,7 @@ void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size
|
|||
{
|
||||
// AyuanX: My experiment shows Control Channel is never used
|
||||
// shuffle2: but homebrew uses this, so we'll do what we must :)
|
||||
HidOutputReport(_channelID, (wm_report*)hidp->data);
|
||||
HidOutputReport((wm_report*)hidp->data);
|
||||
|
||||
u8 handshake = HID_HANDSHAKE_SUCCESS;
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, _channelID, &handshake, 1);
|
||||
|
@ -631,7 +737,7 @@ void Wiimote::InterruptChannel(const u16 _channelID, const void* _pData, u32 _Si
|
|||
// this all good?
|
||||
m_reporting_channel = _channelID;
|
||||
|
||||
hid_packet* hidp = (hid_packet*)_pData;
|
||||
const hid_packet* const hidp = (hid_packet*)_pData;
|
||||
|
||||
switch (hidp->type)
|
||||
{
|
||||
|
@ -640,8 +746,26 @@ void Wiimote::InterruptChannel(const u16 _channelID, const void* _pData, u32 _Si
|
|||
{
|
||||
case HID_PARAM_OUTPUT :
|
||||
{
|
||||
wm_report* sr = (wm_report*)hidp->data;
|
||||
HidOutputReport(_channelID, sr);
|
||||
const wm_report* const sr = (wm_report*)hidp->data;
|
||||
|
||||
if (WIIMOTE_SRC_HYBRID == g_wiimote_sources[m_index])
|
||||
{
|
||||
switch (sr->wm)
|
||||
{
|
||||
// these two types are handled in RequestStatus() & ReadData()
|
||||
case WM_REQUEST_STATUS :
|
||||
case WM_READ_DATA :
|
||||
break;
|
||||
|
||||
default :
|
||||
WiimoteReal::InterruptChannel(m_index, _channelID, _pData, _Size);
|
||||
break;
|
||||
}
|
||||
|
||||
HidOutputReport(sr, m_extension->switch_extension > 0);
|
||||
}
|
||||
else
|
||||
HidOutputReport(sr);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -762,7 +886,7 @@ void Wiimote::Register::Read( size_t address, void* dst, size_t length )
|
|||
}
|
||||
|
||||
// TODO: i need to test this
|
||||
void Wiimote::Register::Write( size_t address, void* src, size_t length )
|
||||
void Wiimote::Register::Write( size_t address, const void* src, size_t length )
|
||||
{
|
||||
iterator i = begin();
|
||||
const const_iterator e = end();
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// just used to get the OpenAL includes :p
|
||||
//#include <OpenALStream.h>
|
||||
|
||||
#include <ControllerEmu.h>
|
||||
#include "ControllerEmu.h"
|
||||
#include "ChunkFile.h"
|
||||
|
||||
#include "WiimoteHid.h"
|
||||
|
@ -32,6 +32,13 @@ extern SWiimoteInitialize g_WiimoteInitialize;
|
|||
namespace WiimoteEmu
|
||||
{
|
||||
|
||||
struct ReportFeatures
|
||||
{
|
||||
u8 core, accel, ir, ext, size;
|
||||
};
|
||||
|
||||
extern const ReportFeatures reporting_mode_features[];
|
||||
|
||||
void EmulateShake(u8* const accel_data
|
||||
, ControllerEmu::Buttons* const buttons_group
|
||||
, unsigned int* const shake_step);
|
||||
|
@ -77,6 +84,19 @@ public:
|
|||
|
||||
void LoadDefaults(const ControllerInterface& ciface);
|
||||
|
||||
protected:
|
||||
bool Step();
|
||||
void HidOutputReport(const wm_report* const sr, const bool send_ack = true);
|
||||
void HandleExtensionSwap();
|
||||
|
||||
void GetCoreData(u8* const data);
|
||||
void GetAccelData(u8* const data);
|
||||
void GetIRData(u8* const data);
|
||||
void GetExtData(u8* const data);
|
||||
|
||||
bool HaveExtension() const { return m_extension->active_extension > 0; }
|
||||
bool WantExtension() const { return m_extension->switch_extension != 0; }
|
||||
|
||||
private:
|
||||
struct ReadRequest
|
||||
{
|
||||
|
@ -87,14 +107,12 @@ private:
|
|||
|
||||
void Reset();
|
||||
|
||||
void ReportMode(const u16 _channelID, wm_report_mode* dr);
|
||||
void HidOutputReport(const u16 _channelID, wm_report* sr);
|
||||
void SendAck(const u16 _channelID, u8 _reportID);
|
||||
void RequestStatus(const u16 _channelID, wm_request_status* rs = NULL);
|
||||
|
||||
void WriteData(const u16 _channelID, wm_write_data* wd);
|
||||
void ReadData(const u16 _channelID, wm_read_data* rd);
|
||||
void SendReadDataReply(const u16 _channelID, ReadRequest& _request);
|
||||
void ReportMode(const wm_report_mode* const dr);
|
||||
void SendAck(const u8 _reportID);
|
||||
void RequestStatus(const wm_request_status* const rs = NULL);
|
||||
void ReadData(const wm_read_data* const rd);
|
||||
void WriteData(const wm_write_data* const wd);
|
||||
void SendReadDataReply(ReadRequest& _request);
|
||||
|
||||
#ifdef USE_WIIMOTE_EMU_SPEAKER
|
||||
void SpeakerData(wm_speaker_data* sd);
|
||||
|
@ -131,7 +149,7 @@ private:
|
|||
class Register : public std::map< size_t, std::vector<u8> >
|
||||
{
|
||||
public:
|
||||
void Write( size_t address, void* src, size_t length );
|
||||
void Write( size_t address, const void* src, size_t length );
|
||||
void Read( size_t address, void* dst, size_t length );
|
||||
|
||||
} m_register;
|
||||
|
|
|
@ -205,7 +205,7 @@ struct wm_read_data {
|
|||
u8 space : 2; //see WM_SPACE_*
|
||||
u8 : 5;
|
||||
u8 address[3];
|
||||
u8 size[2];
|
||||
u16 size;
|
||||
};
|
||||
|
||||
#define WM_SPACE_EEPROM 0
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
#include "pluginspecs_wiimote.h"
|
||||
|
||||
#include "WiimoteReal/WiimoteReal.h"
|
||||
#include "WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "ControllerInterface/ControllerInterface.h"
|
||||
#include "WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#if defined(HAVE_WX) && HAVE_WX
|
||||
#include "WiimoteConfigDiag.h"
|
||||
|
@ -127,15 +127,11 @@ void InitPlugin( void* const hwnd )
|
|||
//
|
||||
void Wiimote_ControlChannel(int _number, u16 _channelID, const void* _pData, u32 _Size)
|
||||
{
|
||||
switch (g_wiimote_sources[_number])
|
||||
{
|
||||
case WIIMOTE_SRC_REAL :
|
||||
if (WIIMOTE_SRC_EMU & g_wiimote_sources[_number])
|
||||
((WiimoteEmu::Wiimote*)g_plugin.controllers[_number])->ControlChannel(_channelID, _pData, _Size);
|
||||
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[_number])
|
||||
WiimoteReal::ControlChannel(_number, _channelID, _pData, _Size);
|
||||
break;
|
||||
case WIIMOTE_SRC_EMU :
|
||||
((WiimoteEmu::Wiimote*)g_plugin.controllers[ _number ])->ControlChannel( _channelID, _pData, _Size );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// __________________________________________________________________________________________________
|
||||
|
@ -169,15 +165,10 @@ unsigned int Wiimote_UnPairWiimotes(void)
|
|||
//
|
||||
void Wiimote_InterruptChannel(int _number, u16 _channelID, const void* _pData, u32 _Size)
|
||||
{
|
||||
switch (g_wiimote_sources[_number])
|
||||
{
|
||||
case WIIMOTE_SRC_REAL :
|
||||
if (WIIMOTE_SRC_EMU & g_wiimote_sources[_number])
|
||||
((WiimoteEmu::Wiimote*)g_plugin.controllers[_number])->InterruptChannel(_channelID, _pData, _Size);
|
||||
else
|
||||
WiimoteReal::InterruptChannel(_number, _channelID, _pData, _Size);
|
||||
break;
|
||||
case WIIMOTE_SRC_EMU :
|
||||
((WiimoteEmu::Wiimote*)g_plugin.controllers[ _number ])->InterruptChannel( _channelID, _pData, _Size );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// __________________________________________________________________________________________________
|
||||
|
@ -201,15 +192,10 @@ void Wiimote_Update(int _number)
|
|||
}
|
||||
_last_number = _number;
|
||||
|
||||
switch (g_wiimote_sources[_number])
|
||||
{
|
||||
case WIIMOTE_SRC_REAL :
|
||||
WiimoteReal::Update(_number);
|
||||
break;
|
||||
case WIIMOTE_SRC_EMU :
|
||||
if (WIIMOTE_SRC_EMU & g_wiimote_sources[_number])
|
||||
((WiimoteEmu::Wiimote*)g_plugin.controllers[_number])->Update();
|
||||
break;
|
||||
}
|
||||
else
|
||||
WiimoteReal::Update(_number);
|
||||
|
||||
g_plugin.controls_crit.Leave();
|
||||
}
|
||||
|
|
|
@ -19,10 +19,8 @@
|
|||
|
||||
#include "Common.h"
|
||||
#include "IniFile.h"
|
||||
#include "Thread.h"
|
||||
#include "StringUtil.h"
|
||||
#include "Timer.h"
|
||||
#include "FifoQueue.h"
|
||||
#include "pluginspecs_wiimote.h"
|
||||
|
||||
#include "wiiuse.h"
|
||||
|
@ -46,45 +44,13 @@ Common::CriticalSection g_refresh_critsec, g_wiimote_critsec;
|
|||
|
||||
THREAD_RETURN WiimoteThreadFunc(void* arg);
|
||||
|
||||
class Wiimote
|
||||
{
|
||||
public:
|
||||
Wiimote(wiimote_t* const wm, const unsigned int index);
|
||||
~Wiimote();
|
||||
|
||||
void ControlChannel(const u16 channel, const void* const data, const u32 size);
|
||||
void InterruptChannel(const u16 channel, const void* const data, const u32 size);
|
||||
void Update();
|
||||
|
||||
void Read();
|
||||
void Write();
|
||||
void Disconnect();
|
||||
void DisableDataReporting();
|
||||
|
||||
void SendPacket(const u8 rpt_id, const void* const data, const unsigned int size);
|
||||
|
||||
// pointer to data, and size of data
|
||||
typedef std::pair<u8*,u8> Report;
|
||||
|
||||
private:
|
||||
void ClearReports();
|
||||
|
||||
wiimote_t* const m_wiimote;
|
||||
const unsigned int m_index;
|
||||
|
||||
u16 m_channel;
|
||||
u8 m_last_data_report[MAX_PAYLOAD];
|
||||
bool m_last_data_report_valid;
|
||||
|
||||
Common::FifoQueue<u8*> m_read_reports;
|
||||
Common::FifoQueue<Report> m_write_reports;
|
||||
};
|
||||
Wiimote *g_wiimotes[4];
|
||||
|
||||
Wiimote::Wiimote(wiimote_t* const wm, const unsigned int index)
|
||||
: m_wiimote(wm)
|
||||
, m_index(index)
|
||||
, m_channel(0)
|
||||
, m_last_data_report_valid(false)
|
||||
, m_last_data_report(NULL)
|
||||
{
|
||||
// disable reporting
|
||||
DisableDataReporting();
|
||||
|
@ -100,7 +66,7 @@ Wiimote::Wiimote(wiimote_t* const wm, const unsigned int index)
|
|||
//SendPacket(g_wiimotes_from_wiiuse[i], WM_LEDS, &rpt, sizeof(rpt));
|
||||
//}
|
||||
|
||||
// Rumble briefly
|
||||
// Rumble briefly, this is a bad spot for the rumble
|
||||
wiiuse_rumble(m_wiimote, 1);
|
||||
SLEEP(200);
|
||||
wiiuse_rumble(m_wiimote, 0);
|
||||
|
@ -145,19 +111,26 @@ void Wiimote::SendPacket(const u8 rpt_id, const void* const data, const unsigned
|
|||
|
||||
void Wiimote::DisableDataReporting()
|
||||
{
|
||||
wm_report_mode rpt = wm_report_mode();
|
||||
wm_report_mode rpt;
|
||||
rpt.mode = WM_REPORT_CORE;
|
||||
rpt.all_the_time = 0;
|
||||
rpt.continuous = 0;
|
||||
rpt.rumble = 0;
|
||||
SendPacket(WM_REPORT_MODE, &rpt, sizeof(rpt));
|
||||
}
|
||||
|
||||
void Wiimote::ClearReports()
|
||||
{
|
||||
m_last_data_report_valid = false;
|
||||
if (m_last_data_report)
|
||||
{
|
||||
delete[] m_last_data_report;
|
||||
m_last_data_report = NULL;
|
||||
}
|
||||
Report rpt;
|
||||
while (m_read_reports.Pop(rpt.first))
|
||||
delete[] rpt.first;
|
||||
while (m_write_reports.Pop(rpt))
|
||||
delete[] rpt.first;
|
||||
delete rpt.first;
|
||||
while (m_read_reports.Pop(rpt.first))
|
||||
delete rpt.first;
|
||||
}
|
||||
|
||||
void Wiimote::ControlChannel(const u16 channel, const void* const data, const u32 size)
|
||||
|
@ -177,7 +150,8 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const
|
|||
while (wiiuse_io_read(m_wiimote)) {};
|
||||
|
||||
// request status
|
||||
wm_request_status rpt = wm_request_status();
|
||||
wm_request_status rpt;
|
||||
rpt.rumble = 0;
|
||||
SendPacket(WM_REQUEST_STATUS, &rpt, sizeof(rpt));
|
||||
}
|
||||
|
||||
|
@ -187,6 +161,24 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const
|
|||
rpt.first = new u8[size];
|
||||
rpt.second = (u8)size;
|
||||
memcpy(rpt.first, (u8*)data, size);
|
||||
|
||||
// some hax, since we just send the last data report to Dolphin on each Update() call
|
||||
// , make the wiimote only send updated data reports when data changes
|
||||
// == less bt traffic, eliminates some unneeded packets
|
||||
if (WM_REPORT_MODE == ((u8*)data)[1])
|
||||
{
|
||||
// also delete the last data report
|
||||
if (m_last_data_report)
|
||||
{
|
||||
delete[] m_last_data_report;
|
||||
m_last_data_report = NULL;
|
||||
}
|
||||
|
||||
// nice var names :p, this seems to be this one
|
||||
((wm_report_mode*)(rpt.first + 2))->all_the_time = false;
|
||||
//((wm_report_mode*)(data + 2))->continuous = false;
|
||||
}
|
||||
|
||||
m_write_reports.Push(rpt);
|
||||
}
|
||||
|
||||
|
@ -198,19 +190,10 @@ void Wiimote::Read()
|
|||
|
||||
if (wiiuse_io_read(m_wiimote))
|
||||
{
|
||||
// a data report, save it
|
||||
if (m_wiimote->event_buf[1] >= 0x30)
|
||||
{
|
||||
memcpy(m_last_data_report, m_wiimote->event_buf, MAX_PAYLOAD);
|
||||
m_last_data_report_valid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// some other report, add it to queue
|
||||
u8* const rpt = new u8[MAX_PAYLOAD];
|
||||
memcpy(rpt, m_wiimote->event_buf, MAX_PAYLOAD);
|
||||
m_read_reports.Push(rpt);
|
||||
}
|
||||
// add it to queue
|
||||
u8* const rpt = new u8[MAX_PAYLOAD];
|
||||
memcpy(rpt, m_wiimote->event_buf, MAX_PAYLOAD);
|
||||
m_read_reports.Push(rpt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,20 +207,37 @@ void Wiimote::Write()
|
|||
}
|
||||
}
|
||||
|
||||
// returns the next report that should be sent
|
||||
u8* Wiimote::ProcessReadQueue()
|
||||
{
|
||||
// pop through the queued reports
|
||||
u8* rpt = m_last_data_report;
|
||||
while (m_read_reports.Pop(rpt))
|
||||
{
|
||||
// a data report
|
||||
if (rpt[1] >= WM_REPORT_CORE)
|
||||
m_last_data_report = rpt;
|
||||
// some other kind of report
|
||||
else
|
||||
return rpt;
|
||||
}
|
||||
|
||||
// the queue was empty, or there were only data reports
|
||||
return rpt;
|
||||
}
|
||||
|
||||
void Wiimote::Update()
|
||||
{
|
||||
// do we have some queued reports
|
||||
u8* rpt;
|
||||
if (m_read_reports.Pop(rpt))
|
||||
{
|
||||
// pop through the queued reports
|
||||
u8* const rpt = ProcessReadQueue();
|
||||
|
||||
// send the report
|
||||
if (rpt)
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_channel, rpt, MAX_PAYLOAD);
|
||||
|
||||
// delete the data if it isn't also the last data rpt
|
||||
if (rpt != m_last_data_report)
|
||||
delete[] rpt;
|
||||
}
|
||||
else if (m_last_data_report_valid)
|
||||
{
|
||||
// otherwise send the last data report, if there is one
|
||||
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_channel, m_last_data_report, MAX_PAYLOAD);
|
||||
}
|
||||
}
|
||||
|
||||
void Wiimote::Disconnect()
|
||||
|
@ -248,14 +248,14 @@ void Wiimote::Disconnect()
|
|||
DisableDataReporting();
|
||||
|
||||
// clear queue
|
||||
ClearReports();
|
||||
u8 *rpt;
|
||||
while (m_read_reports.Pop(rpt))
|
||||
delete rpt;
|
||||
|
||||
// clear out wiiuse queue, or maybe not, silly? idk
|
||||
while (wiiuse_io_read(m_wiimote)) {};
|
||||
}
|
||||
|
||||
Wiimote* g_wiimotes[4];
|
||||
|
||||
void LoadSettings()
|
||||
{
|
||||
std::string ini_filename = (std::string(File::GetUserPath(D_CONFIG_IDX)) + g_plugin.ini_name + ".ini" );
|
||||
|
@ -284,7 +284,7 @@ unsigned int Initialize()
|
|||
// only call wiiuse_find with the number of slots configured for real wiimotes
|
||||
unsigned int wanted_wiimotes = 0;
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (WIIMOTE_SRC_REAL == g_wiimote_sources[i])
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i])
|
||||
++wanted_wiimotes;
|
||||
|
||||
// don't bother initializing wiiuse if we don't want any real wiimotes
|
||||
|
@ -320,14 +320,11 @@ unsigned int Initialize()
|
|||
g_wiimote_critsec.Enter(); // enter
|
||||
|
||||
// create real wiimote class instances, assign wiimotes
|
||||
|
||||
for (unsigned int i = 0, w = 0; i<MAX_WIIMOTES && w<g_wiimotes_found; ++i)
|
||||
{
|
||||
if (WIIMOTE_SRC_REAL != g_wiimote_sources[i])
|
||||
continue;
|
||||
|
||||
// create/assign wiimote
|
||||
g_wiimotes[i] = new Wiimote(g_wiimotes_from_wiiuse[w++], i);
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i])
|
||||
g_wiimotes[i] = new Wiimote(g_wiimotes_from_wiiuse[w++], i);
|
||||
}
|
||||
|
||||
g_wiimote_critsec.Leave(); // leave
|
||||
|
@ -383,29 +380,21 @@ void Shutdown(void)
|
|||
|
||||
void Refresh() // this gets called from the GUI thread
|
||||
{
|
||||
g_refresh_critsec.Enter();
|
||||
|
||||
#ifdef __linux__
|
||||
// make sure real wiimotes have been initialized
|
||||
if (!g_real_wiimotes_initialized)
|
||||
{
|
||||
g_refresh_critsec.Leave();
|
||||
Initialize();
|
||||
return;
|
||||
}
|
||||
|
||||
// find the number of slots configured for real wiimotes
|
||||
unsigned int wanted_wiimotes = 0;
|
||||
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
|
||||
if (g_wiimote_sources[i] == WIIMOTE_SRC_REAL)
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i])
|
||||
++wanted_wiimotes;
|
||||
|
||||
// don't scan for wiimotes if we don't want any more
|
||||
if (wanted_wiimotes <= g_wiimotes_found)
|
||||
{
|
||||
g_refresh_critsec.Leave();
|
||||
return;
|
||||
}
|
||||
|
||||
// scan for wiimotes
|
||||
unsigned int num_wiimotes = wiiuse_find(g_wiimotes_from_wiiuse, wanted_wiimotes, 5);
|
||||
|
@ -416,58 +405,41 @@ void Refresh() // this gets called from the GUI thread
|
|||
|
||||
DEBUG_LOG(WIIMOTE, "Connected to %i additional Real Wiimotes", num_new_wiimotes);
|
||||
|
||||
g_refresh_critsec.Enter();
|
||||
g_wiimote_critsec.Enter(); // enter
|
||||
|
||||
// create real wiimote class instances, and assign wiimotes for the new wiimotes
|
||||
for (unsigned int i = g_wiimotes_found, w = g_wiimotes_found;
|
||||
i < MAX_WIIMOTES && w < num_wiimotes; ++i)
|
||||
{
|
||||
if (g_wiimote_sources[i] != WIIMOTE_SRC_REAL || g_wiimotes[i] != NULL)
|
||||
continue;
|
||||
|
||||
// create/assign wiimote
|
||||
g_wiimotes[i] = new Wiimote(g_wiimotes_from_wiiuse[w++], i);
|
||||
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i] && NULL == g_wiimotes[i])
|
||||
g_wiimotes[i] = new Wiimote(g_wiimotes_from_wiiuse[w++], i);
|
||||
}
|
||||
g_wiimotes_found = num_wiimotes;
|
||||
|
||||
g_wiimote_critsec.Leave(); // leave
|
||||
g_refresh_critsec.Leave();
|
||||
|
||||
#else // windows/ OSX
|
||||
g_refresh_critsec.Enter();
|
||||
|
||||
// should be fine i think
|
||||
Shutdown();
|
||||
Initialize();
|
||||
#endif
|
||||
|
||||
g_refresh_critsec.Leave();
|
||||
#endif
|
||||
}
|
||||
|
||||
void InterruptChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size)
|
||||
{
|
||||
u8* data = (u8*)_pData;
|
||||
|
||||
// some hax, since we just send the last data report to Dolphin on each Update() call
|
||||
// , make the wiimote only send updated data reports when data changes
|
||||
// == less bt traffic, eliminates some unneeded packets
|
||||
if (WM_REPORT_MODE == ((u8*)_pData)[1])
|
||||
{
|
||||
// I dont wanna write on the const *_pData
|
||||
data = new u8[_Size];
|
||||
memcpy(data, _pData, _Size);
|
||||
|
||||
// nice var names :p, this seems to be this one
|
||||
((wm_report_mode*)(data + 2))->all_the_time = false;
|
||||
//((wm_report_mode*)(data + 2))->continuous = false;
|
||||
}
|
||||
|
||||
g_refresh_critsec.Enter();
|
||||
|
||||
if (g_wiimotes[_WiimoteNumber])
|
||||
g_wiimotes[_WiimoteNumber]->InterruptChannel(_channelID, data, _Size);
|
||||
g_wiimotes[_WiimoteNumber]->InterruptChannel(_channelID, _pData, _Size);
|
||||
|
||||
g_refresh_critsec.Leave();
|
||||
|
||||
if (data != _pData)
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
void ControlChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size)
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
#include "wiiuse.h"
|
||||
#include "ChunkFile.h"
|
||||
#include "Thread.h"
|
||||
#include "FifoQueue.h"
|
||||
#include "../WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "../../InputCommon/Src/InputConfig.h"
|
||||
|
||||
|
@ -33,13 +36,53 @@ extern SWiimoteInitialize g_WiimoteInitialize;
|
|||
enum
|
||||
{
|
||||
WIIMOTE_SRC_NONE = 0,
|
||||
WIIMOTE_SRC_EMU,
|
||||
WIIMOTE_SRC_REAL,
|
||||
WIIMOTE_SRC_EMU = 1,
|
||||
WIIMOTE_SRC_REAL = 2,
|
||||
WIIMOTE_SRC_HYBRID = 3, // emu + real
|
||||
};
|
||||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
|
||||
class Wiimote
|
||||
{
|
||||
friend class WiimoteEmu::Wiimote;
|
||||
public:
|
||||
Wiimote(wiimote_t* const wm, const unsigned int index);
|
||||
~Wiimote();
|
||||
|
||||
void ControlChannel(const u16 channel, const void* const data, const u32 size);
|
||||
void InterruptChannel(const u16 channel, const void* const data, const u32 size);
|
||||
void Update();
|
||||
|
||||
u8* ProcessReadQueue();
|
||||
|
||||
void Read();
|
||||
void Write();
|
||||
void Disconnect();
|
||||
void DisableDataReporting();
|
||||
|
||||
void SendPacket(const u8 rpt_id, const void* const data, const unsigned int size);
|
||||
|
||||
// pointer to data, and size of data
|
||||
typedef std::pair<u8*,u8> Report;
|
||||
|
||||
protected:
|
||||
u8 *m_last_data_report;
|
||||
u16 m_channel;
|
||||
|
||||
private:
|
||||
void ClearReports();
|
||||
|
||||
wiimote_t* const m_wiimote;
|
||||
const unsigned int m_index;
|
||||
Common::FifoQueue<u8*> m_read_reports;
|
||||
Common::FifoQueue<Report> m_write_reports;
|
||||
};
|
||||
|
||||
extern Common::CriticalSection g_refresh_critsec;
|
||||
extern Wiimote *g_wiimotes[4];
|
||||
|
||||
unsigned int Initialize();
|
||||
void Shutdown();
|
||||
void Refresh();
|
||||
|
|
Loading…
Reference in New Issue