Core/WiimoteEmu: Add functions to Nunchuk, Classic Controller, and MotionPlus extensions to get/set data without duplicate bithacks everywhere.

This commit is contained in:
Jordan Woyak 2020-01-21 18:36:41 -06:00
parent 8343dadd58
commit 5af2081c75
13 changed files with 649 additions and 283 deletions

View File

@ -5,6 +5,7 @@
#include <cassert>
#include "Common/BitUtils.h"
#include "Common/MathUtil.h"
#include "Core/HW/WiimoteCommon/DataReport.h"
namespace WiimoteCommon
@ -75,40 +76,35 @@ struct IncludeAccel : virtual DataReportManipulator
void GetAccelData(AccelData* result) const override
{
const AccelMSB accel = Common::BitCastPtr<AccelMSB>(data_ptr + 2);
result->x = accel.x << 2;
result->y = accel.y << 2;
result->z = accel.z << 2;
// LSBs
const CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
result->x |= core.acc_bits & 0b11;
result->y |= (core.acc_bits2 & 0b1) << 1;
result->z |= core.acc_bits2 & 0b10;
// X has 10 bits of precision.
result->value.x = accel.x << 2;
result->value.x |= core.acc_bits & 0b11;
// Y and Z only have 9 bits of precision. (convert them to 10)
result->value.y =
Common::ExpandValue<u16>(accel.y << 1 | Common::ExtractBit<0>(core.acc_bits2), 1);
result->value.z =
Common::ExpandValue<u16>(accel.z << 1 | Common::ExtractBit<1>(core.acc_bits2), 1);
}
void SetAccelData(const AccelData& new_accel) override
{
AccelMSB accel = {};
accel.x = new_accel.x >> 2;
accel.y = new_accel.y >> 2;
accel.z = new_accel.z >> 2;
Common::BitCastPtr<AccelMSB>(data_ptr + 2) = accel;
Common::BitCastPtr<AccelMSB>(data_ptr + 2) = AccelMSB(new_accel.value / 4);
// LSBs
CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
core.acc_bits = (new_accel.x >> 0) & 0b11;
core.acc_bits2 = (new_accel.y >> 1) & 0x1;
core.acc_bits2 |= (new_accel.z & 0xb10);
core.acc_bits = (new_accel.value.x >> 0) & 0b11;
core.acc_bits2 = (new_accel.value.y >> 1) & 0x1;
core.acc_bits2 |= (new_accel.value.z & 0xb10);
Common::BitCastPtr<CoreData>(data_ptr) = core;
}
bool HasAccel() const override { return true; }
private:
struct AccelMSB
{
u8 x, y, z;
};
using AccelMSB = Common::TVec3<u8>;
static_assert(sizeof(AccelMSB) == 3, "Wrong size");
};
@ -195,26 +191,28 @@ struct ReportExt21 : NoCore, NoAccel, NoIR, IncludeExt<0, 21>
struct ReportInterleave1 : IncludeCore, IncludeIR<3, 18, 0>, NoExt
{
// FYI: Only 8-bits of precision in this report, and no Y axis.
// Only contains 4 MSB of Z axis.
void GetAccelData(AccelData* accel) const override
{
accel->x = data_ptr[2] << 2;
// X axis only has 8 bits of precision. (converted to 10)
accel->value.x = Common::ExpandValue<u16>(data_ptr[2], 2);
// Retain lower 6 bits.
accel->z &= 0b111111;
// Y axis is not contained in this report. (provided by "Interleave2")
// Clear upper bits, retain lower bits. (provided by "Interleave2")
accel->value.z &= 0b111111;
// Report only contains 4 MSB of Z axis.
const CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
accel->z |= (core.acc_bits << 6) | (core.acc_bits2 << 8);
accel->value.z |= (core.acc_bits << 6) | (core.acc_bits2 << 8);
}
void SetAccelData(const AccelData& accel) override
{
data_ptr[2] = accel.x >> 2;
data_ptr[2] = accel.value.x >> 2;
CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
core.acc_bits = (accel.z >> 6) & 0b11;
core.acc_bits2 = (accel.z >> 8) & 0b11;
core.acc_bits = (accel.value.z >> 6) & 0b11;
core.acc_bits2 = (accel.value.z >> 8) & 0b11;
Common::BitCastPtr<CoreData>(data_ptr) = core;
}
@ -226,26 +224,28 @@ struct ReportInterleave1 : IncludeCore, IncludeIR<3, 18, 0>, NoExt
struct ReportInterleave2 : IncludeCore, IncludeIR<3, 18, 18>, NoExt
{
// FYI: Only 8-bits of precision in this report, and no X axis.
// Only contains 4 LSB of Z axis.
void GetAccelData(AccelData* accel) const override
{
accel->y = data_ptr[2] << 2;
// X axis is not contained in this report. (provided by "Interleave1")
// Retain upper 4 bits.
accel->z &= ~0b111111;
// Y axis only has 8 bits of precision. (converted to 10)
accel->value.y = Common::ExpandValue<u16>(data_ptr[2], 2);
// Clear lower bits, retain upper bits. (provided by "Interleave1")
accel->value.z &= ~0b111111;
// Report only contains 4 LSBs of Z axis. (converted to 6)
const CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
accel->z |= (core.acc_bits << 2) | (core.acc_bits2 << 4);
accel->value.z |= Common::ExpandValue<u16>(core.acc_bits | core.acc_bits2 << 2, 2);
}
void SetAccelData(const AccelData& accel) override
{
data_ptr[2] = accel.y >> 2;
data_ptr[2] = accel.value.y >> 2;
CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
core.acc_bits = (accel.z >> 2) & 0b11;
core.acc_bits2 = (accel.z >> 4) & 0b11;
core.acc_bits = (accel.value.z >> 2) & 0b11;
core.acc_bits2 = (accel.value.z >> 4) & 0b11;
Common::BitCastPtr<CoreData>(data_ptr) = core;
}

View File

@ -8,9 +8,11 @@
#include <memory>
#include "Common/CommonTypes.h"
#include "Common/Matrix.h"
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
namespace WiimoteCommon
{
@ -21,12 +23,6 @@ class DataReportManipulator
public:
virtual ~DataReportManipulator() = default;
// Accel data handled as if there were always 10 bits of precision.
struct AccelData
{
u16 x, y, z;
};
using CoreData = ButtonData;
virtual bool HasCore() const = 0;
@ -66,7 +62,6 @@ public:
explicit DataReportBuilder(InputReportID rpt_id);
using CoreData = ButtonData;
using AccelData = DataReportManipulator::AccelData;
void SetMode(InputReportID rpt_id);
InputReportID GetMode() const;
@ -99,11 +94,10 @@ public:
u32 GetDataSize() const;
private:
static constexpr int HEADER_SIZE = 2;
static constexpr int MAX_DATA_SIZE = MAX_PAYLOAD - 2;
private:
TypedHIDInputData<std::array<u8, MAX_DATA_SIZE>> m_data;
std::unique_ptr<DataReportManipulator> m_manip;

View File

@ -10,6 +10,10 @@ namespace WiimoteCommon
{
constexpr u8 MAX_PAYLOAD = 23;
// Based on testing, old WiiLi.org docs, and WiiUse library:
// Max battery level seems to be 0xc8 (decimal 200)
constexpr u8 MAX_BATTERY_LEVEL = 0xc8;
enum class InputReportID : u8
{
Status = 0x20,

View File

@ -7,7 +7,9 @@
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/Matrix.h"
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#ifdef _MSC_VER
#pragma warning(push)
@ -41,6 +43,8 @@ static_assert(sizeof(OutputReportGeneric) == 2, "Wrong size");
struct OutputReportRumble
{
static constexpr OutputReportID REPORT_ID = OutputReportID::Rumble;
u8 rumble : 1;
};
static_assert(sizeof(OutputReportRumble) == 1, "Wrong size");
@ -55,8 +59,34 @@ struct OutputReportEnableFeature
};
static_assert(sizeof(OutputReportEnableFeature) == 1, "Wrong size");
struct OutputReportIRLogicEnable : OutputReportEnableFeature
{
static constexpr OutputReportID REPORT_ID = OutputReportID::IRLogicEnable;
};
static_assert(sizeof(OutputReportIRLogicEnable) == 1, "Wrong size");
struct OutputReportIRLogicEnable2 : OutputReportEnableFeature
{
static constexpr OutputReportID REPORT_ID = OutputReportID::IRLogicEnable2;
};
static_assert(sizeof(OutputReportIRLogicEnable2) == 1, "Wrong size");
struct OutputReportSpeakerEnable : OutputReportEnableFeature
{
static constexpr OutputReportID REPORT_ID = OutputReportID::SpeakerEnable;
};
static_assert(sizeof(OutputReportSpeakerEnable) == 1, "Wrong size");
struct OutputReportSpeakerMute : OutputReportEnableFeature
{
static constexpr OutputReportID REPORT_ID = OutputReportID::SpeakerMute;
};
static_assert(sizeof(OutputReportSpeakerMute) == 1, "Wrong size");
struct OutputReportLeds
{
static constexpr OutputReportID REPORT_ID = OutputReportID::LED;
u8 rumble : 1;
u8 ack : 1;
u8 : 2;
@ -66,6 +96,8 @@ static_assert(sizeof(OutputReportLeds) == 1, "Wrong size");
struct OutputReportMode
{
static constexpr OutputReportID REPORT_ID = OutputReportID::ReportMode;
u8 rumble : 1;
u8 ack : 1;
u8 continuous : 1;
@ -76,6 +108,8 @@ static_assert(sizeof(OutputReportMode) == 2, "Wrong size");
struct OutputReportRequestStatus
{
static constexpr OutputReportID REPORT_ID = OutputReportID::RequestStatus;
u8 rumble : 1;
u8 : 7;
};
@ -83,6 +117,8 @@ static_assert(sizeof(OutputReportRequestStatus) == 1, "Wrong size");
struct OutputReportWriteData
{
static constexpr OutputReportID REPORT_ID = OutputReportID::WriteData;
u8 rumble : 1;
u8 : 1;
u8 space : 2;
@ -100,6 +136,8 @@ static_assert(sizeof(OutputReportWriteData) == 21, "Wrong size");
struct OutputReportReadData
{
static constexpr OutputReportID REPORT_ID = OutputReportID::ReadData;
u8 rumble : 1;
u8 : 1;
u8 space : 2;
@ -116,6 +154,8 @@ static_assert(sizeof(OutputReportReadData) == 6, "Wrong size");
struct OutputReportSpeakerData
{
static constexpr OutputReportID REPORT_ID = OutputReportID::SpeakerData;
u8 rumble : 1;
u8 : 2;
u8 length : 5;
@ -157,6 +197,8 @@ static_assert(sizeof(ButtonData) == 2, "Wrong size");
struct InputReportStatus
{
static constexpr InputReportID REPORT_ID = InputReportID::Status;
ButtonData buttons;
u8 battery_low : 1;
u8 extension : 1;
@ -170,6 +212,8 @@ static_assert(sizeof(InputReportStatus) == 6, "Wrong size");
struct InputReportAck
{
static constexpr InputReportID REPORT_ID = InputReportID::Ack;
ButtonData buttons;
OutputReportID rpt_id;
ErrorCode error_code;
@ -178,6 +222,8 @@ static_assert(sizeof(InputReportAck) == 4, "Wrong size");
struct InputReportReadDataReply
{
static constexpr InputReportID REPORT_ID = InputReportID::ReadDataReply;
ButtonData buttons;
u8 error : 4;
u8 size_minus_one : 4;
@ -187,6 +233,64 @@ struct InputReportReadDataReply
};
static_assert(sizeof(InputReportReadDataReply) == 21, "Wrong size");
// Accel data handled as if there were always 10 bits of precision.
using AccelType = Common::TVec3<u16>;
using AccelData = ControllerEmu::RawValue<AccelType, 10>;
// Found in Wiimote EEPROM and Nunchuk "register".
// 0g and 1g points exist.
struct AccelCalibrationPoint
{
// All components have 10 bits of precision.
u16 GetX() const { return x2 << 2 | x1; }
u16 GetY() const { return y2 << 2 | y1; }
u16 GetZ() const { return z2 << 2 | z1; }
auto Get() const { return AccelType{GetX(), GetY(), GetZ()}; }
void SetX(u16 x)
{
x2 = x >> 2;
x1 = x;
}
void SetY(u16 y)
{
y2 = y >> 2;
y1 = y;
}
void SetZ(u16 z)
{
z2 = z >> 2;
z1 = z;
}
void Set(AccelType accel)
{
SetX(accel.x);
SetY(accel.y);
SetZ(accel.z);
}
u8 x2, y2, z2;
u8 z1 : 2;
u8 y1 : 2;
u8 x1 : 2;
u8 : 2;
};
// Located at 0x16 and 0x20 of Wii Remote EEPROM.
struct AccelCalibrationData
{
using Calibration = ControllerEmu::TwoPointCalibration<AccelType, 10>;
auto GetCalibration() const { return Calibration(zero_g.Get(), one_g.Get()); }
AccelCalibrationPoint zero_g;
AccelCalibrationPoint one_g;
u8 volume : 7;
u8 motor : 1;
u8 checksum;
};
} // namespace WiimoteCommon
#pragma pack(pop)

View File

@ -114,8 +114,10 @@ void Classic::Update()
{
const ControllerEmu::AnalogStick::StateData left_stick_state = m_left_stick->GetState();
classic_data.lx = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.x * LEFT_STICK_RADIUS));
classic_data.ly = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.y * LEFT_STICK_RADIUS));
const u8 x = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.x * LEFT_STICK_RADIUS));
const u8 y = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.y * LEFT_STICK_RADIUS));
classic_data.SetLeftStick({x, y});
}
// right stick
@ -125,10 +127,7 @@ void Classic::Update()
const u8 x = static_cast<u8>(RIGHT_STICK_CENTER + (right_stick_data.x * RIGHT_STICK_RADIUS));
const u8 y = static_cast<u8>(RIGHT_STICK_CENTER + (right_stick_data.y * RIGHT_STICK_RADIUS));
classic_data.rx1 = x;
classic_data.rx2 = x >> 1;
classic_data.rx3 = x >> 3;
classic_data.ry = y;
classic_data.SetRightStick({x, y});
}
// triggers
@ -139,18 +138,15 @@ void Classic::Update()
const u8 lt = static_cast<u8>(trigs[0] * TRIGGER_RANGE);
const u8 rt = static_cast<u8>(trigs[1] * TRIGGER_RANGE);
classic_data.lt1 = lt;
classic_data.lt2 = lt >> 3;
classic_data.rt = rt;
classic_data.SetLeftTrigger(lt);
classic_data.SetRightTrigger(rt);
}
// buttons
m_buttons->GetState(&classic_data.bt.hex, classic_button_bitmasks.data());
// dpad
m_dpad->GetState(&classic_data.bt.hex, classic_dpad_bitmasks.data());
// flip button bits
classic_data.bt.hex ^= 0xFFFF;
// buttons and dpad
u16 buttons = 0;
m_buttons->GetState(&buttons, classic_button_bitmasks.data());
m_dpad->GetState(&buttons, classic_dpad_bitmasks.data());
classic_data.SetButtons(buttons);
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = classic_data;
}

