mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge pull request #11070 from AdmiralCurtiss/netplay-wiimotes
Netplay: Redesign Wiimote data exchange.
This commit is contained in:
@@ -279,6 +279,8 @@ add_library(core
|
||||
HW/WiimoteCommon/WiimoteReport.h
|
||||
HW/WiimoteEmu/Camera.cpp
|
||||
HW/WiimoteEmu/Camera.h
|
||||
HW/WiimoteEmu/DesiredWiimoteState.cpp
|
||||
HW/WiimoteEmu/DesiredWiimoteState.h
|
||||
HW/WiimoteEmu/Dynamics.cpp
|
||||
HW/WiimoteEmu/Dynamics.h
|
||||
HW/WiimoteEmu/EmuSubroutines.cpp
|
||||
@@ -286,6 +288,7 @@ add_library(core
|
||||
HW/WiimoteEmu/Encryption.h
|
||||
HW/WiimoteEmu/Extension/Classic.cpp
|
||||
HW/WiimoteEmu/Extension/Classic.h
|
||||
HW/WiimoteEmu/Extension/DesiredExtensionState.h
|
||||
HW/WiimoteEmu/Extension/DrawsomeTablet.cpp
|
||||
HW/WiimoteEmu/Extension/DrawsomeTablet.h
|
||||
HW/WiimoteEmu/Extension/Drums.cpp
|
||||
|
||||
@@ -498,9 +498,6 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi
|
||||
if (core_parameter.bWii && !Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_ENABLED))
|
||||
{
|
||||
Wiimote::LoadConfig();
|
||||
|
||||
if (NetPlay::IsNetPlayRunning())
|
||||
NetPlay::SetupWiimotes();
|
||||
}
|
||||
|
||||
FreeLook::LoadInputConfig();
|
||||
|
||||
@@ -94,8 +94,6 @@ ControllerEmu::ControlGroup* GetDrawsomeTabletGroup(int number,
|
||||
WiimoteEmu::DrawsomeTabletGroup group);
|
||||
ControllerEmu::ControlGroup* GetTaTaConGroup(int number, WiimoteEmu::TaTaConGroup group);
|
||||
ControllerEmu::ControlGroup* GetShinkansenGroup(int number, WiimoteEmu::ShinkansenGroup group);
|
||||
|
||||
bool NetPlay_GetButtonPress(int wiimote, bool pressed);
|
||||
} // namespace Wiimote
|
||||
|
||||
namespace WiimoteReal
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
struct DesiredWiimoteState;
|
||||
}
|
||||
|
||||
namespace WiimoteCommon
|
||||
{
|
||||
// Source: HID_010_SPC_PFL/1.0 (official HID specification)
|
||||
@@ -30,8 +35,12 @@ public:
|
||||
virtual void EventLinked() = 0;
|
||||
virtual void EventUnlinked() = 0;
|
||||
|
||||
virtual u8 GetWiimoteDeviceIndex() const = 0;
|
||||
virtual void SetWiimoteDeviceIndex(u8 index) = 0;
|
||||
|
||||
// Called every ~200hz after HID channels are established.
|
||||
virtual void Update() = 0;
|
||||
virtual void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state) = 0;
|
||||
virtual void Update(const WiimoteEmu::DesiredWiimoteState& target_state) = 0;
|
||||
|
||||
void SetInterruptCallback(InterruptCallbackType callback) { m_callback = std::move(callback); }
|
||||
|
||||
@@ -39,8 +48,10 @@ public:
|
||||
// Does not include HID-type header.
|
||||
virtual void InterruptDataOutput(const u8* data, u32 size) = 0;
|
||||
|
||||
// Used to connect a disconnected wii remote on button press.
|
||||
virtual bool IsButtonPressed() = 0;
|
||||
// Get a snapshot of the current state of the Wiimote's buttons.
|
||||
// Note that only the button bits of the return value are meaningful, the rest should be ignored.
|
||||
// This is used to query a disconnected Wiimote whether it wants to reconnect.
|
||||
virtual ButtonData GetCurrentlyPressedButtons() = 0;
|
||||
|
||||
protected:
|
||||
void InterruptDataInputCallback(const u8* data, u32 size)
|
||||
|
||||
@@ -52,36 +52,14 @@ int CameraLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
||||
return RawWrite(&m_reg_data, addr, count, data_in);
|
||||
}
|
||||
|
||||
void CameraLogic::Update(const Common::Matrix44& transform, Common::Vec2 field_of_view)
|
||||
std::array<CameraPoint, CameraLogic::NUM_POINTS>
|
||||
CameraLogic::GetCameraPoints(const Common::Matrix44& transform, Common::Vec2 field_of_view)
|
||||
{
|
||||
// IR data is read from offset 0x37 on real hardware.
|
||||
auto& data = m_reg_data.camera_data;
|
||||
data.fill(0xff);
|
||||
|
||||
constexpr u8 OBJECT_TRACKING_ENABLE = 0x08;
|
||||
|
||||
// If Address 0x30 is not 0x08 the camera will return 0xFFs.
|
||||
// The Wii seems to write 0x01 here before changing modes/sensitivities.
|
||||
if (m_reg_data.enable_object_tracking != OBJECT_TRACKING_ENABLE)
|
||||
return;
|
||||
|
||||
// If the sensor bar is off the camera will see no LEDs and return 0xFFs.
|
||||
if (!IOS::g_gpio_out[IOS::GPIO::SENSOR_BAR])
|
||||
return;
|
||||
|
||||
using Common::Matrix33;
|
||||
using Common::Matrix44;
|
||||
using Common::Vec3;
|
||||
using Common::Vec4;
|
||||
|
||||
// FYI: A real wiimote normally only returns 1 point for each LED cluster (2 total).
|
||||
// Sending all 4 points can actually cause some stuttering issues.
|
||||
constexpr int NUM_POINTS = 2;
|
||||
|
||||
// Range from 0-15. Small values (2-4) seem to be very typical.
|
||||
// This is reduced based on distance from sensor bar.
|
||||
constexpr int MAX_POINT_SIZE = 15;
|
||||
|
||||
const std::array<Vec3, NUM_POINTS> leds{
|
||||
Vec3{-SENSOR_BAR_LED_SEPARATION / 2, 0, 0},
|
||||
Vec3{SENSOR_BAR_LED_SEPARATION / 2, 0, 0},
|
||||
@@ -91,12 +69,6 @@ void CameraLogic::Update(const Common::Matrix44& transform, Common::Vec2 field_o
|
||||
Matrix44::Perspective(field_of_view.y, field_of_view.x / field_of_view.y, 0.001f, 1000) *
|
||||
Matrix44::FromMatrix33(Matrix33::RotateX(float(MathUtil::TAU / 4))) * transform;
|
||||
|
||||
struct CameraPoint
|
||||
{
|
||||
IRBasic::IRObject position;
|
||||
u8 size = 0;
|
||||
};
|
||||
|
||||
std::array<CameraPoint, leds.size()> camera_points;
|
||||
|
||||
std::transform(leds.begin(), leds.end(), camera_points.begin(), [&](const Vec3& v) {
|
||||
@@ -112,13 +84,32 @@ void CameraLogic::Update(const Common::Matrix44& transform, Common::Vec2 field_o
|
||||
const auto point_size = std::lround(MAX_POINT_SIZE / point.w / 2);
|
||||
|
||||
if (x >= 0 && y >= 0 && x < CAMERA_RES_X && y < CAMERA_RES_Y)
|
||||
return CameraPoint{{u16(x), u16(y)}, u8(point_size)};
|
||||
return CameraPoint({u16(x), u16(y)}, u8(point_size));
|
||||
}
|
||||
|
||||
// 0xFFFFs are interpreted as "not visible".
|
||||
return CameraPoint{{0xffff, 0xffff}, 0xff};
|
||||
return CameraPoint();
|
||||
});
|
||||
|
||||
return camera_points;
|
||||
}
|
||||
|
||||
void CameraLogic::Update(const std::array<CameraPoint, NUM_POINTS>& camera_points)
|
||||
{
|
||||
// IR data is read from offset 0x37 on real hardware.
|
||||
auto& data = m_reg_data.camera_data;
|
||||
data.fill(0xff);
|
||||
|
||||
constexpr u8 OBJECT_TRACKING_ENABLE = 0x08;
|
||||
|
||||
// If Address 0x30 is not 0x08 the camera will return 0xFFs.
|
||||
// The Wii seems to write 0x01 here before changing modes/sensitivities.
|
||||
if (m_reg_data.enable_object_tracking != OBJECT_TRACKING_ENABLE)
|
||||
return;
|
||||
|
||||
// If the sensor bar is off the camera will see no LEDs and return 0xFFs.
|
||||
if (!IOS::g_gpio_out[IOS::GPIO::SENSOR_BAR])
|
||||
return;
|
||||
|
||||
switch (m_reg_data.mode)
|
||||
{
|
||||
case IR_MODE_BASIC:
|
||||
|
||||
@@ -16,11 +16,26 @@ class Matrix44;
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
using IRObject = Common::TVec2<u16>;
|
||||
|
||||
struct CameraPoint
|
||||
{
|
||||
IRObject position;
|
||||
u8 size;
|
||||
|
||||
// 0xFFFFs are interpreted as "not visible".
|
||||
constexpr CameraPoint() : position({0xffff, 0xffff}), size(0xff) {}
|
||||
constexpr CameraPoint(IRObject position_, u8 size_) : position(position_), size(size_) {}
|
||||
constexpr bool operator==(const CameraPoint& other) const
|
||||
{
|
||||
return this->position == other.position && this->size == other.size;
|
||||
}
|
||||
constexpr bool operator!=(const CameraPoint& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
// Four bytes for two objects. Filled with 0xFF if empty
|
||||
struct IRBasic
|
||||
{
|
||||
using IRObject = Common::TVec2<u16>;
|
||||
|
||||
u8 x1;
|
||||
u8 y1;
|
||||
u8 x2hi : 2;
|
||||
@@ -59,8 +74,8 @@ struct IRExtended
|
||||
u8 xhi : 2;
|
||||
u8 yhi : 2;
|
||||
|
||||
auto GetPosition() const { return IRBasic::IRObject(xhi << 8 | x, yhi << 8 | y); }
|
||||
void SetPosition(const IRBasic::IRObject& obj)
|
||||
auto GetPosition() const { return IRObject(xhi << 8 | x, yhi << 8 | y); }
|
||||
void SetPosition(const IRObject& obj)
|
||||
{
|
||||
x = obj.x;
|
||||
xhi = obj.x >> 8;
|
||||
@@ -109,9 +124,19 @@ public:
|
||||
IR_MODE_FULL = 5,
|
||||
};
|
||||
|
||||
// FYI: A real wiimote normally only returns 1 point for each LED cluster (2 total).
|
||||
// Sending all 4 points can actually cause some stuttering issues.
|
||||
static constexpr int NUM_POINTS = 2;
|
||||
|
||||
// Range from 0-15. Small values (2-4) seem to be very typical.
|
||||
// This is reduced based on distance from sensor bar.
|
||||
static constexpr int MAX_POINT_SIZE = 15;
|
||||
|
||||
void Reset();
|
||||
void DoState(PointerWrap& p);
|
||||
void Update(const Common::Matrix44& transform, Common::Vec2 field_of_view);
|
||||
static std::array<CameraPoint, NUM_POINTS> GetCameraPoints(const Common::Matrix44& transform,
|
||||
Common::Vec2 field_of_view);
|
||||
void Update(const std::array<CameraPoint, NUM_POINTS>& camera_points);
|
||||
void SetEnabled(bool is_enabled);
|
||||
|
||||
static constexpr u8 I2C_ADDR = 0x58;
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/DrawsomeTablet.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Drums.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Guitar.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Shinkansen.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/TaTaCon.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Turntable.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/UDrawTablet.h"
|
||||
#include "Core/HW/WiimoteEmu/MotionPlus.h"
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
SerializedWiimoteState SerializeDesiredState(const DesiredWiimoteState& state)
|
||||
{
|
||||
const u8 has_buttons = (state.buttons.hex & WiimoteCommon::ButtonData::BUTTON_MASK) != 0 ? 1 : 0;
|
||||
const u8 has_accel = state.acceleration != DesiredWiimoteState::DEFAULT_ACCELERATION ? 1 : 0;
|
||||
const u8 has_camera = state.camera_points != DesiredWiimoteState::DEFAULT_CAMERA ? 1 : 0;
|
||||
const u8 has_motion_plus = state.motion_plus.has_value() ? 1 : 0;
|
||||
|
||||
// Right now we support < 16 extensions so the info which extension is in use fits into 4 bits.
|
||||
// This allows 'empty' packets to be a single byte, which is very nice for reducing bandwidth.
|
||||
// If we ever support 16 or more we have to redesign this a bit; ideally use a variable-length
|
||||
// encoding so that typical extensions (None, Nunchuk, Classic Controller) still fit into the
|
||||
// initial 4 bits.
|
||||
static_assert(std::variant_size_v<DesiredExtensionState::ExtensionData> <= (1 << 4));
|
||||
const u8 extension = u8(state.extension.data.index());
|
||||
|
||||
SerializedWiimoteState s;
|
||||
s.length = 0;
|
||||
s.data[s.length++] = u8(has_buttons | (has_accel << 1) | (has_camera << 2) |
|
||||
(has_motion_plus << 3) | (extension << 4));
|
||||
|
||||
if (has_buttons)
|
||||
{
|
||||
const u8 buttons = u8((state.buttons.a) | (state.buttons.b << 1) | (state.buttons.plus << 2) |
|
||||
(state.buttons.minus << 3) | (state.buttons.one << 4) |
|
||||
(state.buttons.two << 5) | (state.buttons.home << 6));
|
||||
const u8 dpad = u8((state.buttons.up) | (state.buttons.down << 1) | (state.buttons.left << 2) |
|
||||
(state.buttons.right << 3));
|
||||
s.data[s.length++] = buttons;
|
||||
s.data[s.length++] = dpad;
|
||||
}
|
||||
|
||||
if (has_accel)
|
||||
{
|
||||
const u16 accel_x = state.acceleration.value.x; // 10 bits
|
||||
const u16 accel_y = state.acceleration.value.y; // 9 bits (ignore lowest bit)
|
||||
const u16 accel_z = state.acceleration.value.z; // 9 bits (ignore lowest bit)
|
||||
const u8 accel_x_high = u8(accel_x >> 2);
|
||||
const u8 accel_y_high = u8(accel_y >> 2);
|
||||
const u8 accel_z_high = u8(accel_z >> 2);
|
||||
const u8 accel_low = u8((accel_x & 0b11) | (Common::ExtractBit<1>(accel_y) << 2) |
|
||||
(Common::ExtractBit<1>(accel_z) << 3));
|
||||
|
||||
if (has_buttons)
|
||||
{
|
||||
// can use the high bits of the dpad field from buttons
|
||||
s.data[s.length - 1] |= u8(accel_low << 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
s.data[s.length++] = u8(accel_low << 4);
|
||||
}
|
||||
|
||||
s.data[s.length++] = accel_x_high;
|
||||
s.data[s.length++] = accel_y_high;
|
||||
s.data[s.length++] = accel_z_high;
|
||||
}
|
||||
|
||||
if (has_camera)
|
||||
{
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
const u16 camera_x = state.camera_points[i].position.x; // 10 bits
|
||||
const u16 camera_y = state.camera_points[i].position.y; // 10 bits
|
||||
const u8 camera_size = state.camera_points[i].size; // 4 bits
|
||||
s.data[s.length++] = u8((camera_x & 0b11) | ((camera_y & 0b11) << 2) | (camera_size << 4));
|
||||
s.data[s.length++] = u8(camera_x >> 2);
|
||||
s.data[s.length++] = u8(camera_y >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_motion_plus)
|
||||
{
|
||||
const u16 pitch_slow = state.motion_plus->is_slow.x ? 1 : 0;
|
||||
const u16 roll_slow = state.motion_plus->is_slow.y ? 1 : 0;
|
||||
const u16 yaw_slow = state.motion_plus->is_slow.z ? 1 : 0;
|
||||
const u16 pitch_value = state.motion_plus->gyro.value.x; // 14 bits
|
||||
const u16 roll_value = state.motion_plus->gyro.value.y; // 14 bits
|
||||
const u16 yaw_value = state.motion_plus->gyro.value.z; // 14 bits
|
||||
s.data[s.length++] = u8(pitch_value);
|
||||
s.data[s.length++] = u8(((pitch_value >> 8) & 0x3f) | (pitch_slow << 7));
|
||||
s.data[s.length++] = u8(roll_value);
|
||||
s.data[s.length++] = u8(((roll_value >> 8) & 0x3f) | (roll_slow << 7));
|
||||
s.data[s.length++] = u8(yaw_value);
|
||||
s.data[s.length++] = u8(((yaw_value >> 8) & 0x3f) | (yaw_slow << 7));
|
||||
}
|
||||
|
||||
if (extension)
|
||||
{
|
||||
std::visit(
|
||||
[&s](const auto& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (!std::is_same_v<std::monostate, T>)
|
||||
{
|
||||
static_assert(sizeof(arg) <= 6);
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
std::memcpy(&s.data[s.length], &arg, sizeof(arg));
|
||||
s.length += sizeof(arg);
|
||||
}
|
||||
},
|
||||
state.extension.data);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool DeserializeExtensionState(DesiredWiimoteState* state,
|
||||
const SerializedWiimoteState& serialized, size_t offset)
|
||||
{
|
||||
if (serialized.length < offset + sizeof(T))
|
||||
return false;
|
||||
auto& e = state->extension.data.emplace<T>();
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
std::memcpy(&e, &serialized.data[offset], sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeserializeDesiredState(DesiredWiimoteState* state, const SerializedWiimoteState& serialized)
|
||||
{
|
||||
// clear state
|
||||
state->buttons.hex = 0;
|
||||
state->acceleration = DesiredWiimoteState::DEFAULT_ACCELERATION;
|
||||
state->camera_points = DesiredWiimoteState::DEFAULT_CAMERA;
|
||||
state->motion_plus = std::nullopt;
|
||||
state->extension.data = std::monostate();
|
||||
|
||||
if (serialized.length < 1)
|
||||
{
|
||||
// can't be valid
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& d = serialized.data;
|
||||
const u8 has_buttons = d[0] & 1;
|
||||
const u8 has_accel = (d[0] >> 1) & 1;
|
||||
const u8 has_camera = (d[0] >> 2) & 1;
|
||||
const u8 has_motion_plus = (d[0] >> 3) & 1;
|
||||
const u8 extension = (d[0] >> 4);
|
||||
|
||||
if (extension >= ExtensionNumber::MAX)
|
||||
{
|
||||
// invalid extension
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t expected_size = [&]() {
|
||||
size_t s = 1;
|
||||
if (has_buttons && has_accel)
|
||||
s += 5;
|
||||
else if (has_buttons)
|
||||
s += 2;
|
||||
else if (has_accel)
|
||||
s += 4;
|
||||
if (has_camera)
|
||||
s += 6;
|
||||
if (has_motion_plus)
|
||||
s += 6;
|
||||
switch (extension)
|
||||
{
|
||||
case ExtensionNumber::NONE:
|
||||
break;
|
||||
case ExtensionNumber::NUNCHUK:
|
||||
s += sizeof(Nunchuk::DataFormat);
|
||||
break;
|
||||
case ExtensionNumber::CLASSIC:
|
||||
s += sizeof(Classic::DataFormat);
|
||||
break;
|
||||
case ExtensionNumber::GUITAR:
|
||||
s += sizeof(Guitar::DataFormat);
|
||||
break;
|
||||
case ExtensionNumber::DRUMS:
|
||||
s += sizeof(Drums::DesiredState);
|
||||
break;
|
||||
case ExtensionNumber::TURNTABLE:
|
||||
s += sizeof(Turntable::DataFormat);
|
||||
break;
|
||||
case ExtensionNumber::UDRAW_TABLET:
|
||||
s += sizeof(UDrawTablet::DataFormat);
|
||||
break;
|
||||
case ExtensionNumber::DRAWSOME_TABLET:
|
||||
s += sizeof(DrawsomeTablet::DataFormat);
|
||||
break;
|
||||
case ExtensionNumber::TATACON:
|
||||
s += sizeof(TaTaCon::DataFormat);
|
||||
break;
|
||||
case ExtensionNumber::SHINKANSEN:
|
||||
s += sizeof(Shinkansen::DesiredState);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}();
|
||||
|
||||
if (serialized.length != expected_size)
|
||||
{
|
||||
// invalid length
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t pos = 1;
|
||||
|
||||
if (has_buttons)
|
||||
{
|
||||
state->buttons.a = d[pos] & 1;
|
||||
state->buttons.b = (d[pos] >> 1) & 1;
|
||||
state->buttons.plus = (d[pos] >> 2) & 1;
|
||||
state->buttons.minus = (d[pos] >> 3) & 1;
|
||||
state->buttons.one = (d[pos] >> 4) & 1;
|
||||
state->buttons.two = (d[pos] >> 5) & 1;
|
||||
state->buttons.home = (d[pos] >> 6) & 1;
|
||||
state->buttons.up = d[pos + 1] & 1;
|
||||
state->buttons.down = (d[pos + 1] >> 1) & 1;
|
||||
state->buttons.left = (d[pos + 1] >> 2) & 1;
|
||||
state->buttons.right = (d[pos + 1] >> 3) & 1;
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
if (has_accel)
|
||||
{
|
||||
if (has_buttons)
|
||||
pos -= 1;
|
||||
const u8 accel_low = d[pos] >> 4;
|
||||
const u8 accel_x_high = d[pos + 1];
|
||||
const u8 accel_y_high = d[pos + 2];
|
||||
const u8 accel_z_high = d[pos + 3];
|
||||
state->acceleration.value.x = (accel_x_high << 2) | (accel_low & 0b11);
|
||||
state->acceleration.value.y =
|
||||
Common::ExpandValue<u16>((accel_y_high << 1) | Common::ExtractBit<2>(accel_low), 1);
|
||||
state->acceleration.value.z =
|
||||
Common::ExpandValue<u16>((accel_z_high << 1) | Common::ExtractBit<3>(accel_low), 1);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
if (has_camera)
|
||||
{
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
const u8 camera_misc = d[pos];
|
||||
const u8 camera_x_high = d[pos + 1];
|
||||
const u8 camera_y_high = d[pos + 2];
|
||||
const u16 camera_x = (camera_x_high << 2) | (camera_misc & 0b11);
|
||||
const u16 camera_y = (camera_y_high << 2) | ((camera_misc >> 2) & 0b11);
|
||||
const u8 camera_size = camera_misc >> 4;
|
||||
if (camera_y < CameraLogic::CAMERA_RES_Y)
|
||||
{
|
||||
state->camera_points[i] = CameraPoint({camera_x, camera_y}, camera_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// indicates an invalid camera point
|
||||
state->camera_points[i] = CameraPoint();
|
||||
}
|
||||
pos += 3;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_motion_plus)
|
||||
{
|
||||
const u16 pitch_value = d[pos] | ((d[pos + 1] & 0x3f) << 8);
|
||||
const u16 roll_value = d[pos + 2] | ((d[pos + 3] & 0x3f) << 8);
|
||||
const u16 yaw_value = d[pos + 4] | ((d[pos + 5] & 0x3f) << 8);
|
||||
const bool pitch_slow = (d[pos + 1] & 0x80) != 0;
|
||||
const bool roll_slow = (d[pos + 3] & 0x80) != 0;
|
||||
const bool yaw_slow = (d[pos + 5] & 0x80) != 0;
|
||||
state->motion_plus = MotionPlus::DataFormat::Data{
|
||||
MotionPlus::DataFormat::GyroRawValue{
|
||||
MotionPlus::DataFormat::GyroType(pitch_value, roll_value, yaw_value)},
|
||||
MotionPlus::DataFormat::SlowType(pitch_slow, roll_slow, yaw_slow)};
|
||||
pos += 6;
|
||||
}
|
||||
|
||||
switch (extension)
|
||||
{
|
||||
case ExtensionNumber::NONE:
|
||||
return true;
|
||||
case ExtensionNumber::NUNCHUK:
|
||||
return DeserializeExtensionState<Nunchuk::DataFormat>(state, serialized, pos);
|
||||
case ExtensionNumber::CLASSIC:
|
||||
return DeserializeExtensionState<Classic::DataFormat>(state, serialized, pos);
|
||||
case ExtensionNumber::GUITAR:
|
||||
return DeserializeExtensionState<Guitar::DataFormat>(state, serialized, pos);
|
||||
case ExtensionNumber::DRUMS:
|
||||
return DeserializeExtensionState<Drums::DesiredState>(state, serialized, pos);
|
||||
case ExtensionNumber::TURNTABLE:
|
||||
return DeserializeExtensionState<Turntable::DataFormat>(state, serialized, pos);
|
||||
case ExtensionNumber::UDRAW_TABLET:
|
||||
return DeserializeExtensionState<UDrawTablet::DataFormat>(state, serialized, pos);
|
||||
case ExtensionNumber::DRAWSOME_TABLET:
|
||||
return DeserializeExtensionState<DrawsomeTablet::DataFormat>(state, serialized, pos);
|
||||
case ExtensionNumber::TATACON:
|
||||
return DeserializeExtensionState<TaTaCon::DataFormat>(state, serialized, pos);
|
||||
case ExtensionNumber::SHINKANSEN:
|
||||
return DeserializeExtensionState<Shinkansen::DesiredState>(state, serialized, pos);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace WiimoteEmu
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||
#include "Core/HW/WiimoteEmu/Camera.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/DesiredExtensionState.h"
|
||||
#include "Core/HW/WiimoteEmu/MotionPlus.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
struct DesiredWiimoteState
|
||||
{
|
||||
// 1g in Z direction, which is the default returned by an unmoving emulated Wiimote.
|
||||
static constexpr WiimoteCommon::AccelData DEFAULT_ACCELERATION = WiimoteCommon::AccelData(
|
||||
{Wiimote::ACCEL_ZERO_G << 2, Wiimote::ACCEL_ZERO_G << 2, Wiimote::ACCEL_ONE_G << 2});
|
||||
|
||||
// No light detected by the IR camera.
|
||||
static constexpr std::array<CameraPoint, 2> DEFAULT_CAMERA = {CameraPoint(), CameraPoint()};
|
||||
|
||||
WiimoteCommon::ButtonData buttons{}; // non-button state in this is ignored
|
||||
WiimoteCommon::AccelData acceleration = DEFAULT_ACCELERATION;
|
||||
std::array<CameraPoint, 2> camera_points = DEFAULT_CAMERA;
|
||||
std::optional<MotionPlus::DataFormat::Data> motion_plus = std::nullopt;
|
||||
DesiredExtensionState extension;
|
||||
};
|
||||
|
||||
// For Netplay.
|
||||
struct SerializedWiimoteState
|
||||
{
|
||||
u8 length;
|
||||
std::array<u8, 24> data; // 12 bytes Wiimote, 6 bytes MotionPlus, 6 bytes Extension
|
||||
};
|
||||
|
||||
SerializedWiimoteState SerializeDesiredState(const DesiredWiimoteState& state);
|
||||
bool DeserializeDesiredState(DesiredWiimoteState* state, const SerializedWiimoteState& serialized);
|
||||
} // namespace WiimoteEmu
|
||||
@@ -142,7 +142,8 @@ void Wiimote::SendAck(OutputReportID rpt_id, ErrorCode error_code)
|
||||
InterruptDataInputCallback(rpt.GetData(), rpt.GetSize());
|
||||
}
|
||||
|
||||
void Wiimote::HandleExtensionSwap()
|
||||
void Wiimote::HandleExtensionSwap(ExtensionNumber desired_extension_number,
|
||||
bool desired_motion_plus)
|
||||
{
|
||||
if (WIIMOTE_BALANCE_BOARD == m_index)
|
||||
{
|
||||
@@ -151,15 +152,13 @@ void Wiimote::HandleExtensionSwap()
|
||||
return;
|
||||
}
|
||||
|
||||
ExtensionNumber desired_extension_number =
|
||||
static_cast<ExtensionNumber>(m_attachments->GetSelectedAttachment());
|
||||
|
||||
const bool desired_motion_plus = m_motion_plus_setting.GetValue();
|
||||
|
||||
// FYI: AttachExtension also connects devices to the i2c bus
|
||||
|
||||
if (m_is_motion_plus_attached && !desired_motion_plus)
|
||||
{
|
||||
INFO_LOG_FMT(WIIMOTE, "Detaching Motion Plus (Wiimote {} in slot {})", m_index,
|
||||
m_bt_device_index);
|
||||
|
||||
// M+ is attached and it's not wanted, so remove it.
|
||||
m_extension_port.AttachExtension(GetNoneExtension());
|
||||
m_is_motion_plus_attached = false;
|
||||
@@ -184,6 +183,9 @@ void Wiimote::HandleExtensionSwap()
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG_FMT(WIIMOTE, "Attaching Motion Plus (Wiimote {} in slot {})", m_index,
|
||||
m_bt_device_index);
|
||||
|
||||
// No extension attached so attach M+.
|
||||
m_is_motion_plus_attached = true;
|
||||
m_extension_port.AttachExtension(&m_motion_plus);
|
||||
@@ -198,12 +200,18 @@ void Wiimote::HandleExtensionSwap()
|
||||
// A different extension is wanted (either by user or by the M+ logic above)
|
||||
if (GetActiveExtensionNumber() != ExtensionNumber::NONE)
|
||||
{
|
||||
INFO_LOG_FMT(WIIMOTE, "Detaching Extension (Wiimote {} in slot {})", m_index,
|
||||
m_bt_device_index);
|
||||
|
||||
// First we must detach the current extension.
|
||||
// The next call will change to the new extension if needed.
|
||||
m_active_extension = ExtensionNumber::NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG_FMT(WIIMOTE, "Switching to Extension {} (Wiimote {} in slot {})",
|
||||
desired_extension_number, m_index, m_bt_device_index);
|
||||
|
||||
m_active_extension = desired_extension_number;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/DesiredExtensionState.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "InputCommon/ControllerEmu/Control/Input.h"
|
||||
@@ -105,7 +107,7 @@ Classic::Classic() : Extension1stParty("Classic", _trans("Classic Controller"))
|
||||
}
|
||||
}
|
||||
|
||||
void Classic::Update()
|
||||
void Classic::BuildDesiredExtensionState(DesiredExtensionState* target_state)
|
||||
{
|
||||
DataFormat classic_data = {};
|
||||
|
||||
@@ -149,7 +151,12 @@ void Classic::Update()
|
||||
|
||||
classic_data.SetButtons(buttons);
|
||||
|
||||
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = classic_data;
|
||||
target_state->data = classic_data;
|
||||
}
|
||||
|
||||
void Classic::Update(const DesiredExtensionState& target_state)
|
||||
{
|
||||
DefaultExtensionUpdate<DataFormat>(&m_reg, target_state);
|
||||
}
|
||||
|
||||
void Classic::Reset()
|
||||
|
||||
@@ -178,7 +178,8 @@ public:
|
||||
|
||||
Classic();
|
||||
|
||||
void Update() override;
|
||||
void BuildDesiredExtensionState(DesiredExtensionState* target_state) override;
|
||||
void Update(const DesiredExtensionState& target_state) override;
|
||||
void Reset() override;
|
||||
|
||||
ControllerEmu::ControlGroup* GetGroup(ClassicGroup group);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/DrawsomeTablet.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Drums.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Guitar.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Shinkansen.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/TaTaCon.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Turntable.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/UDrawTablet.h"
|
||||
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
struct DesiredExtensionState
|
||||
{
|
||||
using ExtensionData =
|
||||
std::variant<std::monostate, Nunchuk::DataFormat, Classic::DataFormat, Guitar::DataFormat,
|
||||
Drums::DesiredState, Turntable::DataFormat, UDrawTablet::DataFormat,
|
||||
DrawsomeTablet::DataFormat, TaTaCon::DataFormat, Shinkansen::DesiredState>;
|
||||
ExtensionData data = std::monostate();
|
||||
|
||||
static_assert(std::is_same_v<std::monostate,
|
||||
std::variant_alternative_t<ExtensionNumber::NONE, ExtensionData>>);
|
||||
static_assert(
|
||||
std::is_same_v<Nunchuk::DataFormat,
|
||||
std::variant_alternative_t<ExtensionNumber::NUNCHUK, ExtensionData>>);
|
||||
static_assert(
|
||||
std::is_same_v<Classic::DataFormat,
|
||||
std::variant_alternative_t<ExtensionNumber::CLASSIC, ExtensionData>>);
|
||||
static_assert(std::is_same_v<Guitar::DataFormat,
|
||||
std::variant_alternative_t<ExtensionNumber::GUITAR, ExtensionData>>);
|
||||
static_assert(std::is_same_v<Drums::DesiredState,
|
||||
std::variant_alternative_t<ExtensionNumber::DRUMS, ExtensionData>>);
|
||||
static_assert(
|
||||
std::is_same_v<Turntable::DataFormat,
|
||||
std::variant_alternative_t<ExtensionNumber::TURNTABLE, ExtensionData>>);
|
||||
static_assert(
|
||||
std::is_same_v<UDrawTablet::DataFormat,
|
||||
std::variant_alternative_t<ExtensionNumber::UDRAW_TABLET, ExtensionData>>);
|
||||
static_assert(
|
||||
std::is_same_v<DrawsomeTablet::DataFormat,
|
||||
std::variant_alternative_t<ExtensionNumber::DRAWSOME_TABLET, ExtensionData>>);
|
||||
static_assert(
|
||||
std::is_same_v<TaTaCon::DataFormat,
|
||||
std::variant_alternative_t<ExtensionNumber::TATACON, ExtensionData>>);
|
||||
static_assert(
|
||||
std::is_same_v<Shinkansen::DesiredState,
|
||||
std::variant_alternative_t<ExtensionNumber::SHINKANSEN, ExtensionData>>);
|
||||
static_assert(std::variant_size_v<DesiredExtensionState::ExtensionData> == ExtensionNumber::MAX);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void DefaultExtensionUpdate(EncryptedExtension::Register* reg,
|
||||
const DesiredExtensionState& target_state)
|
||||
{
|
||||
if (std::holds_alternative<T>(target_state.data))
|
||||
{
|
||||
Common::BitCastPtr<T>(®->controller_data) = std::get<T>(target_state.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
Common::BitCastPtr<T>(®->controller_data) = T{};
|
||||
}
|
||||
}
|
||||
} // namespace WiimoteEmu
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/DesiredExtensionState.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "InputCommon/ControllerEmu/Control/Input.h"
|
||||
@@ -31,9 +33,9 @@ DrawsomeTablet::DrawsomeTablet() : Extension3rdParty("Drawsome", _trans("Drawsom
|
||||
m_touch->AddInput(ControllerEmu::Translate, _trans("Pressure"));
|
||||
}
|
||||
|
||||
void DrawsomeTablet::Update()
|
||||
void DrawsomeTablet::BuildDesiredExtensionState(DesiredExtensionState* target_state)
|
||||
{
|
||||
DataFormat tablet_data = {};
|
||||
DataFormat& tablet_data = target_state->data.emplace<DataFormat>();
|
||||
|
||||
// Stylus X/Y (calibrated values):
|
||||
constexpr u16 MIN_X = 0x0000;
|
||||
@@ -77,8 +79,11 @@ void DrawsomeTablet::Update()
|
||||
|
||||
tablet_data.pressure1 = u8(pressure);
|
||||
tablet_data.pressure2 = u8(pressure >> 8);
|
||||
}
|
||||
|
||||
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = tablet_data;
|
||||
void DrawsomeTablet::Update(const DesiredExtensionState& target_state)
|
||||
{
|
||||
DefaultExtensionUpdate<DataFormat>(&m_reg, target_state);
|
||||
}
|
||||
|
||||
void DrawsomeTablet::Reset()
|
||||
|
||||
@@ -27,7 +27,8 @@ class DrawsomeTablet : public Extension3rdParty
|
||||
public:
|
||||
DrawsomeTablet();
|
||||
|
||||
void Update() override;
|
||||
void BuildDesiredExtensionState(DesiredExtensionState* target_state) override;
|
||||
void Update(const DesiredExtensionState& target_state) override;
|
||||
void Reset() override;
|
||||
|
||||
ControllerEmu::ControlGroup* GetGroup(DrawsomeTabletGroup group);
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/DesiredExtensionState.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "InputCommon/ControllerEmu/Control/Input.h"
|
||||
@@ -77,8 +79,43 @@ Drums::Drums() : Extension1stParty("Drums", _trans("Drum Kit"))
|
||||
m_buttons->AddInput(ControllerEmu::DoNotTranslate, "+");
|
||||
}
|
||||
|
||||
void Drums::Update()
|
||||
void Drums::BuildDesiredExtensionState(DesiredExtensionState* target_state)
|
||||
{
|
||||
DesiredState& state = target_state->data.emplace<DesiredState>();
|
||||
|
||||
{
|
||||
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
|
||||
|
||||
state.stick_x = MapFloat(stick_state.x, STICK_CENTER, STICK_MIN, STICK_MAX);
|
||||
state.stick_y = MapFloat(stick_state.y, STICK_CENTER, STICK_MIN, STICK_MAX);
|
||||
}
|
||||
|
||||
state.buttons = 0;
|
||||
m_buttons->GetState(&state.buttons, drum_button_bitmasks.data());
|
||||
|
||||
state.drum_pads = 0;
|
||||
m_pads->GetState(&state.drum_pads, drum_pad_bitmasks.data());
|
||||
|
||||
state.softness = u8(7 - std::lround(m_hit_strength_setting.GetValue() * 7 / 100));
|
||||
}
|
||||
|
||||
void Drums::Update(const DesiredExtensionState& target_state)
|
||||
{
|
||||
DesiredState desired_state;
|
||||
if (std::holds_alternative<DesiredState>(target_state.data))
|
||||
{
|
||||
desired_state = std::get<DesiredState>(target_state.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set a sane default
|
||||
desired_state.stick_x = STICK_CENTER;
|
||||
desired_state.stick_y = STICK_CENTER;
|
||||
desired_state.buttons = 0;
|
||||
desired_state.drum_pads = 0;
|
||||
desired_state.softness = 7;
|
||||
}
|
||||
|
||||
DataFormat drum_data = {};
|
||||
|
||||
// The meaning of these bits are unknown but they are usually set.
|
||||
@@ -94,20 +131,12 @@ void Drums::Update()
|
||||
drum_data.no_velocity_data_2 = 1;
|
||||
drum_data.softness = 7;
|
||||
|
||||
// Stick.
|
||||
{
|
||||
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
|
||||
|
||||
drum_data.stick_x = MapFloat(stick_state.x, STICK_CENTER, STICK_MIN, STICK_MAX);
|
||||
drum_data.stick_y = MapFloat(stick_state.y, STICK_CENTER, STICK_MIN, STICK_MAX);
|
||||
}
|
||||
|
||||
// Buttons.
|
||||
m_buttons->GetState(&drum_data.buttons, drum_button_bitmasks.data());
|
||||
drum_data.stick_x = desired_state.stick_x;
|
||||
drum_data.stick_y = desired_state.stick_y;
|
||||
drum_data.buttons = desired_state.buttons;
|
||||
|
||||
// Drum pads.
|
||||
u8 current_pad_input = 0;
|
||||
m_pads->GetState(¤t_pad_input, drum_pad_bitmasks.data());
|
||||
u8 current_pad_input = desired_state.drum_pads;
|
||||
m_new_pad_hits |= ~m_prev_pad_input & current_pad_input;
|
||||
m_prev_pad_input = current_pad_input;
|
||||
|
||||
@@ -130,8 +159,7 @@ void Drums::Update()
|
||||
drum_data.no_velocity_data_1 = 0;
|
||||
drum_data.no_velocity_data_2 = 0;
|
||||
|
||||
// Set softness from user-configured hit strength setting.
|
||||
drum_data.softness = u8(7 - std::lround(m_hit_strength_setting.GetValue() * 7 / 100));
|
||||
drum_data.softness = desired_state.softness;
|
||||
|
||||
// A drum-pad hit causes the relevent bit to be triggered for the next 10 frames.
|
||||
constexpr u8 HIT_FRAME_COUNT = 10;
|
||||
|
||||
@@ -28,6 +28,15 @@ enum class DrumsGroup
|
||||
class Drums : public Extension1stParty
|
||||
{
|
||||
public:
|
||||
struct DesiredState
|
||||
{
|
||||
u8 stick_x; // 6 bits
|
||||
u8 stick_y; // 6 bits
|
||||
u8 buttons; // 2 bits
|
||||
u8 drum_pads; // 6 bits
|
||||
u8 softness; // 3 bits
|
||||
};
|
||||
|
||||
struct DataFormat
|
||||
{
|
||||
u8 stick_x : 6;
|
||||
@@ -77,7 +86,8 @@ public:
|
||||
|
||||
Drums();
|
||||
|
||||
void Update() override;
|
||||
void BuildDesiredExtensionState(DesiredExtensionState* target_state) override;
|
||||
void Update(const DesiredExtensionState& target_state) override;
|
||||
void Reset() override;
|
||||
|
||||
ControllerEmu::ControlGroup* GetGroup(DrumsGroup group);
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Inline.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/DesiredExtensionState.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
@@ -43,7 +45,12 @@ bool None::ReadDeviceDetectPin() const
|
||||
return false;
|
||||
}
|
||||
|
||||
void None::Update()
|
||||
void None::BuildDesiredExtensionState(DesiredExtensionState* target_state)
|
||||
{
|
||||
target_state->data.emplace<std::monostate>();
|
||||
}
|
||||
|
||||
void None::Update(const DesiredExtensionState& target_state)
|
||||
{
|
||||
// Nothing needed.
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
struct DesiredExtensionState;
|
||||
|
||||
class Extension : public ControllerEmu::EmulatedController, public I2CSlave
|
||||
{
|
||||
public:
|
||||
@@ -32,7 +34,8 @@ public:
|
||||
|
||||
virtual void Reset() = 0;
|
||||
virtual void DoState(PointerWrap& p) = 0;
|
||||
virtual void Update() = 0;
|
||||
virtual void BuildDesiredExtensionState(DesiredExtensionState* target_state) = 0;
|
||||
virtual void Update(const DesiredExtensionState& target_state) = 0;
|
||||
|
||||
private:
|
||||
const char* const m_config_name;
|
||||
@@ -46,7 +49,8 @@ public:
|
||||
|
||||
private:
|
||||
bool ReadDeviceDetectPin() const override;
|
||||
void Update() override;
|
||||
void BuildDesiredExtensionState(DesiredExtensionState* target_state) override;
|
||||
void Update(const DesiredExtensionState& target_state) override;
|
||||
void Reset() override;
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
@@ -67,7 +71,6 @@ public:
|
||||
// TODO: TAS handles encryption poorly.
|
||||
EncryptionKey ext_key;
|
||||
|
||||
protected:
|
||||
static constexpr int CALIBRATION_CHECKSUM_BYTES = 2;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
@@ -97,6 +100,7 @@ protected:
|
||||
|
||||
static_assert(0x100 == sizeof(Register));
|
||||
|
||||
protected:
|
||||
Register m_reg = {};
|
||||
|
||||
void Reset() override;
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/DesiredExtensionState.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "InputCommon/ControllerEmu/Control/Input.h"
|
||||
@@ -93,7 +95,7 @@ Guitar::Guitar() : Extension1stParty(_trans("Guitar"))
|
||||
groups.emplace_back(m_slider_bar = new ControllerEmu::Slider(_trans("Slider Bar")));
|
||||
}
|
||||
|
||||
void Guitar::Update()
|
||||
void Guitar::BuildDesiredExtensionState(DesiredExtensionState* target_state)
|
||||
{
|
||||
DataFormat guitar_data = {};
|
||||
|
||||
@@ -135,7 +137,12 @@ void Guitar::Update()
|
||||
// flip button bits
|
||||
guitar_data.bt ^= 0xFFFF;
|
||||
|
||||
Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = guitar_data;
|
||||
target_state->data = guitar_data;
|
||||
}
|
||||
|
||||
void Guitar::Update(const DesiredExtensionState& target_state)
|
||||
{
|
||||
DefaultExtensionUpdate<DataFormat>(&m_reg, target_state);
|
||||
}
|
||||
|
||||
void Guitar::Reset()
|
||||
|
||||
@@ -50,7 +50,8 @@ public:
|
||||
|
||||
Guitar();
|
||||
|
||||
void Update() override;
|
||||
void BuildDesiredExtensionState(DesiredExtensionState* target_state) override;
|
||||
void Update(const DesiredExtensionState& target_state) override;
|
||||
void Reset() override;
|
||||
|
||||
ControllerEmu::ControlGroup* GetGroup(GuitarGroup group);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user