Core/WiimoteEmu: Add functions to Nunchuk, Classic Controller, and MotionPlus extensions to get/set data without duplicate bithacks everywhere.
This commit is contained in:
parent
8343dadd58
commit
5af2081c75
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue