From 5299e902aa7fe9f474b5e3dd37a5a04f9f3e6bbb Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 4 Jul 2020 22:19:26 -0500 Subject: [PATCH] ControllerInterface/Wiimote: Provide fallback values for extensions with bad calibration data. --- .../InputCommon/ControllerEmu/ControllerEmu.h | 37 ++++++++++ .../ControllerInterface/Wiimote/Wiimote.cpp | 73 +++++++++++++++++-- .../ControllerInterface/Wiimote/Wiimote.h | 14 +++- 3 files changed, 114 insertions(+), 10 deletions(-) diff --git a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h index 3301672ccf..b6808f1c0b 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h +++ b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.h @@ -14,6 +14,7 @@ #include "Common/BitUtils.h" #include "Common/Common.h" #include "Common/IniFile.h" +#include "Common/MathUtil.h" #include "InputCommon/ControlReference/ExpressionParser.h" #include "InputCommon/ControllerInterface/Device.h" @@ -37,6 +38,20 @@ struct TwoPointCalibration TwoPointCalibration() = default; TwoPointCalibration(const T& zero_, const T& max_) : zero{zero_}, max{max_} {} + // Sanity check is that max and zero are not equal. + constexpr bool IsSane() const + { + if constexpr (std::is_arithmetic_v) + { + return max != zero; + } + else + { + return std::equal(std::begin(max.data), std::end(max.data), std::begin(zero.data), + std::not_equal_to<>()); + } + } + static constexpr size_t BITS_OF_PRECISION = Bits; T zero; @@ -53,6 +68,28 @@ struct ThreePointCalibration { } + // Sanity check is that min and max are on opposite sides of the zero value. + constexpr bool IsSane() const + { + if constexpr (std::is_arithmetic_v) + { + return MathUtil::Sign(zero - min) * MathUtil::Sign(zero - max) == -1; + } + else + { + for (size_t i = 0; i != std::size(zero.data); ++i) + { + if (MathUtil::Sign(zero.data[i] - min.data[i]) * + MathUtil::Sign(zero.data[i] - max.data[i]) != + -1) + { + return false; + } + } + return true; + } + } + static constexpr size_t BITS_OF_PRECISION = Bits; T min; diff --git a/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.cpp b/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.cpp index 3ff61621d8..28426fc114 100644 --- a/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.cpp @@ -610,22 +610,27 @@ void Device::RunTasks() WiimoteEmu::UpdateCalibrationDataChecksum(calibration_data, 2); + Checksum checksum = Checksum::Good; + if (read_checksum != std::pair(calibration_data[CALIBRATION_SIZE - 2], calibration_data[CALIBRATION_SIZE - 1])) { // We could potentially try another block or call the extension unusable. WARN_LOG(WIIMOTE, "WiiRemote: Bad extension calibration checksum."); + checksum = Checksum::Bad; } if (m_extension_id == ExtensionID::Nunchuk) { m_nunchuk_state.SetCalibrationData( - Common::BitCastPtr(calibration_data.data())); + Common::BitCastPtr(calibration_data.data()), + checksum); } else if (m_extension_id == ExtensionID::Classic) { m_classic_state.SetCalibrationData( - Common::BitCastPtr(calibration_data.data())); + Common::BitCastPtr(calibration_data.data()), + checksum); } }); @@ -730,24 +735,76 @@ void Device::MotionPlusState::SetCalibrationData( calibration->slow = data.slow; } -void Device::NunchukState::SetCalibrationData(const WiimoteEmu::Nunchuk::CalibrationData& data) +Device::NunchukState::Calibration::Calibration() : accel{}, stick{} +{ + accel.zero.data.fill(1 << (accel.BITS_OF_PRECISION - 1)); + // Approximate 1G value per WiiBrew: + accel.max.data.fill(740); + + stick.zero.data.fill(1 << (stick.BITS_OF_PRECISION - 1)); + stick.max.data.fill((1 << stick.BITS_OF_PRECISION) - 1); +} + +void Device::NunchukState::SetCalibrationData(const WiimoteEmu::Nunchuk::CalibrationData& data, + Checksum checksum) { DEBUG_LOG(WIIMOTE, "WiiRemote: Set Nunchuk calibration."); calibration.emplace(); - calibration->stick = data.GetStick(); - calibration->accel = data.GetAccel(); + if (checksum == Checksum::Bad) + return; + + // Genuine Nunchuks have been observed with "min" and "max" values of zero. + // We catch that here and fall back to "full range" calibration. + const auto stick_calibration = data.GetStick(); + if (stick_calibration.IsSane()) + calibration->stick = stick_calibration; + else + WARN_LOG(WIIMOTE, "WiiRemote: Nunchuk stick calibration is not sane. Using fallback values."); + + // No known reports of bad accelerometer calibration but we'll handle it just in case. + const auto accel_calibration = data.GetAccel(); + if (accel_calibration.IsSane()) + calibration->accel = accel_calibration; + else + WARN_LOG(WIIMOTE, "WiiRemote: Nunchuk accel calibration is not sane. Using fallback values."); } -void Device::ClassicState::SetCalibrationData(const WiimoteEmu::Classic::CalibrationData& data) +Device::ClassicState::Calibration::Calibration() + : left_stick{}, right_stick{}, left_trigger{}, right_trigger{} +{ + left_stick.zero.data.fill(1 << (left_stick.BITS_OF_PRECISION - 1)); + left_stick.max.data.fill((1 << left_stick.BITS_OF_PRECISION) - 1); + + right_stick.zero.data.fill(1 << (right_stick.BITS_OF_PRECISION - 1)); + right_stick.max.data.fill((1 << right_stick.BITS_OF_PRECISION) - 1); + + left_trigger.max = (1 << left_trigger.BITS_OF_PRECISION) - 1; + right_trigger.max = (1 << right_trigger.BITS_OF_PRECISION) - 1; +} + +void Device::ClassicState::SetCalibrationData(const WiimoteEmu::Classic::CalibrationData& data, + Checksum checksum) { DEBUG_LOG(WIIMOTE, "WiiRemote: Set Classic Controller calibration."); calibration.emplace(); - calibration->left_stick = data.GetLeftStick(); - calibration->right_stick = data.GetRightStick(); + if (checksum == Checksum::Bad) + return; + + const auto left_stick_calibration = data.GetLeftStick(); + if (left_stick_calibration.IsSane()) + calibration->left_stick = left_stick_calibration; + else + WARN_LOG(WIIMOTE, "WiiRemote: CC left stick calibration is not sane. Using fallback values."); + + const auto right_stick_calibration = data.GetRightStick(); + if (right_stick_calibration.IsSane()) + calibration->right_stick = right_stick_calibration; + else + WARN_LOG(WIIMOTE, "WiiRemote: CC right stick calibration is not sane. Using fallback values."); calibration->left_trigger = data.GetLeftTrigger(); calibration->right_trigger = data.GetRightTrigger(); diff --git a/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.h b/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.h index 87165faff4..7615a92940 100644 --- a/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.h +++ b/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.h @@ -46,6 +46,12 @@ private: Unsupported, }; + enum class Checksum + { + Good, + Bad, + }; + class MotionPlusState { public: @@ -70,7 +76,7 @@ private: { using CalibrationData = WiimoteEmu::Nunchuk::CalibrationData; - void SetCalibrationData(const CalibrationData&); + void SetCalibrationData(const CalibrationData&, Checksum); void ProcessData(const WiimoteEmu::Nunchuk::DataFormat&); Common::Vec2 stick = {}; @@ -80,6 +86,8 @@ private: struct Calibration { + Calibration(); + CalibrationData::AccelCalibration accel; CalibrationData::StickCalibration stick; }; @@ -91,7 +99,7 @@ private: { using CalibrationData = WiimoteEmu::Classic::CalibrationData; - void SetCalibrationData(const CalibrationData&); + void SetCalibrationData(const CalibrationData&, Checksum); void ProcessData(const WiimoteEmu::Classic::DataFormat&); std::array sticks = {}; @@ -101,6 +109,8 @@ private: struct Calibration { + Calibration(); + CalibrationData::StickCalibration left_stick; CalibrationData::StickCalibration right_stick;