View File

@ -4,6 +4,9 @@
#pragma once
#include <limits>
#include "Common/Matrix.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
@ -56,12 +59,59 @@ public:
};
static_assert(sizeof(ButtonFormat) == 2, "Wrong size");
static constexpr int LEFT_STICK_BITS = 6;
static constexpr int RIGHT_STICK_BITS = 5;
static constexpr int TRIGGER_BITS = 5;
struct DataFormat
{
// lx/ly/lz; left joystick
// rx/ry/rz; right joystick
// lt; left trigger
// rt; left trigger
using StickType = Common::TVec2<u8>;
using LeftStickRawValue = ControllerEmu::RawValue<StickType, LEFT_STICK_BITS>;
using RightStickRawValue = ControllerEmu::RawValue<StickType, RIGHT_STICK_BITS>;
using TriggerType = u8;
using TriggerRawValue = ControllerEmu::RawValue<TriggerType, TRIGGER_BITS>;
// 6-bit X and Y values (0-63)
auto GetLeftStick() const { return LeftStickRawValue{StickType(lx, ly)}; };
void SetLeftStick(const StickType& value)
{
lx = value.x;
ly = value.y;
}
// 5-bit X and Y values (0-31)
auto GetRightStick() const
{
return RightStickRawValue{StickType(rx1 | rx2 << 1 | rx3 << 3, ry)};
};
void SetRightStick(const StickType& value)
{
rx1 = value.x & 0b1;
rx2 = (value.x >> 1) & 0b11;
rx3 = (value.x >> 3) & 0b11;
ry = value.y;
}
// 5-bit values (0-31)
auto GetLeftTrigger() const { return TriggerRawValue(lt1 | lt2 << 3); }
void SetLeftTrigger(TriggerType value)
{
lt1 = value & 0b111;
lt2 = (value >> 3) & 0b11;
}
auto GetRightTrigger() const { return TriggerRawValue(rt); }
void SetRightTrigger(TriggerType value) { rt = value; }
u16 GetButtons() const
{
// 0 == pressed.
return ~bt.hex;
}
void SetButtons(u16 value)
{
// 0 == pressed.
bt.hex = ~value;
}
u8 lx : 6; // byte 0
u8 rx3 : 2;
@ -80,6 +130,53 @@ public:
};
static_assert(sizeof(DataFormat) == 6, "Wrong size");
static constexpr int CAL_STICK_BITS = 8;
static constexpr int CAL_TRIGGER_BITS = 8;
struct CalibrationData
{
using StickType = DataFormat::StickType;
using TriggerType = DataFormat::TriggerType;
using StickCalibration = ControllerEmu::ThreePointCalibration<StickType, CAL_STICK_BITS>;
using TriggerCalibration = ControllerEmu::TwoPointCalibration<TriggerType, CAL_TRIGGER_BITS>;
static constexpr TriggerType TRIGGER_MAX = std::numeric_limits<TriggerType>::max();
struct StickAxis
{
u8 max;
u8 min;
u8 center;
};
auto GetLeftStick() const
{
return StickCalibration{StickType{left_stick_x.min, left_stick_y.min},
StickType{left_stick_x.center, left_stick_y.center},
StickType{left_stick_x.max, left_stick_y.max}};
}
auto GetRightStick() const
{
return StickCalibration{StickType{right_stick_x.min, right_stick_y.min},
StickType{right_stick_x.center, right_stick_y.center},
StickType{right_stick_x.max, right_stick_y.max}};
}
auto GetLeftTrigger() const { return TriggerCalibration{left_trigger_zero, TRIGGER_MAX}; }
auto GetRightTrigger() const { return TriggerCalibration{right_trigger_zero, TRIGGER_MAX}; }
StickAxis left_stick_x;
StickAxis left_stick_y;
StickAxis right_stick_x;
StickAxis right_stick_y;
u8 left_trigger_zero;
u8 right_trigger_zero;
std::array<u8, 2> checksum;
};
static_assert(sizeof(CalibrationData) == 16, "Wrong size");
Classic();
void Update() override;
@ -110,13 +207,10 @@ public:
static constexpr u8 CAL_STICK_CENTER = 0x80;
static constexpr u8 CAL_STICK_RANGE = 0x7f;
static constexpr int CAL_STICK_BITS = 8;
static constexpr int LEFT_STICK_BITS = 6;
static constexpr u8 LEFT_STICK_CENTER = CAL_STICK_CENTER >> (CAL_STICK_BITS - LEFT_STICK_BITS);
static constexpr u8 LEFT_STICK_RADIUS = CAL_STICK_RANGE >> (CAL_STICK_BITS - LEFT_STICK_BITS);
static constexpr int RIGHT_STICK_BITS = 5;
static constexpr u8 RIGHT_STICK_CENTER = CAL_STICK_CENTER >> (CAL_STICK_BITS - RIGHT_STICK_BITS);
static constexpr u8 RIGHT_STICK_RADIUS = CAL_STICK_RANGE >> (CAL_STICK_BITS - RIGHT_STICK_BITS);

