Implement the rest of the wiimote input reports.

This commit is contained in:
Jordan Woyak 2018-11-25 18:28:32 -06:00
parent 10c2101e72
commit 0b4329e077
6 changed files with 160 additions and 70 deletions

View File

@ -63,7 +63,7 @@ union wm_buttons // also just called "core data"
u8 down : 1;
u8 up : 1;
u8 plus : 1;
u8 acc_x_lsb : 2; // LSB of accelerometer (10 bits in total)
u8 acc_bits : 2;
u8 unknown : 1;
u8 two : 1;
@ -71,8 +71,7 @@ union wm_buttons // also just called "core data"
u8 b : 1;
u8 a : 1;
u8 minus : 1;
u8 acc_y_lsb : 1; // LSB of accelerometer (9 bits in total)
u8 acc_z_lsb : 1; // LSB of accelerometer (9 bits in total)
u8 acc_bits2 : 2;
u8 home : 1;
};
};
@ -109,6 +108,23 @@ struct wm_ir_extended
};
static_assert(sizeof(wm_ir_extended) == 3, "Wrong size");
// Nine bytes for one object
// first 3 bytes are the same as extended
struct wm_ir_full : wm_ir_extended
{
u8 xmin : 7;
u8 : 1;
u8 ymin : 7;
u8 : 1;
u8 xmax : 7;
u8 : 1;
u8 ymax : 7;
u8 : 1;
u8 zero;
u8 intensity;
};
static_assert(sizeof(wm_ir_full) == 9, "Wrong size");
// Nunchuk
union wm_nc_core
{

View File

@ -46,10 +46,6 @@ void Wiimote::ReportMode(const wm_report_mode* const dr)
WARN_LOG(WIIMOTE, "Game requested invalid report mode: 0x%02x", dr->mode);
return;
}
else if (dr->mode > RT_REPORT_CORE_ACCEL_IR10_EXT6)
{
PanicAlert("Wiimote: Unsupported Reporting mode: 0x%02x", dr->mode);
}
// TODO: A real wiimote sends a report immediately.
// even on REPORT_CORE and continuous off when the buttons haven't changed.

View File

