mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-14 00:09:24 +01:00
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 <cassert>
|
||||||
|
|
||||||
#include "Common/BitUtils.h"
|
#include "Common/BitUtils.h"
|
||||||
|
#include "Common/MathUtil.h"
|
||||||
#include "Core/HW/WiimoteCommon/DataReport.h"
|
#include "Core/HW/WiimoteCommon/DataReport.h"
|
||||||
|
|
||||||
namespace WiimoteCommon
|
namespace WiimoteCommon
|
||||||
@ -75,40 +76,35 @@ struct IncludeAccel : virtual DataReportManipulator
|
|||||||
void GetAccelData(AccelData* result) const override
|
void GetAccelData(AccelData* result) const override
|
||||||
{
|
{
|
||||||
const AccelMSB accel = Common::BitCastPtr<AccelMSB>(data_ptr + 2);
|
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);
|
const CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
|
||||||
result->x |= core.acc_bits & 0b11;
|
|
||||||
result->y |= (core.acc_bits2 & 0b1) << 1;
|
// X has 10 bits of precision.
|
||||||
result->z |= core.acc_bits2 & 0b10;
|
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
|
void SetAccelData(const AccelData& new_accel) override
|
||||||
{
|
{
|
||||||
AccelMSB accel = {};
|
Common::BitCastPtr<AccelMSB>(data_ptr + 2) = AccelMSB(new_accel.value / 4);
|
||||||
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;
|
|
||||||
|
|
||||||
// LSBs
|
// LSBs
|
||||||
CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
|
CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
|
||||||
core.acc_bits = (new_accel.x >> 0) & 0b11;
|
core.acc_bits = (new_accel.value.x >> 0) & 0b11;
|
||||||
core.acc_bits2 = (new_accel.y >> 1) & 0x1;
|
core.acc_bits2 = (new_accel.value.y >> 1) & 0x1;
|
||||||
core.acc_bits2 |= (new_accel.z & 0xb10);
|
core.acc_bits2 |= (new_accel.value.z & 0xb10);
|
||||||
Common::BitCastPtr<CoreData>(data_ptr) = core;
|
Common::BitCastPtr<CoreData>(data_ptr) = core;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasAccel() const override { return true; }
|
bool HasAccel() const override { return true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct AccelMSB
|
using AccelMSB = Common::TVec3<u8>;
|
||||||
{
|
|
||||||
u8 x, y, z;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(AccelMSB) == 3, "Wrong size");
|
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
|
struct ReportInterleave1 : IncludeCore, IncludeIR<3, 18, 0>, NoExt
|
||||||
{
|
{
|
||||||
// FYI: Only 8-bits of precision in this report, and no Y axis.
|
// 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
|
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.
|
// Y axis is not contained in this report. (provided by "Interleave2")
|
||||||
accel->z &= 0b111111;
|
|
||||||
|
|
||||||
|
// 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);
|
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
|
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);
|
CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
|
||||||
core.acc_bits = (accel.z >> 6) & 0b11;
|
core.acc_bits = (accel.value.z >> 6) & 0b11;
|
||||||
core.acc_bits2 = (accel.z >> 8) & 0b11;
|
core.acc_bits2 = (accel.value.z >> 8) & 0b11;
|
||||||
Common::BitCastPtr<CoreData>(data_ptr) = core;
|
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
|
struct ReportInterleave2 : IncludeCore, IncludeIR<3, 18, 18>, NoExt
|
||||||
{
|
{
|
||||||
// FYI: Only 8-bits of precision in this report, and no X axis.
|
// 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
|
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.
|
// Y axis only has 8 bits of precision. (converted to 10)
|
||||||
accel->z &= ~0b111111;
|
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);
|
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
|
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);
|
CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
|
||||||
core.acc_bits = (accel.z >> 2) & 0b11;
|
core.acc_bits = (accel.value.z >> 2) & 0b11;
|
||||||
core.acc_bits2 = (accel.z >> 4) & 0b11;
|
core.acc_bits2 = (accel.value.z >> 4) & 0b11;
|
||||||
Common::BitCastPtr<CoreData>(data_ptr) = core;
|
Common::BitCastPtr<CoreData>(data_ptr) = core;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,11 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Matrix.h"
|
||||||
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
|
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
|
||||||
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
|
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
|
||||||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||||
|
#include "InputCommon/ControllerEmu/ControllerEmu.h"
|
||||||
|
|
||||||
namespace WiimoteCommon
|
namespace WiimoteCommon
|
||||||
{
|
{
|
||||||
@ -21,12 +23,6 @@ class DataReportManipulator
|
|||||||
public:
|
public:
|
||||||
virtual ~DataReportManipulator() = default;
|
virtual ~DataReportManipulator() = default;
|
||||||
|
|
||||||
// Accel data handled as if there were always 10 bits of precision.
|
|
||||||
struct AccelData
|
|
||||||
{
|
|
||||||
u16 x, y, z;
|
|
||||||
};
|
|
||||||
|
|
||||||
using CoreData = ButtonData;
|
using CoreData = ButtonData;
|
||||||
|
|
||||||
virtual bool HasCore() const = 0;
|
virtual bool HasCore() const = 0;
|
||||||
@ -66,7 +62,6 @@ public:
|
|||||||
explicit DataReportBuilder(InputReportID rpt_id);
|
explicit DataReportBuilder(InputReportID rpt_id);
|
||||||
|
|
||||||
using CoreData = ButtonData;
|
using CoreData = ButtonData;
|
||||||
using AccelData = DataReportManipulator::AccelData;
|
|
||||||
|
|
||||||
void SetMode(InputReportID rpt_id);
|
void SetMode(InputReportID rpt_id);
|
||||||
InputReportID GetMode() const;
|
InputReportID GetMode() const;
|
||||||
@ -99,11 +94,10 @@ public:
|
|||||||
|
|
||||||
u32 GetDataSize() const;
|
u32 GetDataSize() const;
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr int HEADER_SIZE = 2;
|
static constexpr int HEADER_SIZE = 2;
|
||||||
|
|
||||||
static constexpr int MAX_DATA_SIZE = MAX_PAYLOAD - 2;
|
static constexpr int MAX_DATA_SIZE = MAX_PAYLOAD - 2;
|
||||||
|
|
||||||
|
private:
|
||||||
TypedHIDInputData<std::array<u8, MAX_DATA_SIZE>> m_data;
|
TypedHIDInputData<std::array<u8, MAX_DATA_SIZE>> m_data;
|
||||||
|
|
||||||
std::unique_ptr<DataReportManipulator> m_manip;
|
std::unique_ptr<DataReportManipulator> m_manip;
|
||||||
|
@ -10,6 +10,10 @@ namespace WiimoteCommon
|
|||||||
{
|
{
|
||||||
constexpr u8 MAX_PAYLOAD = 23;
|
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
|
enum class InputReportID : u8
|
||||||
{
|
{
|
||||||
Status = 0x20,
|
Status = 0x20,
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Matrix.h"
|
||||||
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
|
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
|
||||||
|
#include "InputCommon/ControllerEmu/ControllerEmu.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
@ -41,6 +43,8 @@ static_assert(sizeof(OutputReportGeneric) == 2, "Wrong size");
|
|||||||
|
|
||||||
struct OutputReportRumble
|
struct OutputReportRumble
|
||||||
{
|
{
|
||||||
|
static constexpr OutputReportID REPORT_ID = OutputReportID::Rumble;
|
||||||
|
|
||||||
u8 rumble : 1;
|
u8 rumble : 1;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputReportRumble) == 1, "Wrong size");
|
static_assert(sizeof(OutputReportRumble) == 1, "Wrong size");
|
||||||
@ -55,8 +59,34 @@ struct OutputReportEnableFeature
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(OutputReportEnableFeature) == 1, "Wrong size");
|
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
|
struct OutputReportLeds
|
||||||
{
|
{
|
||||||
|
static constexpr OutputReportID REPORT_ID = OutputReportID::LED;
|
||||||
|
|
||||||
u8 rumble : 1;
|
u8 rumble : 1;
|
||||||
u8 ack : 1;
|
u8 ack : 1;
|
||||||
u8 : 2;
|
u8 : 2;
|
||||||
@ -66,6 +96,8 @@ static_assert(sizeof(OutputReportLeds) == 1, "Wrong size");
|
|||||||
|
|
||||||
struct OutputReportMode
|
struct OutputReportMode
|
||||||
{
|
{
|
||||||
|
static constexpr OutputReportID REPORT_ID = OutputReportID::ReportMode;
|
||||||
|
|
||||||
u8 rumble : 1;
|
u8 rumble : 1;
|
||||||
u8 ack : 1;
|
u8 ack : 1;
|
||||||
u8 continuous : 1;
|
u8 continuous : 1;
|
||||||
@ -76,6 +108,8 @@ static_assert(sizeof(OutputReportMode) == 2, "Wrong size");
|
|||||||
|
|
||||||
struct OutputReportRequestStatus
|
struct OutputReportRequestStatus
|
||||||
{
|
{
|
||||||
|
static constexpr OutputReportID REPORT_ID = OutputReportID::RequestStatus;
|
||||||
|
|
||||||
u8 rumble : 1;
|
u8 rumble : 1;
|
||||||
u8 : 7;
|
u8 : 7;
|
||||||
};
|
};
|
||||||
@ -83,6 +117,8 @@ static_assert(sizeof(OutputReportRequestStatus) == 1, "Wrong size");
|
|||||||
|
|
||||||
struct OutputReportWriteData
|
struct OutputReportWriteData
|
||||||
{
|
{
|
||||||
|
static constexpr OutputReportID REPORT_ID = OutputReportID::WriteData;
|
||||||
|
|
||||||
u8 rumble : 1;
|
u8 rumble : 1;
|
||||||
u8 : 1;
|
u8 : 1;
|
||||||
u8 space : 2;
|
u8 space : 2;
|
||||||
@ -100,6 +136,8 @@ static_assert(sizeof(OutputReportWriteData) == 21, "Wrong size");
|
|||||||
|
|
||||||
struct OutputReportReadData
|
struct OutputReportReadData
|
||||||
{
|
{
|
||||||
|
static constexpr OutputReportID REPORT_ID = OutputReportID::ReadData;
|
||||||
|
|
||||||
u8 rumble : 1;
|
u8 rumble : 1;
|
||||||
u8 : 1;
|
u8 : 1;
|
||||||
u8 space : 2;
|
u8 space : 2;
|
||||||
@ -116,6 +154,8 @@ static_assert(sizeof(OutputReportReadData) == 6, "Wrong size");
|
|||||||
|
|
||||||
struct OutputReportSpeakerData
|
struct OutputReportSpeakerData
|
||||||
{
|
{
|
||||||
|
static constexpr OutputReportID REPORT_ID = OutputReportID::SpeakerData;
|
||||||
|
|
||||||
u8 rumble : 1;
|
u8 rumble : 1;
|
||||||
u8 : 2;
|
u8 : 2;
|
||||||
u8 length : 5;
|
u8 length : 5;
|
||||||
@ -157,6 +197,8 @@ static_assert(sizeof(ButtonData) == 2, "Wrong size");
|
|||||||
|
|
||||||
struct InputReportStatus
|
struct InputReportStatus
|
||||||
{
|
{
|
||||||
|
static constexpr InputReportID REPORT_ID = InputReportID::Status;
|
||||||
|
|
||||||
ButtonData buttons;
|
ButtonData buttons;
|
||||||
u8 battery_low : 1;
|
u8 battery_low : 1;
|
||||||
u8 extension : 1;
|
u8 extension : 1;
|
||||||
@ -170,6 +212,8 @@ static_assert(sizeof(InputReportStatus) == 6, "Wrong size");
|
|||||||
|
|
||||||
struct InputReportAck
|
struct InputReportAck
|
||||||
{
|
{
|
||||||
|
static constexpr InputReportID REPORT_ID = InputReportID::Ack;
|
||||||
|
|
||||||
ButtonData buttons;
|
ButtonData buttons;
|
||||||
OutputReportID rpt_id;
|
OutputReportID rpt_id;
|
||||||
ErrorCode error_code;
|
ErrorCode error_code;
|
||||||
@ -178,6 +222,8 @@ static_assert(sizeof(InputReportAck) == 4, "Wrong size");
|
|||||||
|
|
||||||
struct InputReportReadDataReply
|
struct InputReportReadDataReply
|
||||||
{
|
{
|
||||||
|
static constexpr InputReportID REPORT_ID = InputReportID::ReadDataReply;
|
||||||
|
|
||||||
ButtonData buttons;
|
ButtonData buttons;
|
||||||
u8 error : 4;
|
u8 error : 4;
|
||||||
u8 size_minus_one : 4;
|
u8 size_minus_one : 4;
|
||||||
@ -187,6 +233,64 @@ struct InputReportReadDataReply
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(InputReportReadDataReply) == 21, "Wrong size");
|
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
|
} // namespace WiimoteCommon
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
@ -114,8 +114,10 @@ void Classic::Update()
|
|||||||
{
|
{
|
||||||
const ControllerEmu::AnalogStick::StateData left_stick_state = m_left_stick->GetState();
|
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));
|
const u8 x = 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 y = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.y * LEFT_STICK_RADIUS));
|
||||||
|
|
||||||
|
classic_data.SetLeftStick({x, y});
|
||||||
}
|
}
|
||||||
|
|
||||||
// right stick
|
// 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 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));
|
const u8 y = static_cast<u8>(RIGHT_STICK_CENTER + (right_stick_data.y * RIGHT_STICK_RADIUS));
|
||||||
|
|
||||||
classic_data.rx1 = x;
|
classic_data.SetRightStick({x, y});
|
||||||
classic_data.rx2 = x >> 1;
|
|
||||||
classic_data.rx3 = x >> 3;
|
|
||||||
classic_data.ry = y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// triggers
|
// triggers
|
||||||
@ -139,18 +138,15 @@ void Classic::Update()
|
|||||||
const u8 lt = static_cast<u8>(trigs[0] * TRIGGER_RANGE);
|
const u8 lt = static_cast<u8>(trigs[0] * TRIGGER_RANGE);
|
||||||
const u8 rt = static_cast<u8>(trigs[1] * TRIGGER_RANGE);
|
const u8 rt = static_cast<u8>(trigs[1] * TRIGGER_RANGE);
|
||||||
|
|
||||||
classic_data.lt1 = lt;
|
classic_data.SetLeftTrigger(lt);
|
||||||
classic_data.lt2 = lt >> 3;
|
classic_data.SetRightTrigger(rt);
|
||||||
classic_data.rt = rt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// buttons
|
// buttons and dpad
|
||||||
m_buttons->GetState(&classic_data.bt.hex, classic_button_bitmasks.data());
|
u16 buttons = 0;
|
||||||
// dpad
|
m_buttons->GetState(&buttons, classic_button_bitmasks.data());
|
||||||
m_dpad->GetState(&classic_data.bt.hex, classic_dpad_bitmasks.data());
|
m_dpad->GetState(&buttons, classic_dpad_bitmasks.data());
|
||||||
|
classic_data.SetButtons(buttons);
|
||||||
// flip button bits
|
|
||||||
classic_data.bt.hex ^= 0xFFFF;
|
|
||||||
|
|
||||||
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = classic_data;
|
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = classic_data;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "Common/Matrix.h"
|
||||||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||||
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
|
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
|
||||||
|
|
||||||
@ -56,12 +59,59 @@ public:
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(ButtonFormat) == 2, "Wrong size");
|
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
|
struct DataFormat
|
||||||
{
|
{
|
||||||
// lx/ly/lz; left joystick
|
using StickType = Common::TVec2<u8>;
|
||||||
// rx/ry/rz; right joystick
|
using LeftStickRawValue = ControllerEmu::RawValue<StickType, LEFT_STICK_BITS>;
|
||||||
// lt; left trigger
|
using RightStickRawValue = ControllerEmu::RawValue<StickType, RIGHT_STICK_BITS>;
|
||||||
// rt; left trigger
|
|
||||||
|
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 lx : 6; // byte 0
|
||||||
u8 rx3 : 2;
|
u8 rx3 : 2;
|
||||||
@ -80,6 +130,53 @@ public:
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(DataFormat) == 6, "Wrong size");
|
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();
|
Classic();
|
||||||
|
|
||||||
void Update() override;
|
void Update() override;
|
||||||
@ -110,13 +207,10 @@ public:
|
|||||||
|
|
||||||
static constexpr u8 CAL_STICK_CENTER = 0x80;
|
static constexpr u8 CAL_STICK_CENTER = 0x80;
|
||||||
static constexpr u8 CAL_STICK_RANGE = 0x7f;
|
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_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 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_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);
|
static constexpr u8 RIGHT_STICK_RADIUS = CAL_STICK_RANGE >> (CAL_STICK_BITS - RIGHT_STICK_BITS);
|
||||||
|
|
||||||
|
@ -87,10 +87,9 @@ void Nunchuk::Update()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buttons
|
// buttons
|
||||||
m_buttons->GetState(&nc_data.bt.hex, nunchuk_button_bitmasks.data());
|
u8 buttons = 0;
|
||||||
|
m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data());
|
||||||
// flip the button bits :/
|
nc_data.SetButtons(buttons);
|
||||||
nc_data.bt.hex ^= 0x03;
|
|
||||||
|
|
||||||
// Acceleration data:
|
// Acceleration data:
|
||||||
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);
|
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.
|
// 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);
|
const auto acc = ConvertAccelData(accel, ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
|
||||||
|
nc_data.SetAccel(acc.value);
|
||||||
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;
|
|
||||||
|
|
||||||
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = nc_data;
|
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = nc_data;
|
||||||
}
|
}
|
||||||
|
@ -51,25 +51,103 @@ public:
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(ButtonFormat) == 1, "Wrong size");
|
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
|
ax = val >> 2;
|
||||||
u8 jx;
|
bt.acc_x_lsb = val & 0b11;
|
||||||
u8 jy;
|
}
|
||||||
|
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 GetButtons() const
|
||||||
u8 ax;
|
{
|
||||||
u8 ay;
|
// 0 == pressed.
|
||||||
u8 az;
|
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
|
// joystick x, y
|
||||||
ButtonFormat bt;
|
u8 jx;
|
||||||
};
|
u8 jy;
|
||||||
|
|
||||||
|
// accelerometer
|
||||||
|
u8 ax;
|
||||||
|
u8 ay;
|
||||||
|
u8 az;
|
||||||
|
|
||||||
|
// buttons + accelerometer LSBs
|
||||||
|
ButtonFormat bt;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(DataFormat) == 6, "Wrong size");
|
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();
|
Nunchuk();
|
||||||
|
|
||||||
void Update() override;
|
void Update() override;
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/MathUtil.h"
|
#include "Common/MathUtil.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Common/Swap.h"
|
|
||||||
|
|
||||||
#include "Core/HW/Wiimote.h"
|
#include "Core/HW/Wiimote.h"
|
||||||
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
||||||
@ -56,6 +55,41 @@ struct MPI : mbedtls_mpi
|
|||||||
|
|
||||||
namespace WiimoteEmu
|
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")
|
MotionPlus::MotionPlus() : Extension("MotionPlus")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -82,35 +116,20 @@ void MotionPlus::Reset()
|
|||||||
constexpr u16 ROLL_SCALE = CALIBRATION_ZERO + CALIBRATION_SCALE_OFFSET;
|
constexpr u16 ROLL_SCALE = CALIBRATION_ZERO + CALIBRATION_SCALE_OFFSET;
|
||||||
constexpr u16 PITCH_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(sizeof(CalibrationData) == 0x20, "Bad size.");
|
||||||
|
|
||||||
static_assert(CALIBRATION_FAST_SCALE_DEGREES % 6 == 0, "Value should be divisible by 6.");
|
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.");
|
static_assert(CALIBRATION_SLOW_SCALE_DEGREES % 6 == 0, "Value should be divisible by 6.");
|
||||||
|
|
||||||
CalibrationData calibration;
|
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.fast.degrees_div_6 = CALIBRATION_FAST_SCALE_DEGREES / 6;
|
||||||
calibration.slow.degrees_div_6 = CALIBRATION_SLOW_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_1 = 0x0b;
|
||||||
calibration.uid_2 = 0xe9;
|
calibration.uid_2 = 0xe9;
|
||||||
|
|
||||||
// Update checksum (crc32 of all data other than the checksum itself):
|
calibration.UpdateChecksum();
|
||||||
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);
|
|
||||||
|
|
||||||
Common::BitCastPtr<CalibrationData>(m_reg_data.calibration_data.data()) = calibration;
|
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)
|
void MotionPlus::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
p.Do(m_reg_data);
|
p.Do(m_reg_data);
|
||||||
@ -547,47 +571,10 @@ void MotionPlus::PrepareInput(const Common::Vec3& angular_velocity)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PassthroughMode::Nunchuk:
|
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:
|
case PassthroughMode::Classic:
|
||||||
{
|
|
||||||
if (EXT_AMT == m_i2c_bus.BusRead(EXT_SLAVE, EXT_ADDR, EXT_AMT, data))
|
if (EXT_AMT == m_i2c_bus.BusRead(EXT_SLAVE, EXT_ADDR, EXT_AMT, data))
|
||||||
{
|
{
|
||||||
// Passthrough data modifications via wiibrew.org
|
ApplyPassthroughModifications(GetPassthroughMode(), data);
|
||||||
// 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));
|
|
||||||
|
|
||||||
mplus_data = Common::BitCastPtr<DataFormat>(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.
|
// 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;
|
mplus_data.is_mp_data = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
// This really shouldn't happen as the M+ deactivates on an invalid mode write.
|
// This really shouldn't happen as the M+ deactivates on an invalid mode write.
|
||||||
ERROR_LOG(WIIMOTE, "M+ unknown passthrough-mode %d", int(GetPassthroughMode()));
|
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;
|
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
|
} // namespace WiimoteEmu
|
||||||
|
@ -7,59 +7,90 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Swap.h"
|
||||||
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
||||||
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
|
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
|
||||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
||||||
|
|
||||||
namespace WiimoteEmu
|
namespace WiimoteEmu
|
||||||
{
|
{
|
||||||
struct AngularVelocity;
|
|
||||||
|
|
||||||
struct MotionPlus : public Extension
|
struct MotionPlus : public Extension
|
||||||
{
|
{
|
||||||
public:
|
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
|
enum class PassthroughMode : u8
|
||||||
{
|
{
|
||||||
|
// Note: `Disabled` is an M+ enabled with no passthrough. Maybe there is a better name.
|
||||||
Disabled = 0x04,
|
Disabled = 0x04,
|
||||||
Nunchuk = 0x05,
|
Nunchuk = 0x05,
|
||||||
Classic = 0x07,
|
Classic = 0x07,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ActivationStatus
|
#pragma pack(push, 1)
|
||||||
|
struct CalibrationBlock
|
||||||
{
|
{
|
||||||
Inactive,
|
Common::BigEndianValue<u16> yaw_zero;
|
||||||
Activating,
|
Common::BigEndianValue<u16> roll_zero;
|
||||||
Deactivating,
|
Common::BigEndianValue<u16> pitch_zero;
|
||||||
Active,
|
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
|
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
|
// yaw1, roll1, pitch1: Bits 0-7
|
||||||
// yaw2, roll2, pitch2: Bits 8-13
|
// yaw2, roll2, pitch2: Bits 8-13
|
||||||
|
|
||||||
@ -79,7 +110,50 @@ private:
|
|||||||
u8 is_mp_data : 1;
|
u8 is_mp_data : 1;
|
||||||
u8 pitch2 : 6;
|
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
|
struct Register
|
||||||
{
|
{
|
||||||
std::array<u8, 21> controller_data;
|
std::array<u8, 21> controller_data;
|
||||||
@ -135,14 +209,8 @@ private:
|
|||||||
std::array<u8, 6> ext_identifier;
|
std::array<u8, 6> ext_identifier;
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
static_assert(sizeof(DataFormat) == 6, "Wrong size");
|
|
||||||
static_assert(0x100 == sizeof(Register), "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 int CALIBRATION_BITS = 16;
|
||||||
|
|
||||||
static constexpr u16 CALIBRATION_ZERO = 1 << (CALIBRATION_BITS - 1);
|
static constexpr u16 CALIBRATION_ZERO = 1 << (CALIBRATION_BITS - 1);
|
||||||
|
@ -500,7 +500,7 @@ void Wiimote::SendDataReport()
|
|||||||
if (rpt_builder.HasAccel())
|
if (rpt_builder.HasAccel())
|
||||||
{
|
{
|
||||||
// Calibration values are 8-bit but we want 10-bit precision, so << 2.
|
// 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);
|
ConvertAccelData(GetTotalAcceleration(), ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
|
||||||
rpt_builder.SetAccelData(accel);
|
rpt_builder.SetAccelData(accel);
|
||||||
}
|
}
|
||||||
|
@ -678,12 +678,13 @@ static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt,
|
|||||||
|
|
||||||
if (rpt.HasAccel())
|
if (rpt.HasAccel())
|
||||||
{
|
{
|
||||||
DataReportBuilder::AccelData accel_data;
|
AccelData accel_data;
|
||||||
rpt.GetAccelData(&accel_data);
|
rpt.GetAccelData(&accel_data);
|
||||||
|
|
||||||
// FYI: This will only print partial data for interleaved reports.
|
// 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())
|
if (rpt.HasIR())
|
||||||
@ -707,9 +708,8 @@ static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt,
|
|||||||
key.Decrypt((u8*)&nunchuk, 0, sizeof(nunchuk));
|
key.Decrypt((u8*)&nunchuk, 0, sizeof(nunchuk));
|
||||||
nunchuk.bt.hex = nunchuk.bt.hex ^ 0x3;
|
nunchuk.bt.hex = nunchuk.bt.hex ^ 0x3;
|
||||||
|
|
||||||
const std::string accel = fmt::format(
|
const std::string accel = fmt::format(" N-ACC:{},{},{}", nunchuk.GetAccelX(),
|
||||||
" N-ACC:{},{},{}", (nunchuk.ax << 2) | nunchuk.bt.acc_x_lsb,
|
nunchuk.GetAccelY(), nunchuk.GetAccelZ());
|
||||||
(nunchuk.ay << 2) | nunchuk.bt.acc_y_lsb, (nunchuk.az << 2) | nunchuk.bt.acc_z_lsb);
|
|
||||||
|
|
||||||
if (nunchuk.bt.c)
|
if (nunchuk.bt.c)
|
||||||
display_str += " C";
|
display_str += " C";
|
||||||
@ -756,10 +756,14 @@ static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt,
|
|||||||
if (cc.bt.home)
|
if (cc.bt.home)
|
||||||
display_str += " HOME";
|
display_str += " HOME";
|
||||||
|
|
||||||
display_str += Analog1DToString(cc.lt1 | (cc.lt2 << 3), " L", 31);
|
display_str += Analog1DToString(cc.GetLeftTrigger().value, " L", 31);
|
||||||
display_str += Analog1DToString(cc.rt, " R", 31);
|
display_str += Analog1DToString(cc.GetRightTrigger().value, " 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);
|
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);
|
std::lock_guard<std::mutex> guard(s_input_display_lock);
|
||||||
|
@ -334,18 +334,20 @@ void WiiTASInputWindow::GetValues(DataReportBuilder& rpt, int ext,
|
|||||||
DataReportBuilder::CoreData core;
|
DataReportBuilder::CoreData core;
|
||||||
rpt.GetCoreData(&core);
|
rpt.GetCoreData(&core);
|
||||||
|
|
||||||
|
using EmuWiimote = WiimoteEmu::Wiimote;
|
||||||
|
|
||||||
u16& buttons = core.hex;
|
u16& buttons = core.hex;
|
||||||
GetButton<u16>(m_a_button, buttons, WiimoteEmu::Wiimote::BUTTON_A);
|
GetButton<u16>(m_a_button, buttons, EmuWiimote::BUTTON_A);
|
||||||
GetButton<u16>(m_b_button, buttons, WiimoteEmu::Wiimote::BUTTON_B);
|
GetButton<u16>(m_b_button, buttons, EmuWiimote::BUTTON_B);
|
||||||
GetButton<u16>(m_1_button, buttons, WiimoteEmu::Wiimote::BUTTON_ONE);
|
GetButton<u16>(m_1_button, buttons, EmuWiimote::BUTTON_ONE);
|
||||||
GetButton<u16>(m_2_button, buttons, WiimoteEmu::Wiimote::BUTTON_TWO);
|
GetButton<u16>(m_2_button, buttons, EmuWiimote::BUTTON_TWO);
|
||||||
GetButton<u16>(m_plus_button, buttons, WiimoteEmu::Wiimote::BUTTON_PLUS);
|
GetButton<u16>(m_plus_button, buttons, EmuWiimote::BUTTON_PLUS);
|
||||||
GetButton<u16>(m_minus_button, buttons, WiimoteEmu::Wiimote::BUTTON_MINUS);
|
GetButton<u16>(m_minus_button, buttons, EmuWiimote::BUTTON_MINUS);
|
||||||
GetButton<u16>(m_home_button, buttons, WiimoteEmu::Wiimote::BUTTON_HOME);
|
GetButton<u16>(m_home_button, buttons, EmuWiimote::BUTTON_HOME);
|
||||||
GetButton<u16>(m_left_button, buttons, WiimoteEmu::Wiimote::PAD_LEFT);
|
GetButton<u16>(m_left_button, buttons, EmuWiimote::PAD_LEFT);
|
||||||
GetButton<u16>(m_up_button, buttons, WiimoteEmu::Wiimote::PAD_UP);
|
GetButton<u16>(m_up_button, buttons, EmuWiimote::PAD_UP);
|
||||||
GetButton<u16>(m_down_button, buttons, WiimoteEmu::Wiimote::PAD_DOWN);
|
GetButton<u16>(m_down_button, buttons, EmuWiimote::PAD_DOWN);
|
||||||
GetButton<u16>(m_right_button, buttons, WiimoteEmu::Wiimote::PAD_RIGHT);
|
GetButton<u16>(m_right_button, buttons, EmuWiimote::PAD_RIGHT);
|
||||||
|
|
||||||
rpt.SetCoreData(core);
|
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.
|
// FYI: Interleaved reports may behave funky as not all data is always available.
|
||||||
|
|
||||||
DataReportBuilder::AccelData accel;
|
AccelData accel;
|
||||||
rpt.GetAccelData(&accel);
|
rpt.GetAccelData(&accel);
|
||||||
|
|
||||||
GetSpinBoxU16(m_remote_orientation_x_value, accel.x);
|
GetSpinBoxU16(m_remote_orientation_x_value, accel.value.x);
|
||||||
GetSpinBoxU16(m_remote_orientation_y_value, accel.y);
|
GetSpinBoxU16(m_remote_orientation_y_value, accel.value.y);
|
||||||
GetSpinBoxU16(m_remote_orientation_z_value, accel.z);
|
GetSpinBoxU16(m_remote_orientation_z_value, accel.value.z);
|
||||||
|
|
||||||
rpt.SetAccelData(accel);
|
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_x_value, nunchuk.jx);
|
||||||
GetSpinBoxU8(m_nunchuk_stick_y_value, nunchuk.jy);
|
GetSpinBoxU8(m_nunchuk_stick_y_value, nunchuk.jy);
|
||||||
|
|
||||||
u16 accel_x = nunchuk.ax << 2 & (nunchuk.bt.acc_x_lsb & 0b11);
|
auto accel = nunchuk.GetAccel().value;
|
||||||
u16 accel_y = nunchuk.ay << 2 & (nunchuk.bt.acc_y_lsb & 0b11);
|
GetSpinBoxU16(m_nunchuk_orientation_x_value, accel.x);
|
||||||
u16 accel_z = nunchuk.az << 2 & (nunchuk.bt.acc_z_lsb & 0b11);
|
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);
|
u8 bt = nunchuk.GetButtons();
|
||||||
GetSpinBoxU16(m_nunchuk_orientation_y_value, accel_y);
|
GetButton<u8>(m_c_button, bt, WiimoteEmu::Nunchuk::BUTTON_C);
|
||||||
GetSpinBoxU16(m_nunchuk_orientation_z_value, accel_z);
|
GetButton<u8>(m_z_button, bt, WiimoteEmu::Nunchuk::BUTTON_Z);
|
||||||
|
nunchuk.SetButtons(bt);
|
||||||
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;
|
|
||||||
|
|
||||||
key.Encrypt(reinterpret_cast<u8*>(&nunchuk), 0, sizeof(nunchuk));
|
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);
|
auto& cc = *reinterpret_cast<WiimoteEmu::Classic::DataFormat*>(ext_data);
|
||||||
key.Decrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
|
key.Decrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
|
||||||
|
|
||||||
cc.bt.hex ^= 0xFFFF;
|
u16 bt = cc.GetButtons();
|
||||||
GetButton<u16>(m_classic_a_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_A);
|
GetButton<u16>(m_classic_a_button, bt, WiimoteEmu::Classic::BUTTON_A);
|
||||||
GetButton<u16>(m_classic_b_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_B);
|
GetButton<u16>(m_classic_b_button, bt, WiimoteEmu::Classic::BUTTON_B);
|
||||||
GetButton<u16>(m_classic_x_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_X);
|
GetButton<u16>(m_classic_x_button, bt, WiimoteEmu::Classic::BUTTON_X);
|
||||||
GetButton<u16>(m_classic_y_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_Y);
|
GetButton<u16>(m_classic_y_button, bt, WiimoteEmu::Classic::BUTTON_Y);
|
||||||
GetButton<u16>(m_classic_plus_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_PLUS);
|
GetButton<u16>(m_classic_plus_button, bt, WiimoteEmu::Classic::BUTTON_PLUS);
|
||||||
GetButton<u16>(m_classic_minus_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_MINUS);
|
GetButton<u16>(m_classic_minus_button, bt, WiimoteEmu::Classic::BUTTON_MINUS);
|
||||||
GetButton<u16>(m_classic_l_button, cc.bt.hex, WiimoteEmu::Classic::TRIGGER_L);
|
GetButton<u16>(m_classic_l_button, bt, WiimoteEmu::Classic::TRIGGER_L);
|
||||||
GetButton<u16>(m_classic_r_button, cc.bt.hex, WiimoteEmu::Classic::TRIGGER_R);
|
GetButton<u16>(m_classic_r_button, bt, WiimoteEmu::Classic::TRIGGER_R);
|
||||||
GetButton<u16>(m_classic_zl_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_ZL);
|
GetButton<u16>(m_classic_zl_button, bt, WiimoteEmu::Classic::BUTTON_ZL);
|
||||||
GetButton<u16>(m_classic_zr_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_ZR);
|
GetButton<u16>(m_classic_zr_button, bt, WiimoteEmu::Classic::BUTTON_ZR);
|
||||||
GetButton<u16>(m_classic_home_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_HOME);
|
GetButton<u16>(m_classic_home_button, bt, WiimoteEmu::Classic::BUTTON_HOME);
|
||||||
GetButton<u16>(m_classic_left_button, cc.bt.hex, WiimoteEmu::Classic::PAD_LEFT);
|
GetButton<u16>(m_classic_left_button, bt, WiimoteEmu::Classic::PAD_LEFT);
|
||||||
GetButton<u16>(m_classic_up_button, cc.bt.hex, WiimoteEmu::Classic::PAD_UP);
|
GetButton<u16>(m_classic_up_button, bt, WiimoteEmu::Classic::PAD_UP);
|
||||||
GetButton<u16>(m_classic_down_button, cc.bt.hex, WiimoteEmu::Classic::PAD_DOWN);
|
GetButton<u16>(m_classic_down_button, bt, WiimoteEmu::Classic::PAD_DOWN);
|
||||||
GetButton<u16>(m_classic_right_button, cc.bt.hex, WiimoteEmu::Classic::PAD_RIGHT);
|
GetButton<u16>(m_classic_right_button, bt, WiimoteEmu::Classic::PAD_RIGHT);
|
||||||
cc.bt.hex ^= 0xFFFF;
|
cc.SetButtons(bt);
|
||||||
|
|
||||||
u8 rx = (cc.rx1 & 0b1) & ((cc.rx2 & 0b11) << 1) & ((cc.rx3 & 0b11) << 3);
|
auto right_stick = cc.GetRightStick().value;
|
||||||
GetSpinBoxU8(m_classic_right_stick_x_value, rx);
|
GetSpinBoxU8(m_classic_right_stick_x_value, right_stick.x);
|
||||||
cc.rx1 = rx & 0b1;
|
GetSpinBoxU8(m_classic_right_stick_y_value, right_stick.y);
|
||||||
cc.rx2 = (rx >> 1) & 0b11;
|
cc.SetRightStick(right_stick);
|
||||||
cc.rx3 = (rx >> 3) & 0b11;
|
|
||||||
|
|
||||||
u8 ry = cc.ry;
|
auto left_stick = cc.GetLeftStick().value;
|
||||||
GetSpinBoxU8(m_classic_right_stick_y_value, ry);
|
GetSpinBoxU8(m_classic_left_stick_x_value, left_stick.x);
|
||||||
cc.ry = ry;
|
GetSpinBoxU8(m_classic_left_stick_y_value, left_stick.y);
|
||||||
|
cc.SetLeftStick(left_stick);
|
||||||
|
|
||||||
u8 lx = cc.lx;
|
u8 rt = cc.GetRightTrigger().value;
|
||||||
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;
|
|
||||||
GetSpinBoxU8(m_right_trigger_value, rt);
|
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);
|
GetSpinBoxU8(m_left_trigger_value, lt);
|
||||||
cc.lt1 = lt & 0b111;
|
cc.SetLeftTrigger(lt);
|
||||||
cc.lt2 = (lt >> 3) & 0b11;
|
|
||||||
|
|
||||||
key.Encrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
|
key.Encrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user