View File

@ -87,10 +87,9 @@ void Nunchuk::Update()
}
// buttons
m_buttons->GetState(&nc_data.bt.hex, nunchuk_button_bitmasks.data());
// flip the button bits :/
nc_data.bt.hex ^= 0x03;
u8 buttons = 0;
m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data());
nc_data.SetButtons(buttons);
// Acceleration data:
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);
@ -109,13 +108,7 @@ void Nunchuk::Update()
// Calibration values are 8-bit but we want 10-bit precision, so << 2.
const auto acc = ConvertAccelData(accel, ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
nc_data.ax = (acc.x >> 2) & 0xFF;
nc_data.ay = (acc.y >> 2) & 0xFF;
nc_data.az = (acc.z >> 2) & 0xFF;
nc_data.bt.acc_x_lsb = acc.x & 0x3;
nc_data.bt.acc_y_lsb = acc.y & 0x3;
nc_data.bt.acc_z_lsb = acc.z & 0x3;
nc_data.SetAccel(acc.value);
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = nc_data;
}

View File

@ -51,25 +51,103 @@ public:
};
static_assert(sizeof(ButtonFormat) == 1, "Wrong size");
union DataFormat
struct DataFormat
{
struct
using StickType = Common::TVec2<u8>;
using StickRawValue = ControllerEmu::RawValue<StickType, 8>;
using AccelType = WiimoteCommon::AccelType;
using AccelData = WiimoteCommon::AccelData;
auto GetStick() const { return StickRawValue(StickType(jx, jy)); }
// Components have 10 bits of precision.
u16 GetAccelX() const { return ax << 2 | bt.acc_x_lsb; }
u16 GetAccelY() const { return ay << 2 | bt.acc_y_lsb; }
u16 GetAccelZ() const { return az << 2 | bt.acc_z_lsb; }
auto GetAccel() const { return AccelData{AccelType{GetAccelX(), GetAccelY(), GetAccelZ()}}; }
void SetAccelX(u16 val)
{
// joystick x, y
u8 jx;
u8 jy;
ax = val >> 2;
bt.acc_x_lsb = val & 0b11;
}
void SetAccelY(u16 val)
{
ay = val >> 2;
bt.acc_y_lsb = val & 0b11;
}
void SetAccelZ(u16 val)
{
az = val >> 2;
bt.acc_z_lsb = val & 0b11;
}
void SetAccel(const AccelType& accel)
{
SetAccelX(accel.x);
SetAccelY(accel.y);
SetAccelZ(accel.z);
}
// accelerometer
u8 ax;
u8 ay;
u8 az;
u8 GetButtons() const
{
// 0 == pressed.
return ~bt.hex & (BUTTON_C | BUTTON_Z);
}
void SetButtons(u8 value)
{
// 0 == pressed.
bt.hex |= (BUTTON_C | BUTTON_Z);
bt.hex ^= value & (BUTTON_C | BUTTON_Z);
}
// buttons + accelerometer LSBs
ButtonFormat bt;
};
// joystick x, y
u8 jx;
u8 jy;
// accelerometer
u8 ax;
u8 ay;
u8 az;
// buttons + accelerometer LSBs
ButtonFormat bt;
};
static_assert(sizeof(DataFormat) == 6, "Wrong size");
struct CalibrationData
{
using StickType = DataFormat::StickType;
using StickCalibration = ControllerEmu::ThreePointCalibration<StickType, 8>;
using AccelType = WiimoteCommon::AccelType;
using AccelCalibration = ControllerEmu::TwoPointCalibration<AccelType, 10>;
struct Stick
{
u8 max;
u8 min;
u8 center;
};
auto GetStick() const
{
return StickCalibration(StickType{stick_x.min, stick_y.min},
StickType{stick_x.center, stick_y.center},
StickType{stick_x.max, stick_y.max});
}
auto GetAccel() const { return AccelCalibration(accel_zero_g.Get(), accel_one_g.Get()); }
WiimoteCommon::AccelCalibrationPoint accel_zero_g;
WiimoteCommon::AccelCalibrationPoint accel_one_g;
Stick stick_x;
Stick stick_y;
std::array<u8, 2> checksum;
};
static_assert(sizeof(CalibrationData) == 16, "Wrong size");
Nunchuk();
void Update() override;

View File

@ -16,7 +16,6 @@
#include "Common/Logging/Log.h"
#include "Common/MathUtil.h"
#include "Common/MsgHandler.h"
#include "Common/Swap.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/Dynamics.h"
@ -56,6 +55,41 @@ struct MPI : mbedtls_mpi
namespace WiimoteEmu
{
Common::Vec3 MotionPlus::DataFormat::Data::GetAngularVelocity(const CalibrationBlocks& blocks) const
{
// Each axis may be using either slow or fast calibration.
const auto calibration = blocks.GetRelevantCalibration(is_slow);
// It seems M+ calibration data does not follow the "right-hand rule".
const auto sign_fix = Common::Vec3(-1, +1, -1);
// Adjust deg/s to rad/s.
constexpr auto scalar = float(MathUtil::TAU / 360);
return gyro.GetNormalizedValue(calibration.value) * sign_fix * Common::Vec3(calibration.degrees) *
scalar;
}
auto MotionPlus::CalibrationBlocks::GetRelevantCalibration(SlowType is_slow) const
-> RelevantCalibration
{
RelevantCalibration result;
const auto& pitch_block = is_slow.x ? slow : fast;
const auto& roll_block = is_slow.y ? slow : fast;
const auto& yaw_block = is_slow.z ? slow : fast;
result.value.max = {pitch_block.pitch_scale, roll_block.roll_scale, yaw_block.yaw_scale};
result.value.zero = {pitch_block.pitch_zero, roll_block.roll_zero, yaw_block.yaw_zero};
result.degrees.x = pitch_block.degrees_div_6 * 6;
result.degrees.y = roll_block.degrees_div_6 * 6;
result.degrees.z = yaw_block.degrees_div_6 * 6;
return result;
}
MotionPlus::MotionPlus() : Extension("MotionPlus")
{
}
@ -82,35 +116,20 @@ void MotionPlus::Reset()
constexpr u16 ROLL_SCALE = CALIBRATION_ZERO + CALIBRATION_SCALE_OFFSET;
constexpr u16 PITCH_SCALE = CALIBRATION_ZERO - CALIBRATION_SCALE_OFFSET;
#pragma pack(push, 1)
struct CalibrationBlock
{
u16 yaw_zero = Common::swap16(CALIBRATION_ZERO);
u16 roll_zero = Common::swap16(CALIBRATION_ZERO);
u16 pitch_zero = Common::swap16(CALIBRATION_ZERO);
u16 yaw_scale = Common::swap16(YAW_SCALE);
u16 roll_scale = Common::swap16(ROLL_SCALE);
u16 pitch_scale = Common::swap16(PITCH_SCALE);
u8 degrees_div_6;
};
struct CalibrationData
{
CalibrationBlock fast;
u8 uid_1;
Common::BigEndianValue<u16> crc32_msb;
CalibrationBlock slow;
u8 uid_2;
Common::BigEndianValue<u16> crc32_lsb;
};
#pragma pack(pop)
static_assert(sizeof(CalibrationData) == 0x20, "Bad size.");
static_assert(CALIBRATION_FAST_SCALE_DEGREES % 6 == 0, "Value should be divisible by 6.");
static_assert(CALIBRATION_SLOW_SCALE_DEGREES % 6 == 0, "Value should be divisible by 6.");
CalibrationData calibration;
calibration.fast.yaw_zero = calibration.slow.yaw_zero = CALIBRATION_ZERO;
calibration.fast.roll_zero = calibration.slow.roll_zero = CALIBRATION_ZERO;
calibration.fast.pitch_zero = calibration.slow.pitch_zero = CALIBRATION_ZERO;
calibration.fast.yaw_scale = calibration.slow.yaw_scale = YAW_SCALE;
calibration.fast.roll_scale = calibration.slow.roll_scale = ROLL_SCALE;
calibration.fast.pitch_scale = calibration.slow.pitch_scale = PITCH_SCALE;
calibration.fast.degrees_div_6 = CALIBRATION_FAST_SCALE_DEGREES / 6;
calibration.slow.degrees_div_6 = CALIBRATION_SLOW_SCALE_DEGREES / 6;
@ -120,17 +139,22 @@ void MotionPlus::Reset()
calibration.uid_1 = 0x0b;
calibration.uid_2 = 0xe9;
// Update checksum (crc32 of all data other than the checksum itself):
auto crc_result = crc32(0, Z_NULL, 0);
crc_result = crc32(crc_result, reinterpret_cast<const Bytef*>(&calibration), 0xe);
crc_result = crc32(crc_result, reinterpret_cast<const Bytef*>(&calibration) + 0x10, 0xe);
calibration.crc32_lsb = u16(crc_result);
calibration.crc32_msb = u16(crc_result >> 16);
calibration.UpdateChecksum();
Common::BitCastPtr<CalibrationData>(m_reg_data.calibration_data.data()) = calibration;
}
void MotionPlus::CalibrationData::UpdateChecksum()
{
// Checksum is crc32 of all data other than the checksum itself.
auto crc_result = crc32(0, Z_NULL, 0);
crc_result = crc32(crc_result, reinterpret_cast<const Bytef*>(this), 0xe);
crc_result = crc32(crc_result, reinterpret_cast<const Bytef*>(this) + 0x10, 0xe);
crc32_lsb = u16(crc_result);
crc32_msb = u16(crc_result >> 16);
}
void MotionPlus::DoState(PointerWrap& p)
{
p.Do(m_reg_data);
@ -547,47 +571,10 @@ void MotionPlus::PrepareInput(const Common::Vec3& angular_velocity)
break;
}
case PassthroughMode::Nunchuk:
{
if (EXT_AMT == m_i2c_bus.BusRead(EXT_SLAVE, EXT_ADDR, EXT_AMT, data))
{
// Passthrough data modifications via wiibrew.org
// Verified on real hardware via a test of every bit.
// Data passing through drops the least significant bit of the three accelerometer values.
// Bit 7 of byte 5 is moved to bit 6 of byte 5, overwriting it
Common::SetBit(data[5], 6, Common::ExtractBit(data[5], 7));
// Bit 0 of byte 4 is moved to bit 7 of byte 5
Common::SetBit(data[5], 7, Common::ExtractBit(data[4], 0));
// Bit 3 of byte 5 is moved to bit 4 of byte 5, overwriting it
Common::SetBit(data[5], 4, Common::ExtractBit(data[5], 3));
// Bit 1 of byte 5 is moved to bit 3 of byte 5
Common::SetBit(data[5], 3, Common::ExtractBit(data[5], 1));
// Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it
Common::SetBit(data[5], 2, Common::ExtractBit(data[5], 0));
mplus_data = Common::BitCastPtr<DataFormat>(data);
// Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below.
mplus_data.is_mp_data = false;
}
else
{
// Read failed (extension unplugged), Send M+ data instead
mplus_data.is_mp_data = true;
}
break;
}
case PassthroughMode::Classic:
{
if (EXT_AMT == m_i2c_bus.BusRead(EXT_SLAVE, EXT_ADDR, EXT_AMT, data))
{
// Passthrough data modifications via wiibrew.org
// Verified on real hardware via a test of every bit.
// 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.
Common::SetBit(data[0], 0, Common::ExtractBit(data[5], 0));
Common::SetBit(data[1], 0, Common::ExtractBit(data[5], 1));
ApplyPassthroughModifications(GetPassthroughMode(), data);
mplus_data = Common::BitCastPtr<DataFormat>(data);
// Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below.
@ -599,7 +586,6 @@ void MotionPlus::PrepareInput(const Common::Vec3& angular_velocity)
mplus_data.is_mp_data = true;
}
break;
}
default:
// This really shouldn't happen as the M+ deactivates on an invalid mode write.
ERROR_LOG(WIIMOTE, "M+ unknown passthrough-mode %d", int(GetPassthroughMode()));
@ -664,4 +650,66 @@ void MotionPlus::PrepareInput(const Common::Vec3& angular_velocity)
Common::BitCastPtr<DataFormat>(data) = mplus_data;
}
void MotionPlus::ApplyPassthroughModifications(PassthroughMode mode, u8* data)
{
if (mode == PassthroughMode::Nunchuk)
{
// Passthrough data modifications via wiibrew.org
// Verified on real hardware via a test of every bit.
// Data passing through drops the least significant bit of the three accelerometer values.
// Bit 7 of byte 5 is moved to bit 6 of byte 5, overwriting it
Common::SetBit<6>(data[5], Common::ExtractBit<7>(data[5]));
// Bit 0 of byte 4 is moved to bit 7 of byte 5
Common::SetBit<7>(data[5], Common::ExtractBit<0>(data[4]));
// Bit 3 of byte 5 is moved to bit 4 of byte 5, overwriting it
Common::SetBit<4>(data[5], Common::ExtractBit<3>(data[5]));
// Bit 1 of byte 5 is moved to bit 3 of byte 5
Common::SetBit<3>(data[5], Common::ExtractBit<1>(data[5]));
// Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it
Common::SetBit<2>(data[5], Common::ExtractBit<0>(data[5]));
}
else if (mode == PassthroughMode::Classic)
{
// Passthrough data modifications via wiibrew.org
// Verified on real hardware via a test of every bit.
// 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.
Common::SetBit<0>(data[0], Common::ExtractBit<0>(data[5]));
Common::SetBit<0>(data[1], Common::ExtractBit<1>(data[5]));
}
}
void MotionPlus::ReversePassthroughModifications(PassthroughMode mode, u8* data)
{
if (mode == PassthroughMode::Nunchuk)
{
// Undo M+'s "nunchuk passthrough" modifications.
Common::SetBit<0>(data[5], Common::ExtractBit<2>(data[5]));
Common::SetBit<1>(data[5], Common::ExtractBit<3>(data[5]));
Common::SetBit<3>(data[5], Common::ExtractBit<4>(data[5]));
Common::SetBit<0>(data[4], Common::ExtractBit<7>(data[5]));
Common::SetBit<7>(data[5], Common::ExtractBit<6>(data[5]));
// Set the overwritten bits from the next LSB.
Common::SetBit<2>(data[5], Common::ExtractBit<3>(data[5]));
Common::SetBit<4>(data[5], Common::ExtractBit<5>(data[5]));
Common::SetBit<6>(data[5], Common::ExtractBit<7>(data[5]));
}
else if (mode == PassthroughMode::Classic)
{
// Undo M+'s "classic controller passthrough" modifications.
Common::SetBit<0>(data[5], Common::ExtractBit<0>(data[0]));
Common::SetBit<1>(data[5], Common::ExtractBit<0>(data[1]));
// Set the overwritten bits from the next LSB.
Common::SetBit<0>(data[0], Common::ExtractBit<1>(data[0]));
Common::SetBit<0>(data[1], Common::ExtractBit<1>(data[1]));
// This is an overwritten unused button bit on the Classic Controller.
// Note it's a significant bit on the DJ Hero Turntable. (passthrough not feasible)
Common::SetBit<0>(data[4], 1);
}
}
} // namespace WiimoteEmu