@ -13,12 +13,12 @@
#include <mutex>
#include <numeric>
#include "Common/BitUtils.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
#include "Common/MathUtil.h"
#include "Common/MsgHandler.h"
#include "Common/BitUtils.h"
#include "Core/Config/SYSCONFSettings.h"
#include "Core/Config/WiimoteInputSettings.h"
@ -101,11 +101,16 @@ static const ReportFeatures reporting_mode_features[] = {
{2, 0, 10, 9, 23},
// 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes
{2, 3, 10, 6, 23},
// UNSUPPORTED (but should be easy enough to implement):
// Ugly padding members so 0x3d,3e,3f are properly placed in the array:
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
// 0x3d: 21 Extension Bytes
{0, 0, 0, 21, 23},
// 0x3e / 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes
{0, 0, 0, 0, 23},
{2, 1, 0, 18, 23},
};
// Used for extension calibration data:
@ -335,13 +340,13 @@ void Wiimote::Reset()
memcpy(&m_motion_plus_logic.reg_data.ext_identifier, motion_plus_id, sizeof(motion_plus_id));
// calibration hackery
static const u8 c1[16] = {0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, 0xcf, 0xf0, 0x31, 0xad,
0xc8, 0x0b, 0x5e, 0x39};
static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60, 0xc9, 0xf5, 0x37, 0xc1,
0x2d, 0xe9, 0x15, 0x8d};
static const u8 c1[16] = {0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c,
0xcf, 0xf0, 0x31, 0xad, 0xc8, 0x0b, 0x5e, 0x39};
static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60,
0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d};
//std::copy(std::begin(c1), std::end(c1), m_motion_plus_logic.reg_data.calibration_data);
//std::copy(std::begin(c2), std::end(c2), m_motion_plus_logic.reg_data.calibration_data + 0x10);
// std::copy(std::begin(c1), std::end(c1), m_motion_plus_logic.reg_data.calibration_data);
// std::copy(std::begin(c2), std::end(c2), m_motion_plus_logic.reg_data.calibration_data + 0x10);
// status
memset(&m_status, 0, sizeof(m_status));
@ -610,7 +615,8 @@ void Wiimote::GetButtonData(u8* const data)
reinterpret_cast<wm_buttons*>(data)->hex |= m_status.buttons.hex;
}
void Wiimote::GetAccelData(u8* const data)
// Produces 10-bit precision values.
void Wiimote::GetAccelData(s16* x, s16* y, s16* z)
{
const bool sideways_modifier_toggle = m_hotkeys->getSettingsModifier()[0];
const bool upright_modifier_toggle = m_hotkeys->getSettingsModifier()[1];
@ -661,26 +667,14 @@ void Wiimote::GetAccelData(u8* const data)
EmulateDynamicShake(&m_accel, m_shake_dynamic_data, m_shake_dynamic, shake_config,
m_shake_dynamic_step.data());
// TODO: kill these ugly looking offsets
wm_accel& accel = *reinterpret_cast<wm_accel*>(data + 4);
wm_buttons& core = *reinterpret_cast<wm_buttons*>(data + 2);
// We now use 2 bits more precision, so multiply by 4 before converting to int
s16 x = (s16)(4 * (m_accel.x * ACCEL_RANGE + ACCEL_ZERO_G));
s16 y = (s16)(4 * (m_accel.y * ACCEL_RANGE + ACCEL_ZERO_G));
s16 z = (s16)(4 * (m_accel.z * ACCEL_RANGE + ACCEL_ZERO_G));
*x = (s16)(4 * (m_accel.x * ACCEL_RANGE + ACCEL_ZERO_G));
*y = (s16)(4 * (m_accel.y * ACCEL_RANGE + ACCEL_ZERO_G));
*z = (s16)(4 * (m_accel.z * ACCEL_RANGE + ACCEL_ZERO_G));
x = MathUtil::Clamp<s16>(x, 0, 0x3ff);
y = MathUtil::Clamp<s16>(y, 0, 0x3ff);
z = MathUtil::Clamp<s16>(z, 0, 0x3ff);
accel.x = (x >> 2) & 0xFF;
accel.y = (y >> 2) & 0xFF;
accel.z = (z >> 2) & 0xFF;
core.acc_x_lsb = x & 0x3;
core.acc_y_lsb = (y >> 1) & 0x1;
core.acc_z_lsb = (z >> 1) & 0x1;
*x = MathUtil::Clamp<s16>(*x, 0, 0x3ff);
*y = MathUtil::Clamp<s16>(*y, 0, 0x3ff);
*z = MathUtil::Clamp<s16>(*z, 0, 0x3ff);
}
inline void LowPassFilter(double& var, double newval, double period)
@ -694,9 +688,6 @@ inline void LowPassFilter(double& var, double newval, double period)
void Wiimote::UpdateIRData(bool use_accel)
{
// IR data is stored at offset 0x37
u8* const data = m_camera_logic.reg_data.camera_data;
u16 x[4], y[4];
memset(x, 0xFF, sizeof(x));
@ -781,15 +772,20 @@ void Wiimote::UpdateIRData(bool use_accel)
y[i] = static_cast<u16>(lround((v[i].y + 1) / 2 * (camHeight - 1)));
}
// IR data is read from offset 0x37 on real hardware
u8* const data = m_camera_logic.reg_data.camera_data;
// A maximum of 36 bytes.
std::fill_n(data, sizeof(wm_ir_full) * 4, 0xff);
// Fill report with valid data when full handshake was done
if (m_camera_logic.reg_data.data[0x30])
{
// ir mode
switch (m_camera_logic.reg_data.mode)
{
// basic
case 1:
{
memset(data, 0xFF, 10);
wm_ir_basic* const irdata = reinterpret_cast<wm_ir_basic*>(data);
for (unsigned int i = 0; i < 2; ++i)
{
@ -810,12 +806,11 @@ void Wiimote::UpdateIRData(bool use_accel)
irdata[i].y2hi = y[i * 2 + 1] >> 8;
}
}
break;
}
break;
// extended
case 3:
{
memset(data, 0xFF, 12);
wm_ir_extended* const irdata = reinterpret_cast<wm_ir_extended*>(data);
for (unsigned int i = 0; i < 4; ++i)
if (x[i] < 1024 && y[i] < 768)
@ -828,14 +823,39 @@ void Wiimote::UpdateIRData(bool use_accel)
irdata[i].size = 10;
}
}
break;
// full
case 5:
PanicAlert("Full IR report");
// UNSUPPORTED
break;
}
// full
case 5:
{
wm_ir_full* const irdata = reinterpret_cast<wm_ir_full*>(data);
for (unsigned int i = 0; i < 4; ++i)
if (x[i] < 1024 && y[i] < 768)
{
irdata[i].x = static_cast<u8>(x[i]);
irdata[i].xhi = x[i] >> 8;
irdata[i].y = static_cast<u8>(y[i]);
irdata[i].yhi = y[i] >> 8;
irdata[i].size = 10;
// TODO: implement these sensibly:
// TODO: do high bits of x/y min/max need to be set to zero?
irdata[i].xmin = 0;
irdata[i].ymin = 0;
irdata[i].xmax = 0;
irdata[i].ymax = 0;
irdata[i].zero = 0;
irdata[i].intensity = 0;
}
break;
}
default:
WARN_LOG(WIIMOTE, "Game is requesting IR data before setting IR mode.");
break;
}
}
}
void Wiimote::Update()
@ -863,13 +883,21 @@ void Wiimote::Update()
return;
}
if (RT_REPORT_CORE == m_reporting_mode && !m_reporting_auto)
{
// TODO: we only need to send a report if the data changed when reporting_auto is disabled
// probably only sensible to check this with RT_REPORT_CORE
// and just always send a report with the other modes
// return;
}
const ReportFeatures& rptf = reporting_mode_features[m_reporting_mode - RT_REPORT_CORE];
s8 rptf_size = rptf.total_size;
if (Movie::IsPlayingInput() &&
Movie::PlayWiimote(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key))
{
if (rptf.core_size)
m_status.buttons = *reinterpret_cast<wm_buttons*>(data + 2);
m_status.buttons = *reinterpret_cast<wm_buttons*>(data + rptf.GetCoreOffset());
}
else
{
@ -881,8 +909,7 @@ void Wiimote::Update()
// hotkey/settings modifier
m_hotkeys->GetState(); // data is later accessed in UpdateButtonsStatus and GetAccelData
// Data starts at byte 2 in the report
u8* feature_ptr = data + 2;
u8* feature_ptr = data + rptf.GetCoreOffset();
// core buttons
if (rptf.core_size)
@ -895,7 +922,38 @@ void Wiimote::Update()
if (rptf.accel_size)
{
// TODO: GetAccelData has hardcoded payload offsets..
GetAccelData(data);
s16 x, y, z;
GetAccelData(&x, &y, &z);
auto& core = *reinterpret_cast<wm_buttons*>(data + rptf.GetCoreOffset());
// Special cases for the interleaved reports:
// only 8 bits of precision.
if (RT_REPORT_INTERLEAVE1 == m_reporting_mode)
{
*feature_ptr = (x >> 2) & 0xff;
core.acc_bits = (z >> 4) & 0b11;
core.acc_bits2 = (z >> 6) & 0b11;
}
if (RT_REPORT_INTERLEAVE2 == m_reporting_mode)
{
*feature_ptr = (y >> 2) & 0xff;
core.acc_bits = (z >> 0) & 0b11;
core.acc_bits2 = (z >> 2) & 0b11;
}
else
{
auto& accel = *reinterpret_cast<wm_accel*>(feature_ptr);
accel.x = (x >> 2) & 0xFF;
accel.y = (y >> 2) & 0xFF;
accel.z = (z >> 2) & 0xFF;
// Set the LSBs
core.acc_bits = x & 0b11;
core.acc_bits2 = (y >> 1) & 0x1;
core.acc_bits2 |= ((z >> 1) & 0x1) << 1;
}
feature_ptr += rptf.accel_size;
}
@ -909,8 +967,16 @@ void Wiimote::Update()
if (!m_status.ir)
WARN_LOG(WIIMOTE, "Game is reading IR data without enabling IR logic first.");
m_i2c_bus.BusRead(IRCameraLogic::DEVICE_ADDR, offsetof(IRCameraLogic::RegData, camera_data),
rptf.ir_size, feature_ptr);
u8 camera_data_offset = offsetof(IRCameraLogic::RegData, camera_data);
if (RT_REPORT_INTERLEAVE2 == m_reporting_mode)
{
// Interleave2 reads the 2nd half of the "FULL" IR data
camera_data_offset += sizeof(wm_ir_full) * 2;
}
m_i2c_bus.BusRead(IRCameraLogic::DEVICE_ADDR, camera_data_offset, rptf.ir_size, feature_ptr);
feature_ptr += rptf.ir_size;
}
@ -944,15 +1010,17 @@ void Wiimote::Update()
Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension,
m_ext_logic.ext_key);
// don't send a data report if auto reporting is off
if (false == m_reporting_auto && data[1] >= RT_REPORT_CORE)
return;
// send data report
if (rptf_size)
{
Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, rptf_size);
}
// The interleaved reporting modes toggle back and forth:
if (RT_REPORT_INTERLEAVE1 == m_reporting_mode)
m_reporting_mode = RT_REPORT_INTERLEAVE2;
else if (RT_REPORT_INTERLEAVE2 == m_reporting_mode)
m_reporting_mode = RT_REPORT_INTERLEAVE1;
}
void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size)
@ -1213,9 +1281,9 @@ void Wiimote::MotionPlusLogic::Update()
if (6 == i2c_bus.BusRead(ACTIVE_DEVICE_ADDR, 0x00, 6, data))
{
// Passthrough data modifications via wiibrew.org
// Data passing through drops the least significant bit of the axes of the left (or only) joystick
// Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag]
// Bits 0 and 1 of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before
// Data passing through drops the least significant bit of the axes of the left (or only)
// joystick Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] Bits 0 and 1
// of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before
SetBit(data[0], 0, Common::ExtractBit(data[5], 0));
SetBit(data[1], 0, Common::ExtractBit(data[5], 1));

View File

@ -269,6 +269,9 @@ public:
// TODO: change int to u16 or something
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
{
// TODO: the real bus seems to read in blocks of 8.
// peripherals NACK after each 8 bytes.
// INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count);
for (auto& slave : m_slaves)
{
@ -286,6 +289,7 @@ public:
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
{
// TODO: write in blocks of 6 to simulate the real bus
// sometimes it writes in blocks of 8, (speaker data)
// this might trigger activation writes more accurately
// INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count);
@ -389,7 +393,7 @@ protected:
void UpdateButtonsStatus();
void GetButtonData(u8* data);
void GetAccelData(u8* data);
void GetAccelData(s16* x, s16* y, s16* z);
void UpdateIRData(bool use_accel);
private:
@ -399,6 +403,8 @@ private:
struct IRCameraLogic : public I2CSlave
{
// TODO: some of this memory is write-only and should return error 7.
struct RegData
{
// Contains sensitivity and other unknown data
@ -501,6 +507,8 @@ private:
static const u8 DATA_FORMAT_ADPCM = 0x00;
static const u8 DATA_FORMAT_PCM = 0x40;
// TODO: It seems reading address 0x00 should always return 0xff.
struct
{
// Speaker reports result in a write of samples to addr 0x00 (which also plays sound)
@ -746,6 +754,7 @@ private:
DynamicData m_shake_dynamic_data;
// Wiimote accel data
// TODO: can this member be eliminated?
AccelData m_accel;
// Wiimote index, 0-3

View File

@ -688,8 +688,7 @@ static void SetWiiInputDisplayString(int remoteID, const u8* const data,
std::memcpy(&dt, accelData, sizeof(dt));
display_str +=
StringFromFormat(" ACC:%d,%d,%d", dt.x << 2 | buttons.acc_x_lsb,
dt.y << 2 | buttons.acc_y_lsb << 1, dt.z << 2 | buttons.acc_z_lsb << 1);
StringFromFormat(" ACC:%d,%d,%d (LSB not shown)", dt.x << 2, dt.y << 2, dt.z << 2);
}
}

View File

@ -350,11 +350,12 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
if (m_remote_orientation_box->isVisible() && accel_data && buttons_data)
{
wm_accel& accel = *reinterpret_cast<wm_accel*>(accel_data);
wm_buttons& buttons = *reinterpret_cast<wm_buttons*>(buttons_data);
//wm_buttons& buttons = *reinterpret_cast<wm_buttons*>(buttons_data);
u16 accel_x = (accel.x << 2) & (buttons.acc_x_lsb & 0b11);
u16 accel_y = (accel.y << 2) & ((buttons.acc_y_lsb & 0b1) << 1);
u16 accel_z = (accel.z << 2) & ((buttons.acc_z_lsb & 0b1) << 1);
// TODO: lsb
u16 accel_x = (accel.x << 2); // & (buttons.acc_x_lsb & 0b11);
u16 accel_y = (accel.y << 2); // & ((buttons.acc_y_lsb & 0b1) << 1);
u16 accel_z = (accel.z << 2); // &((buttons.acc_z_lsb & 0b1) << 1);
GetSpinBoxU16(m_remote_orientation_x_value, accel_x);
GetSpinBoxU16(m_remote_orientation_y_value, accel_y);
@ -364,9 +365,10 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
accel.y = accel_y >> 2;
accel.z = accel_z >> 2;
buttons.acc_x_lsb = accel_x & 0b11;
buttons.acc_y_lsb = (accel_y >> 1) & 0b1;
buttons.acc_z_lsb = (accel_z >> 1) & 0b1;
// TODO: lsb
//buttons.acc_x_lsb = accel_x & 0b11;
//buttons.acc_y_lsb = (accel_y >> 1) & 0b1;
//buttons.acc_z_lsb = (accel_z >> 1) & 0b1;
}
if (m_ir_box->isVisible() && ir_data && !m_use_controller->isChecked())