View File

@ -7,59 +7,90 @@
#include <array>
#include "Common/CommonTypes.h"
#include "Common/Swap.h"
#include "Core/HW/WiimoteEmu/Dynamics.h"
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
#include "Core/HW/WiimoteEmu/I2CBus.h"
namespace WiimoteEmu
{
struct AngularVelocity;
struct MotionPlus : public Extension
{
public:
MotionPlus();
void Update() override;
void Reset() override;
void DoState(PointerWrap& p) override;
ExtensionPort& GetExtPort();
// Vec3 is interpreted as radians/s about the x,y,z axes following the "right-hand rule".
void PrepareInput(const Common::Vec3& angular_velocity);
private:
enum class ChallengeState : u8
{
// Note: This is not a value seen on a real M+.
// Used to emulate activation state during which the M+ is not responsive.
Activating = 0x00,
PreparingX = 0x02,
ParameterXReady = 0x0e,
PreparingY = 0x14,
ParameterYReady = 0x1a,
};
enum class PassthroughMode : u8
{
// Note: `Disabled` is an M+ enabled with no passthrough. Maybe there is a better name.
Disabled = 0x04,
Nunchuk = 0x05,
Classic = 0x07,
};
enum class ActivationStatus
#pragma pack(push, 1)
struct CalibrationBlock
{
Inactive,
Activating,
Deactivating,
Active,
Common::BigEndianValue<u16> yaw_zero;
Common::BigEndianValue<u16> roll_zero;
Common::BigEndianValue<u16> pitch_zero;
Common::BigEndianValue<u16> yaw_scale;
Common::BigEndianValue<u16> roll_scale;
Common::BigEndianValue<u16> pitch_scale;
u8 degrees_div_6;
};
#pragma pack(push, 1)
struct CalibrationBlocks
{
using GyroType = Common::TVec3<u16>;
using SlowType = Common::TVec3<bool>;
struct RelevantCalibration
{
ControllerEmu::TwoPointCalibration<GyroType, 16> value;
Common::TVec3<u16> degrees;
};
// Each axis may be using either slow or fast calibration.
// This function builds calibration that is relevant for current data.
RelevantCalibration GetRelevantCalibration(SlowType is_slow) const;
CalibrationBlock fast;
CalibrationBlock slow;
};
struct CalibrationData
{
void UpdateChecksum();
CalibrationBlock fast;
u8 uid_1;
Common::BigEndianValue<u16> crc32_msb;
CalibrationBlock slow;
u8 uid_2;
Common::BigEndianValue<u16> crc32_lsb;
};
static_assert(sizeof(CalibrationData) == 0x20, "Wrong size");
struct DataFormat
{
using GyroType = CalibrationBlocks::GyroType;
using SlowType = CalibrationBlocks::SlowType;
using GyroRawValue = ControllerEmu::RawValue<GyroType, 14>;
struct Data
{
// Return radian/s following "right-hand rule" with given calibration blocks.
Common::Vec3 GetAngularVelocity(const CalibrationBlocks&) const;
GyroRawValue gyro;
SlowType is_slow;
};
auto GetData() const
{
return Data{
GyroRawValue{GyroType(pitch1 | pitch2 << 8, roll1 | roll2 << 8, yaw1 | yaw2 << 8)},
SlowType(pitch_slow, roll_slow, yaw_slow)};
}
// yaw1, roll1, pitch1: Bits 0-7
// yaw2, roll2, pitch2: Bits 8-13
@ -79,7 +110,50 @@ private:
u8 is_mp_data : 1;
u8 pitch2 : 6;
};
static_assert(sizeof(DataFormat) == 6, "Wrong size");
#pragma pack(pop)
static constexpr u8 INACTIVE_DEVICE_ADDR = 0x53;
static constexpr u8 ACTIVE_DEVICE_ADDR = 0x52;
static constexpr u8 PASSTHROUGH_MODE_OFFSET = 0xfe;
MotionPlus();
void Update() override;
void Reset() override;
void DoState(PointerWrap& p) override;
ExtensionPort& GetExtPort();
// Vec3 is interpreted as radians/s about the x,y,z axes following the "right-hand rule".
void PrepareInput(const Common::Vec3& angular_velocity);
// Pointer to 6 bytes is expected.
static void ApplyPassthroughModifications(PassthroughMode, u8* data);
static void ReversePassthroughModifications(PassthroughMode, u8* data);
private:
enum class ChallengeState : u8
{
// Note: This is not a value seen on a real M+.
// Used to emulate activation state during which the M+ is not responsive.
Activating = 0x00,
PreparingX = 0x02,
ParameterXReady = 0x0e,
PreparingY = 0x14,
ParameterYReady = 0x1a,
};
enum class ActivationStatus
{
Inactive,
Activating,
Deactivating,
Active,
};
#pragma pack(push, 1)
struct Register
{
std::array<u8, 21> controller_data;
@ -135,14 +209,8 @@ private:
std::array<u8, 6> ext_identifier;
};
#pragma pack(pop)
static_assert(sizeof(DataFormat) == 6, "Wrong size");
static_assert(0x100 == sizeof(Register), "Wrong size");
static constexpr u8 INACTIVE_DEVICE_ADDR = 0x53;
static constexpr u8 ACTIVE_DEVICE_ADDR = 0x52;
static constexpr u8 PASSTHROUGH_MODE_OFFSET = 0xfe;
static constexpr int CALIBRATION_BITS = 16;
static constexpr u16 CALIBRATION_ZERO = 1 << (CALIBRATION_BITS - 1);

View File

@ -500,7 +500,7 @@ void Wiimote::SendDataReport()
if (rpt_builder.HasAccel())
{
// Calibration values are 8-bit but we want 10-bit precision, so << 2.
DataReportBuilder::AccelData accel =
AccelData accel =
ConvertAccelData(GetTotalAcceleration(), ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
rpt_builder.SetAccelData(accel);
}

View File

@ -678,12 +678,13 @@ static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt,
if (rpt.HasAccel())
{
DataReportBuilder::AccelData accel_data;
AccelData accel_data;
rpt.GetAccelData(&accel_data);
// FYI: This will only print partial data for interleaved reports.
display_str += fmt::format(" ACC:{},{},{}", accel_data.x, accel_data.y, accel_data.z);
display_str +=
fmt::format(" ACC:{},{},{}", accel_data.value.x, accel_data.value.y, accel_data.value.z);
}
if (rpt.HasIR())
@ -707,9 +708,8 @@ static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt,
key.Decrypt((u8*)&nunchuk, 0, sizeof(nunchuk));
nunchuk.bt.hex = nunchuk.bt.hex ^ 0x3;
const std::string accel = fmt::format(
" N-ACC:{},{},{}", (nunchuk.ax << 2) | nunchuk.bt.acc_x_lsb,
(nunchuk.ay << 2) | nunchuk.bt.acc_y_lsb, (nunchuk.az << 2) | nunchuk.bt.acc_z_lsb);
const std::string accel = fmt::format(" N-ACC:{},{},{}", nunchuk.GetAccelX(),
nunchuk.GetAccelY(), nunchuk.GetAccelZ());
if (nunchuk.bt.c)
display_str += " C";
@ -756,10 +756,14 @@ static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt,
if (cc.bt.home)
display_str += " HOME";
display_str += Analog1DToString(cc.lt1 | (cc.lt2 << 3), " L", 31);
display_str += Analog1DToString(cc.rt, " R", 31);
display_str += Analog2DToString(cc.lx, cc.ly, " ANA", 63);
display_str += Analog2DToString(cc.rx1 | (cc.rx2 << 1) | (cc.rx3 << 3), cc.ry, " R-ANA", 31);
display_str += Analog1DToString(cc.GetLeftTrigger().value, " L", 31);
display_str += Analog1DToString(cc.GetRightTrigger().value, " R", 31);
const auto left_stick = cc.GetLeftStick().value;
display_str += Analog2DToString(left_stick.x, left_stick.y, " ANA", 63);
const auto right_stick = cc.GetRightStick().value;
display_str += Analog2DToString(right_stick.x, right_stick.y, " R-ANA", 31);
}
std::lock_guard<std::mutex> guard(s_input_display_lock);

View File

@ -334,18 +334,20 @@ void WiiTASInputWindow::GetValues(DataReportBuilder& rpt, int ext,
DataReportBuilder::CoreData core;
rpt.GetCoreData(&core);
using EmuWiimote = WiimoteEmu::Wiimote;
u16& buttons = core.hex;
GetButton<u16>(m_a_button, buttons, WiimoteEmu::Wiimote::BUTTON_A);
GetButton<u16>(m_b_button, buttons, WiimoteEmu::Wiimote::BUTTON_B);
GetButton<u16>(m_1_button, buttons, WiimoteEmu::Wiimote::BUTTON_ONE);
GetButton<u16>(m_2_button, buttons, WiimoteEmu::Wiimote::BUTTON_TWO);
GetButton<u16>(m_plus_button, buttons, WiimoteEmu::Wiimote::BUTTON_PLUS);
GetButton<u16>(m_minus_button, buttons, WiimoteEmu::Wiimote::BUTTON_MINUS);
GetButton<u16>(m_home_button, buttons, WiimoteEmu::Wiimote::BUTTON_HOME);
GetButton<u16>(m_left_button, buttons, WiimoteEmu::Wiimote::PAD_LEFT);
GetButton<u16>(m_up_button, buttons, WiimoteEmu::Wiimote::PAD_UP);
GetButton<u16>(m_down_button, buttons, WiimoteEmu::Wiimote::PAD_DOWN);
GetButton<u16>(m_right_button, buttons, WiimoteEmu::Wiimote::PAD_RIGHT);
GetButton<u16>(m_a_button, buttons, EmuWiimote::BUTTON_A);
GetButton<u16>(m_b_button, buttons, EmuWiimote::BUTTON_B);
GetButton<u16>(m_1_button, buttons, EmuWiimote::BUTTON_ONE);
GetButton<u16>(m_2_button, buttons, EmuWiimote::BUTTON_TWO);
GetButton<u16>(m_plus_button, buttons, EmuWiimote::BUTTON_PLUS);
GetButton<u16>(m_minus_button, buttons, EmuWiimote::BUTTON_MINUS);
GetButton<u16>(m_home_button, buttons, EmuWiimote::BUTTON_HOME);
GetButton<u16>(m_left_button, buttons, EmuWiimote::PAD_LEFT);
GetButton<u16>(m_up_button, buttons, EmuWiimote::PAD_UP);
GetButton<u16>(m_down_button, buttons, EmuWiimote::PAD_DOWN);
GetButton<u16>(m_right_button, buttons, EmuWiimote::PAD_RIGHT);
rpt.SetCoreData(core);
}
@ -354,12 +356,12 @@ void WiiTASInputWindow::GetValues(DataReportBuilder& rpt, int ext,
{
// FYI: Interleaved reports may behave funky as not all data is always available.
DataReportBuilder::AccelData accel;
AccelData accel;
rpt.GetAccelData(&accel);
GetSpinBoxU16(m_remote_orientation_x_value, accel.x);
GetSpinBoxU16(m_remote_orientation_y_value, accel.y);
GetSpinBoxU16(m_remote_orientation_z_value, accel.z);
GetSpinBoxU16(m_remote_orientation_x_value, accel.value.x);
GetSpinBoxU16(m_remote_orientation_y_value, accel.value.y);
GetSpinBoxU16(m_remote_orientation_z_value, accel.value.z);
rpt.SetAccelData(accel);
}
@ -439,26 +441,16 @@ void WiiTASInputWindow::GetValues(DataReportBuilder& rpt, int ext,
GetSpinBoxU8(m_nunchuk_stick_x_value, nunchuk.jx);
GetSpinBoxU8(m_nunchuk_stick_y_value, nunchuk.jy);
u16 accel_x = nunchuk.ax << 2 & (nunchuk.bt.acc_x_lsb & 0b11);
u16 accel_y = nunchuk.ay << 2 & (nunchuk.bt.acc_y_lsb & 0b11);
u16 accel_z = nunchuk.az << 2 & (nunchuk.bt.acc_z_lsb & 0b11);
auto accel = nunchuk.GetAccel().value;
GetSpinBoxU16(m_nunchuk_orientation_x_value, accel.x);
GetSpinBoxU16(m_nunchuk_orientation_y_value, accel.y);
GetSpinBoxU16(m_nunchuk_orientation_z_value, accel.z);
nunchuk.SetAccel(accel);
GetSpinBoxU16(m_nunchuk_orientation_x_value, accel_x);
GetSpinBoxU16(m_nunchuk_orientation_y_value, accel_y);
GetSpinBoxU16(m_nunchuk_orientation_z_value, accel_z);
nunchuk.ax = accel_x >> 2;
nunchuk.ay = accel_y >> 2;
nunchuk.az = accel_z >> 2;
nunchuk.bt.acc_x_lsb = accel_x & 0b11;
nunchuk.bt.acc_y_lsb = accel_y & 0b11;
nunchuk.bt.acc_z_lsb = accel_z & 0b11;
nunchuk.bt.hex ^= 0b11;
GetButton<u8>(m_c_button, nunchuk.bt.hex, WiimoteEmu::Nunchuk::BUTTON_C);
GetButton<u8>(m_z_button, nunchuk.bt.hex, WiimoteEmu::Nunchuk::BUTTON_Z);
nunchuk.bt.hex ^= 0b11;
u8 bt = nunchuk.GetButtons();
GetButton<u8>(m_c_button, bt, WiimoteEmu::Nunchuk::BUTTON_C);
GetButton<u8>(m_z_button, bt, WiimoteEmu::Nunchuk::BUTTON_Z);
nunchuk.SetButtons(bt);
key.Encrypt(reinterpret_cast<u8*>(&nunchuk), 0, sizeof(nunchuk));
}
@ -470,50 +462,41 @@ void WiiTASInputWindow::GetValues(DataReportBuilder& rpt, int ext,
auto& cc = *reinterpret_cast<WiimoteEmu::Classic::DataFormat*>(ext_data);
key.Decrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
cc.bt.hex ^= 0xFFFF;
GetButton<u16>(m_classic_a_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_A);
GetButton<u16>(m_classic_b_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_B);
GetButton<u16>(m_classic_x_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_X);
GetButton<u16>(m_classic_y_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_Y);
GetButton<u16>(m_classic_plus_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_PLUS);
GetButton<u16>(m_classic_minus_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_MINUS);
GetButton<u16>(m_classic_l_button, cc.bt.hex, WiimoteEmu::Classic::TRIGGER_L);
GetButton<u16>(m_classic_r_button, cc.bt.hex, WiimoteEmu::Classic::TRIGGER_R);
GetButton<u16>(m_classic_zl_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_ZL);
GetButton<u16>(m_classic_zr_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_ZR);
GetButton<u16>(m_classic_home_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_HOME);
GetButton<u16>(m_classic_left_button, cc.bt.hex, WiimoteEmu::Classic::PAD_LEFT);
GetButton<u16>(m_classic_up_button, cc.bt.hex, WiimoteEmu::Classic::PAD_UP);
GetButton<u16>(m_classic_down_button, cc.bt.hex, WiimoteEmu::Classic::PAD_DOWN);
GetButton<u16>(m_classic_right_button, cc.bt.hex, WiimoteEmu::Classic::PAD_RIGHT);
cc.bt.hex ^= 0xFFFF;
u16 bt = cc.GetButtons();
GetButton<u16>(m_classic_a_button, bt, WiimoteEmu::Classic::BUTTON_A);
GetButton<u16>(m_classic_b_button, bt, WiimoteEmu::Classic::BUTTON_B);
GetButton<u16>(m_classic_x_button, bt, WiimoteEmu::Classic::BUTTON_X);
GetButton<u16>(m_classic_y_button, bt, WiimoteEmu::Classic::BUTTON_Y);
GetButton<u16>(m_classic_plus_button, bt, WiimoteEmu::Classic::BUTTON_PLUS);
GetButton<u16>(m_classic_minus_button, bt, WiimoteEmu::Classic::BUTTON_MINUS);
GetButton<u16>(m_classic_l_button, bt, WiimoteEmu::Classic::TRIGGER_L);
GetButton<u16>(m_classic_r_button, bt, WiimoteEmu::Classic::TRIGGER_R);
GetButton<u16>(m_classic_zl_button, bt, WiimoteEmu::Classic::BUTTON_ZL);
GetButton<u16>(m_classic_zr_button, bt, WiimoteEmu::Classic::BUTTON_ZR);
GetButton<u16>(m_classic_home_button, bt, WiimoteEmu::Classic::BUTTON_HOME);
GetButton<u16>(m_classic_left_button, bt, WiimoteEmu::Classic::PAD_LEFT);
GetButton<u16>(m_classic_up_button, bt, WiimoteEmu::Classic::PAD_UP);
GetButton<u16>(m_classic_down_button, bt, WiimoteEmu::Classic::PAD_DOWN);
GetButton<u16>(m_classic_right_button, bt, WiimoteEmu::Classic::PAD_RIGHT);
cc.SetButtons(bt);
u8 rx = (cc.rx1 & 0b1) & ((cc.rx2 & 0b11) << 1) & ((cc.rx3 & 0b11) << 3);
GetSpinBoxU8(m_classic_right_stick_x_value, rx);
cc.rx1 = rx & 0b1;
cc.rx2 = (rx >> 1) & 0b11;
cc.rx3 = (rx >> 3) & 0b11;
auto right_stick = cc.GetRightStick().value;
GetSpinBoxU8(m_classic_right_stick_x_value, right_stick.x);
GetSpinBoxU8(m_classic_right_stick_y_value, right_stick.y);
cc.SetRightStick(right_stick);
u8 ry = cc.ry;
GetSpinBoxU8(m_classic_right_stick_y_value, ry);
cc.ry = ry;
auto left_stick = cc.GetLeftStick().value;
GetSpinBoxU8(m_classic_left_stick_x_value, left_stick.x);
GetSpinBoxU8(m_classic_left_stick_y_value, left_stick.y);
cc.SetLeftStick(left_stick);
u8 lx = cc.lx;
GetSpinBoxU8(m_classic_left_stick_x_value, lx);
cc.lx = lx;
u8 ly = cc.ly;
GetSpinBoxU8(m_classic_left_stick_y_value, ly);
cc.ly = ly;
u8 rt = cc.rt;
u8 rt = cc.GetRightTrigger().value;
GetSpinBoxU8(m_right_trigger_value, rt);
cc.rt = rt;
cc.SetRightTrigger(rt);
u8 lt = (cc.lt1 & 0b111) & (cc.lt2 >> 3);
u8 lt = cc.GetLeftTrigger().value;
GetSpinBoxU8(m_left_trigger_value, lt);
cc.lt1 = lt & 0b111;
cc.lt2 = (lt >> 3) & 0b11;
cc.SetLeftTrigger(lt);
key.Encrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
}