@@ -0,0 +1,131 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <array>

#include "Common/CommonTypes.h"
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
#include "Core/HW/WiimoteEmu/I2CBus.h"

namespace WiimoteEmu
{
struct MotionPlus : public Extension
{
public:
MotionPlus();

void Update() override;
void Reset() override;
void DoState(PointerWrap& p) override;

ExtensionPort& GetExtPort();

private:
#pragma pack(push, 1)
struct DataFormat
{
// yaw1, roll1, pitch1: Bits 0-7
// yaw2, roll2, pitch2: Bits 8-13

u8 yaw1;
u8 roll1;
u8 pitch1;

u8 pitch_slow : 1;
u8 yaw_slow : 1;
u8 yaw2 : 6;

u8 extension_connected : 1;
u8 roll_slow : 1;
u8 roll2 : 6;

u8 zero : 1;
u8 is_mp_data : 1;
u8 pitch2 : 6;
};

struct Register
{
u8 controller_data[21];
u8 unknown_0x15[11];

// address 0x20
u8 calibration_data[0x20];

u8 unknown_0x40[0x10];

// address 0x50
u8 cert_data[0x40];

u8 unknown_0x90[0x60];

// address 0xF0
u8 initialized;

// address 0xF1
u8 cert_enable;

// Conduit 2 writes 1 byte to 0xf2 on calibration screen
u8 unknown_0xf2[5];

// address 0xf7
// Wii Sports Resort reads regularly
// Value starts at 0x00 and goes up after activation (not initialization)
// Immediately returns 0x02, even still after 15 and 30 seconds
// After the first data read the value seems to progress to 0x4,0x8,0xc,0xe
// More typical seems to be 2,8,c,e
// A value of 0xe triggers the game to read 64 bytes from 0x50
// The game claims M+ is disconnected after this read of unsatisfactory data
u8 cert_ready;

u8 unknown_0xf8[2];

// address 0xFA
u8 ext_identifier[6];
};
#pragma pack(pop)

static_assert(sizeof(DataFormat) == 6, "Wrong size");
static_assert(0x100 == sizeof(Register));

static const u8 INACTIVE_DEVICE_ADDR = 0x53;
static const u8 ACTIVE_DEVICE_ADDR = 0x52;

enum class PassthroughMode : u8
{
Disabled = 0x04,
Nunchuk = 0x05,
Classic = 0x07,
};

bool IsActive() const;

PassthroughMode GetPassthroughMode() const;

// TODO: when activated it seems the motion plus reactivates the extension
// It sends 0x55 to 0xf0
// It also writes 0x00 to slave:0x52 addr:0xfa for some reason
// And starts a write to 0xfa but never writes bytes..
// It tries to read data at 0x00 for 3 times (failing)
// then it reads the 16 bytes of calibration at 0x20 and stops

// TODO: if an extension is attached after activation, it also does this.

int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;

bool ReadDeviceDetectPin() const override;
bool IsButtonPressed() const override;

// TODO: rename m_

Register reg_data = {};

// The port on the end of the motion plus:
I2CBus i2c_bus;
ExtensionPort m_extension_port{&i2c_bus};
};
} // namespace WiimoteEmu
@@ -2,11 +2,14 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "Core/HW/WiimoteEmu/Speaker.h"

#include <memory>

#include "AudioCommon/AudioCommon.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/MathUtil.h"
#include "Core/ConfigManager.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
@@ -68,67 +71,71 @@ void stopdamnwav()
}
#endif

void Wiimote::SpeakerData(const wm_speaker_data* sd)
void SpeakerLogic::SpeakerData(const u8* data, int length, int speaker_pan)
{
// TODO: should we still process samples for the decoder state?
if (!SConfig::GetInstance().m_WiimoteEnableSpeaker)
return;
if (m_reg_speaker.volume == 0 || m_reg_speaker.sample_rate == 0 || sd->length == 0)

if (reg_data.sample_rate == 0 || length == 0)
return;

// Even if volume is zero we process samples to maintain proper decoder state.

// TODO consider using static max size instead of new
std::unique_ptr<s16[]> samples(new s16[sd->length * 2]);
std::unique_ptr<s16[]> samples(new s16[length * 2]);

unsigned int sample_rate_dividend, sample_length;
u8 volume_divisor;

if (m_reg_speaker.format == 0x40)
if (reg_data.format == SpeakerLogic::DATA_FORMAT_PCM)
{
// 8 bit PCM
for (int i = 0; i < sd->length; ++i)
for (int i = 0; i < length; ++i)
{
samples[i] = ((s16)(s8)sd->data[i]) << 8;
samples[i] = ((s16)(s8)data[i]) * 0x100;
}

// Following details from http://wiibrew.org/wiki/Wiimote#Speaker
sample_rate_dividend = 12000000;
volume_divisor = 0xff;
sample_length = (unsigned int)sd->length;
sample_length = (unsigned int)length;
}
else if (m_reg_speaker.format == 0x00)
else if (reg_data.format == SpeakerLogic::DATA_FORMAT_ADPCM)
{
// 4 bit Yamaha ADPCM (same as dreamcast)
for (int i = 0; i < sd->length; ++i)
for (int i = 0; i < length; ++i)
{
samples[i * 2] = adpcm_yamaha_expand_nibble(m_adpcm_state, (sd->data[i] >> 4) & 0xf);
samples[i * 2 + 1] = adpcm_yamaha_expand_nibble(m_adpcm_state, sd->data[i] & 0xf);
samples[i * 2] = adpcm_yamaha_expand_nibble(adpcm_state, (data[i] >> 4) & 0xf);
samples[i * 2 + 1] = adpcm_yamaha_expand_nibble(adpcm_state, data[i] & 0xf);
}

// Following details from http://wiibrew.org/wiki/Wiimote#Speaker
sample_rate_dividend = 6000000;

// 0 - 127
// TODO: does it go beyond 127 for format == 0x40?
volume_divisor = 0x7F;
sample_length = (unsigned int)sd->length * 2;
sample_length = (unsigned int)length * 2;
}
else
{
ERROR_LOG(IOS_WIIMOTE, "Unknown speaker format %x", m_reg_speaker.format);
ERROR_LOG(IOS_WIIMOTE, "Unknown speaker format %x", reg_data.format);
return;
}

// Speaker Pan
unsigned int vol = (unsigned int)(m_options->numeric_settings[0]->GetValue() * 100);
if (reg_data.volume > volume_divisor)
{
DEBUG_LOG(IOS_WIIMOTE, "Wiimote volume is higher than suspected maximum!");
volume_divisor = reg_data.volume;
}

unsigned int sample_rate = sample_rate_dividend / m_reg_speaker.sample_rate;
float speaker_volume_ratio = (float)m_reg_speaker.volume / volume_divisor;
unsigned int left_volume = (unsigned int)((128 + vol) * speaker_volume_ratio);
unsigned int right_volume = (unsigned int)((128 - vol) * speaker_volume_ratio);
// TODO: use speaker pan law

if (left_volume > 255)
left_volume = 255;
if (right_volume > 255)
right_volume = 255;
const unsigned int sample_rate = sample_rate_dividend / reg_data.sample_rate;
float speaker_volume_ratio = (float)reg_data.volume / volume_divisor;
// Sloppy math:
unsigned int left_volume =
MathUtil::Clamp<unsigned int>((0xff + (2 * speaker_pan)) * speaker_volume_ratio, 0, 0xff);
unsigned int right_volume =
MathUtil::Clamp<unsigned int>((0xff - (2 * speaker_pan)) * speaker_volume_ratio, 0, 0xff);

g_sound_stream->GetMixer()->SetWiimoteSpeakerVolume(left_volume, right_volume);

@@ -147,15 +154,57 @@ void Wiimote::SpeakerData(const wm_speaker_data* sd)
File::OpenFStream(ofile, "rmtdump.bin", ofile.binary | ofile.out);
wav.Start("rmtdump.wav", 6000);
}
wav.AddMonoSamples(samples.get(), sd->length * 2);
wav.AddMonoSamples(samples.get(), length * 2);
if (ofile.good())
{
for (int i = 0; i < sd->length; i++)
for (int i = 0; i < length; i++)
{
ofile << sd->data[i];
ofile << data[i];
}
}
num++;
#endif
}

void SpeakerLogic::Reset()
{
reg_data = {};

// Yamaha ADPCM state initialize
adpcm_state.predictor = 0;
adpcm_state.step = 127;
}

void SpeakerLogic::DoState(PointerWrap& p)
{
p.Do(adpcm_state);
p.Do(reg_data);
}

int SpeakerLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
{
if (I2C_ADDR != slave_addr)
return 0;

return RawRead(&reg_data, addr, count, data_out);
}

int SpeakerLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
{
if (I2C_ADDR != slave_addr)
return 0;

if (0x00 == addr)
{
ERROR_LOG(WIIMOTE, "Writing of speaker data to address 0x00 is unimplemented!");
return count;
}
else
{
// TODO: Does writing immediately change the decoder config even when active
// or does a write to 0x08 activate the new configuration or something?
return RawWrite(&reg_data, addr, count, data_in);
}
}

} // namespace WiimoteEmu
@@ -0,0 +1,67 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Core/HW/WiimoteEmu/I2CBus.h"

namespace WiimoteEmu
{
struct ADPCMState
{
s32 predictor, step;
};

class SpeakerLogic : public I2CSlave
{
public:
static const u8 I2C_ADDR = 0x51;

void Reset();
void DoState(PointerWrap& p);

void SpeakerData(const u8* data, int length, int speaker_pan);

private:
// TODO: enum class
static const u8 DATA_FORMAT_ADPCM = 0x00;
static const u8 DATA_FORMAT_PCM = 0x40;

// TODO: It seems reading address 0x00 should always return 0xff.
#pragma pack(push, 1)
struct Register
{
// Speaker reports result in a write of samples to addr 0x00 (which also plays sound)
u8 speaker_data;
u8 unk_1;
u8 format;
// seems to always play at 6khz no matter what this is set to?
// or maybe it only applies to pcm input
// Little-endian:
u16 sample_rate;
u8 volume;
u8 unk_5;
u8 unk_6;
// Reading this byte on real hardware seems to return 0x09:
u8 unk_7;
u8 unk_8;
u8 unknown[0xf6];
};
#pragma pack(pop)

static_assert(0x100 == sizeof(Register));

int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;

Register reg_data;

// TODO: What actions reset this state?
// Is this actually in the register somewhere?
ADPCMState adpcm_state;
};

} // namespace WiimoteEmu

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -28,6 +28,8 @@
#include "Common/ScopeGuard.h"
#include "Common/Thread.h"
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "Core/HW/WiimoteCommon/DataReport.h"
#include "Core/HW/WiimoteReal/IOWin.h"

// Create func_t function pointer type and declare a nullptr-initialized static variable of that
@@ -436,6 +438,8 @@ int ReadFromHandle(HANDLE& dev_handle, u8* buf)

bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMethod& method)
{
using namespace WiimoteCommon;

HANDLE dev_handle = CreateFile(device_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, nullptr);
@@ -445,7 +449,7 @@ bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMethod& meth
Common::ScopeGuard handle_guard{[&dev_handle] { CloseHandle(dev_handle); }};

u8 buf[MAX_PAYLOAD];
u8 const req_status_report[] = {WR_SET_REPORT | BT_OUTPUT, RT_REQUEST_STATUS, 0};
u8 const req_status_report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::RequestStatus), 0};
int invalid_report_count = 0;
int rc = WriteToHandle(dev_handle, method, req_status_report, sizeof(req_status_report));
while (rc > 0)
@@ -454,9 +458,9 @@ bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMethod& meth
if (rc <= 0)
break;

switch (buf[1])
switch (InputReportID(buf[1]))
{
case RT_STATUS_REPORT:
case InputReportID::Status:
return true;
default:
WARN_LOG(WIIMOTE, "IsWiimote(): Received unexpected report %02x", buf[1]);
@@ -691,34 +695,25 @@ bool WiimoteWindows::IsConnected() const
}

// See http://wiibrew.org/wiki/Wiimote for the Report IDs and its sizes
size_t GetReportSize(u8 report_id)
size_t GetReportSize(u8 rid)
{
using namespace WiimoteCommon;

const auto report_id = static_cast<InputReportID>(rid);

switch (report_id)
{
case RT_STATUS_REPORT:
return sizeof(wm_status_report);
case RT_READ_DATA_REPLY:
return sizeof(wm_read_data_reply);
case RT_ACK_DATA:
return sizeof(wm_acknowledge);
case RT_REPORT_CORE:
return sizeof(wm_report_core);
case RT_REPORT_CORE_ACCEL:
return sizeof(wm_report_core_accel);
case RT_REPORT_CORE_EXT8:
return sizeof(wm_report_core_ext8);
case RT_REPORT_CORE_ACCEL_IR12:
return sizeof(wm_report_core_accel_ir12);
case RT_REPORT_CORE_EXT19:
case RT_REPORT_CORE_ACCEL_EXT16:
case RT_REPORT_CORE_IR10_EXT9:
case RT_REPORT_CORE_ACCEL_IR10_EXT6:
case RT_REPORT_EXT21:
case RT_REPORT_INTERLEAVE1:
case RT_REPORT_INTERLEAVE2:
return sizeof(wm_report_ext21);
case InputReportID::Status:
return sizeof(InputReportStatus);
case InputReportID::ReadDataReply:
return sizeof(InputReportReadDataReply);
case InputReportID::Ack:
return sizeof(InputReportAck);
default:
return 0;
if (DataReportBuilder::IsValidMode(report_id))
return MakeDataReportManipulator(report_id, nullptr)->GetDataSize();
else
return 0;
}
}

@@ -1004,4 +999,4 @@ bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi)

return false;
}
}
} // namespace WiimoteReal
@@ -298,7 +298,7 @@ - (void)l2capChannelData:(IOBluetoothL2CAPChannel*)l2capChannel
return;
}

if (length > MAX_PAYLOAD)
if (length > WiimoteCommon::MAX_PAYLOAD)
{
WARN_LOG(WIIMOTE, "Dropping packet for Wiimote %i, too large", wm->GetIndex() + 1);
return;
@@ -10,6 +10,9 @@
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
#include "Core/HW/WiimoteReal/IOhidapi.h"

using namespace WiimoteCommon;
using namespace WiimoteReal;

static bool IsDeviceUsable(const std::string& device_path)
{
hid_device* handle = hid_open_path(device_path.c_str());
@@ -24,7 +27,7 @@ static bool IsDeviceUsable(const std::string& device_path)
// Some third-party adapters (DolphinBar) always expose all four Wii Remotes as HIDs
// even when they are not connected, which causes an endless error loop when we try to use them.
// Try to write a report to the device to see if this Wii Remote is really usable.
static const u8 report[] = {WR_SET_REPORT | BT_OUTPUT, RT_REQUEST_STATUS, 0};
static const u8 report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::RequestStatus), 0};
const int result = hid_write(handle, report, sizeof(report));
// The DolphinBar uses EPIPE to signal the absence of a Wii Remote connected to this HID.
if (result == -1 && errno != EPIPE)
@@ -145,4 +148,4 @@ int WiimoteHidapi::IOWrite(const u8* buf, size_t len)
}
return (result == 0) ? 1 : result;
}
}; // WiimoteReal
}; // namespace WiimoteReal
@@ -34,6 +34,8 @@ unsigned int g_wiimote_sources[MAX_BBMOTES];

namespace WiimoteReal
{
using namespace WiimoteCommon;

static void TryToConnectBalanceBoard(Wiimote*);
static void TryToConnectWiimote(Wiimote*);
static void HandleWiimoteDisconnect(int index);
@@ -69,7 +71,7 @@ void Wiimote::WriteReport(Report rpt)
bool const new_rumble_state = (rpt[2] & 0x1) != 0;

// If this is a rumble report and the rumble state didn't change, ignore.
if (rpt[1] == RT_RUMBLE && new_rumble_state == m_rumble_state)
if (rpt[1] == u8(OutputReportID::Rumble) && new_rumble_state == m_rumble_state)
return;

m_rumble_state = new_rumble_state;
@@ -95,24 +97,22 @@ void Wiimote::DisableDataReporting()
{
m_last_input_report.clear();

// This probably accomplishes nothing.
wm_report_mode rpt = {};
rpt.mode = RT_REPORT_CORE;
rpt.all_the_time = 0;
// This accomplishes very little:
OutputReportMode rpt = {};
rpt.mode = InputReportID::ReportCore;
rpt.continuous = 0;
rpt.rumble = 0;
QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt));
QueueReport(u8(OutputReportID::ReportMode), &rpt, sizeof(rpt));
}

void Wiimote::EnableDataReporting(u8 mode)
{
m_last_input_report.clear();

wm_report_mode rpt = {};
rpt.mode = mode;
rpt.all_the_time = 1;
OutputReportMode rpt = {};
rpt.mode = InputReportID(mode);
rpt.continuous = 1;
QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt));
QueueReport(u8(OutputReportID::ReportMode), &rpt, sizeof(rpt));
}

void Wiimote::SetChannel(u16 channel)
@@ -141,11 +141,11 @@ void Wiimote::ControlChannel(const u16 channel, const void* const data, const u3
else
{
InterruptChannel(channel, data, size);
const hid_packet* const hidp = reinterpret_cast<const hid_packet*>(data);
if (hidp->type == HID_TYPE_SET_REPORT)
const auto& hidp = *static_cast<const HIDPacket*>(data);
if (hidp.type == HID_TYPE_SET_REPORT)
{
u8 handshake_ok = HID_HANDSHAKE_SUCCESS;
Core::Callback_WiimoteInterruptChannel(m_index, channel, &handshake_ok, sizeof(handshake_ok));
u8 handshake = HID_HANDSHAKE_SUCCESS;
Core::Callback_WiimoteInterruptChannel(m_index, channel, &handshake, sizeof(handshake));
}
}
}
@@ -177,20 +177,21 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const

// Disallow games from turning off all of the LEDs.
// It makes Wiimote connection status confusing.
if (rpt[1] == RT_LEDS)
if (rpt[1] == u8(OutputReportID::LED))
{
auto& leds_rpt = *reinterpret_cast<wm_leds*>(&rpt[2]);
auto& leds_rpt = *reinterpret_cast<OutputReportLeds*>(&rpt[2]);
if (0 == leds_rpt.leds)
{
// Turn on ALL of the LEDs.
leds_rpt.leds = 0xf;
}
}
else if (rpt[1] == RT_WRITE_SPEAKER_DATA && (!SConfig::GetInstance().m_WiimoteEnableSpeaker ||
(!wm->m_status.speaker || wm->m_speaker_mute)))
else if (rpt[1] == u8(OutputReportID::SpeakerData) &&
(!SConfig::GetInstance().m_WiimoteEnableSpeaker ||
(!wm->m_status.speaker || wm->m_speaker_mute)))
{
// Translate speaker data reports into rumble reports.
rpt[1] = RT_RUMBLE;
rpt[1] = u8(OutputReportID::Rumble);
// Keep only the rumble bit.
rpt[2] &= 0x1;
rpt.resize(3);
@@ -253,11 +254,13 @@ bool Wiimote::IsBalanceBoard()
if (!ConnectInternal())
return false;
// Initialise the extension by writing 0x55 to 0xa400f0, then writing 0x00 to 0xa400fb.
// TODO: Use the structs for building these reports..
static const u8 init_extension_rpt1[MAX_PAYLOAD] = {
WR_SET_REPORT | BT_OUTPUT, RT_WRITE_DATA, 0x04, 0xa4, 0x00, 0xf0, 0x01, 0x55};
WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::WriteData), 0x04, 0xa4, 0x00, 0xf0, 0x01, 0x55};
static const u8 init_extension_rpt2[MAX_PAYLOAD] = {
WR_SET_REPORT | BT_OUTPUT, RT_WRITE_DATA, 0x04, 0xa4, 0x00, 0xfb, 0x01, 0x00};
static const u8 status_report[] = {WR_SET_REPORT | BT_OUTPUT, RT_REQUEST_STATUS, 0};
WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::WriteData), 0x04, 0xa4, 0x00, 0xfb, 0x01, 0x00};
static const u8 status_report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::RequestStatus),
0};
if (!IOWrite(init_extension_rpt1, sizeof(init_extension_rpt1)) ||
!IOWrite(init_extension_rpt2, sizeof(init_extension_rpt2)))
{
@@ -273,23 +276,29 @@ bool Wiimote::IsBalanceBoard()
if (ret == -1)
continue;

switch (buf[1])
switch (InputReportID(buf[1]))
{
case RT_STATUS_REPORT:
case InputReportID::Status:
{
const auto* status = reinterpret_cast<wm_status_report*>(&buf[2]);
const auto* status = reinterpret_cast<InputReportStatus*>(&buf[2]);
// A Balance Board has a Balance Board extension.
if (!status->extension)
return false;
// Read two bytes from 0xa400fe to identify the extension.
static const u8 identify_ext_rpt[] = {
WR_SET_REPORT | BT_OUTPUT, RT_READ_DATA, 0x04, 0xa4, 0x00, 0xfe, 0x02, 0x00};
static const u8 identify_ext_rpt[] = {WR_SET_REPORT | BT_OUTPUT,
u8(OutputReportID::ReadData),
0x04,
0xa4,
0x00,
0xfe,
0x02,
0x00};
ret = IOWrite(identify_ext_rpt, sizeof(identify_ext_rpt));
break;
}
case RT_READ_DATA_REPLY:
case InputReportID::ReadDataReply:
{
const auto* reply = reinterpret_cast<wm_read_data_reply*>(&buf[2]);
const auto* reply = reinterpret_cast<InputReportReadDataReply*>(&buf[2]);
if (Common::swap16(reply->address) != 0x00fe)
{
ERROR_LOG(WIIMOTE, "IsBalanceBoard(): Received unexpected data reply for address %X",
@@ -299,23 +308,25 @@ bool Wiimote::IsBalanceBoard()
// A Balance Board ext can be identified by checking for 0x0402.
return reply->data[0] == 0x04 && reply->data[1] == 0x02;
}
case RT_ACK_DATA:
case InputReportID::Ack:
{
const auto* ack = reinterpret_cast<wm_acknowledge*>(&buf[2]);
if (ack->reportID == RT_READ_DATA && ack->errorID != 0x00)
const auto* ack = reinterpret_cast<InputReportAck*>(&buf[2]);
if (ack->rpt_id == OutputReportID::ReadData && ack->error_code != ErrorCode::Success)
{
WARN_LOG(WIIMOTE, "Failed to read from 0xa400fe, assuming Wiimote is not a Balance Board.");
return false;
}
}
default:
break;
}
}
return false;
}

static bool IsDataReport(const Report& rpt)
{
return rpt.size() >= 2 && rpt[1] >= RT_REPORT_CORE;
return rpt.size() >= 2 && rpt[1] >= u8(InputReportID::ReportCore);
}

// Returns the next report that should be sent
@@ -364,29 +375,17 @@ void Wiimote::Update()

bool Wiimote::CheckForButtonPress()
{
const Report& rpt = ProcessReadQueue();
Report& rpt = ProcessReadQueue();
if (rpt.size() >= 4)
{
switch (rpt[1])
const auto mode = InputReportID(rpt[1]);
if (DataReportBuilder::IsValidMode(mode))
{
case RT_REPORT_CORE:
case RT_REPORT_CORE_ACCEL:
case RT_REPORT_CORE_EXT8:
case RT_REPORT_CORE_ACCEL_IR12:
case RT_REPORT_CORE_EXT19:
case RT_REPORT_CORE_ACCEL_EXT16:
case RT_REPORT_CORE_IR10_EXT9:
case RT_REPORT_CORE_ACCEL_IR10_EXT6:
case RT_REPORT_INTERLEAVE1:
case RT_REPORT_INTERLEAVE2:
// check any button without checking accelerometer data
if ((rpt[2] & 0x1F) != 0 || (rpt[3] & 0x9F) != 0)
{
return true;
}
break;
default:
break;
auto builder = MakeDataReportManipulator(mode, rpt.data() + 2);
ButtonData buttons = {};
builder->GetCoreData(&buttons);

return buttons.hex != 0;
}
}
return false;
@@ -401,17 +400,20 @@ void Wiimote::Prepare()
bool Wiimote::PrepareOnThread()
{
// core buttons, no continuous reporting
u8 static const mode_report[] = {WR_SET_REPORT | BT_OUTPUT, RT_REPORT_MODE, 0, RT_REPORT_CORE};
// TODO: use the structs..
u8 static const mode_report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::ReportMode), 0,
u8(InputReportID::ReportCore)};

// Set the active LEDs and turn on rumble.
u8 static led_report[] = {WR_SET_REPORT | BT_OUTPUT, RT_LEDS, 0};
led_report[2] = u8(WiimoteLED::LED_1 << (m_index % WIIMOTE_BALANCE_BOARD) | 0x1);
u8 static led_report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::LED), 0};
led_report[2] = u8(u8(LED::LED_1) << (m_index % WIIMOTE_BALANCE_BOARD) | 0x1);

// Turn off rumble
u8 static const rumble_report[] = {WR_SET_REPORT | BT_OUTPUT, RT_RUMBLE, 0};
u8 static const rumble_report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::Rumble), 0};

// Request status report
u8 static const req_status_report[] = {WR_SET_REPORT | BT_OUTPUT, RT_REQUEST_STATUS, 0};
u8 static const req_status_report[] = {WR_SET_REPORT | BT_OUTPUT,
u8(OutputReportID::RequestStatus), 0};
// TODO: check for sane response?

return (IOWrite(mode_report, sizeof(mode_report)) && IOWrite(led_report, sizeof(led_report)) &&
@@ -443,11 +445,10 @@ void Wiimote::EmuResume()

m_last_input_report.clear();

wm_report_mode rpt = {};
OutputReportMode rpt = {};
rpt.mode = wm->m_reporting_mode;
rpt.all_the_time = 1;
rpt.continuous = 1;
QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt));
QueueReport(u8(OutputReportID::ReportMode), &rpt, sizeof(rpt));

NOTICE_LOG(WIIMOTE, "Resuming Wiimote data reporting.");

@@ -458,11 +459,10 @@ void Wiimote::EmuPause()
{
m_last_input_report.clear();

wm_report_mode rpt = {};
rpt.mode = RT_REPORT_CORE;
rpt.all_the_time = 0;
OutputReportMode rpt = {};
rpt.mode = InputReportID::ReportCore;
rpt.continuous = 0;
QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt));
QueueReport(u8(OutputReportID::ReportMode), &rpt, sizeof(rpt));

NOTICE_LOG(WIIMOTE, "Pausing Wiimote data reporting.");

@@ -23,6 +23,37 @@ class PointerWrap;

namespace WiimoteReal
{
using WiimoteCommon::MAX_PAYLOAD;

using Report = std::vector<u8>;

constexpr u32 WIIMOTE_DEFAULT_TIMEOUT = 1000;

// Communication channels
enum WiimoteChannel
{
WC_OUTPUT = 0x11,
WC_INPUT = 0x13,
};

// The 4 most significant bits of the first byte of an outgoing command must be
// 0x50 if sending on the command channel and 0xA0 if sending on the interrupt
// channel. On Mac and Linux we use interrupt channel; on Windows, command.
enum WiimoteReport
{
#ifdef _WIN32
WR_SET_REPORT = 0x50
#else
WR_SET_REPORT = 0xA0
#endif
};

enum WiimoteBT
{
BT_INPUT = 0x01,
BT_OUTPUT = 0x02
};

class Wiimote
{
public:
@@ -174,4 +205,4 @@ bool IsNewWiimote(const std::string& identifier);
void InitAdapterClass();
#endif

} // WiimoteReal
} // namespace WiimoteReal
@@ -45,8 +45,14 @@
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteCommon/DataReport.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"

#include "Core/HW/WiimoteEmu/Encryption.h"
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
#include "Core/HW/WiimoteEmu/ExtensionPort.h"

#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
#include "Core/NetPlayProto.h"
@@ -64,6 +70,9 @@

namespace Movie
{
using namespace WiimoteCommon;
using namespace WiimoteEmu;

static bool s_bReadOnly = true;
static u32 s_rerecords = 0;
static PlayMode s_playMode = MODE_NONE;
@@ -640,23 +649,17 @@ static void SetInputDisplayString(ControllerState padState, int controllerID)
}

// NOTE: CPU Thread
static void SetWiiInputDisplayString(int remoteID, const u8* const data,
const WiimoteEmu::ReportFeatures& rptf, int ext,
const wiimote_key key)
static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt, int ext,
const EncryptionKey& key)
{
int controllerID = remoteID + 4;

std::string display_str = StringFromFormat("R%d:", remoteID + 1);

const u8* const coreData = rptf.core ? (data + rptf.core) : nullptr;
const u8* const accelData = rptf.accel ? (data + rptf.accel) : nullptr;
const u8* const irData = rptf.ir ? (data + rptf.ir) : nullptr;
const u8* const extData = rptf.ext ? (data + rptf.ext) : nullptr;

if (coreData)
if (rpt.HasCore())
{
wm_buttons buttons;
std::memcpy(&buttons, coreData, sizeof(buttons));
ButtonData buttons;
rpt.GetCoreData(&buttons);

if (buttons.left)
display_str += " LEFT";
@@ -680,32 +683,37 @@ static void SetWiiInputDisplayString(int remoteID, const u8* const data,
display_str += " 2";
if (buttons.home)
display_str += " HOME";
}

// A few bits of accelData are actually inside the coreData struct.
if (accelData)
{
wm_accel dt;
std::memcpy(&dt, accelData, sizeof(dt));
if (rpt.HasAccel())
{
DataReportBuilder::AccelData accel_data;
rpt.GetAccelData(&accel_data);

display_str +=
StringFromFormat(" ACC:%d,%d,%d", dt.x << 2 | buttons.acc_x_lsb,
dt.y << 2 | buttons.acc_y_lsb << 1, dt.z << 2 | buttons.acc_z_lsb << 1);
}
// FYI: This will only print partial data for interleaved reports.

display_str += StringFromFormat(" ACC:%d,%d,%d", accel_data.x, accel_data.y, accel_data.z);
}

if (irData)
if (rpt.HasIR())
{
const u8* const irData = rpt.GetIRDataPtr();

// TODO: This does not handle the different IR formats.

u16 x = irData[0] | ((irData[2] >> 4 & 0x3) << 8);
u16 y = irData[1] | ((irData[2] >> 6 & 0x3) << 8);
display_str += StringFromFormat(" IR:%d,%d", x, y);
}

// Nunchuk
if (extData && ext == 1)
if (rpt.HasExt() && ext == ExtensionNumber::NUNCHUK)
{
wm_nc nunchuk;
memcpy(&nunchuk, extData, sizeof(wm_nc));
WiimoteDecrypt(&key, (u8*)&nunchuk, 0, sizeof(wm_nc));
const u8* const extData = rpt.GetExtDataPtr();

Nunchuk::DataFormat nunchuk;
memcpy(&nunchuk, extData, sizeof(nunchuk));
key.Decrypt((u8*)&nunchuk, 0, sizeof(nunchuk));
nunchuk.bt.hex = nunchuk.bt.hex ^ 0x3;

std::string accel = StringFromFormat(
@@ -721,20 +729,22 @@ static void SetWiiInputDisplayString(int remoteID, const u8* const data,
}

// Classic controller
if (extData && ext == 2)
if (rpt.HasExt() && ext == ExtensionNumber::CLASSIC)
{
wm_classic_extension cc;
memcpy(&cc, extData, sizeof(wm_classic_extension));
WiimoteDecrypt(&key, (u8*)&cc, 0, sizeof(wm_classic_extension));
const u8* const extData = rpt.GetExtDataPtr();

Classic::DataFormat cc;
memcpy(&cc, extData, sizeof(cc));
key.Decrypt((u8*)&cc, 0, sizeof(cc));
cc.bt.hex = cc.bt.hex ^ 0xFFFF;

if (cc.bt.regular_data.dpad_left)
if (cc.bt.dpad_left)
display_str += " LEFT";
if (cc.bt.dpad_right)
display_str += " RIGHT";
if (cc.bt.dpad_down)
display_str += " DOWN";
if (cc.bt.regular_data.dpad_up)
if (cc.bt.dpad_up)
display_str += " UP";
if (cc.bt.a)
display_str += " A";
@@ -757,7 +767,7 @@ static void SetWiiInputDisplayString(int remoteID, const u8* const data,

display_str += Analog1DToString(cc.lt1 | (cc.lt2 << 3), " L", 31);
display_str += Analog1DToString(cc.rt, " R", 31);
display_str += Analog2DToString(cc.regular_data.lx, cc.regular_data.ly, " ANA", 63);
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);
}

@@ -815,13 +825,13 @@ void RecordInput(const GCPadStatus* PadStatus, int controllerID)
}

// NOTE: CPU Thread
void CheckWiimoteStatus(int wiimote, const u8* data, const WiimoteEmu::ReportFeatures& rptf,
int ext, const wiimote_key key)
void CheckWiimoteStatus(int wiimote, const DataReportBuilder& rpt, int ext,
const EncryptionKey& key)
{
SetWiiInputDisplayString(wiimote, data, rptf, ext, key);
SetWiiInputDisplayString(wiimote, rpt, ext, key);

if (IsRecordingInput())
RecordWiimote(wiimote, data, rptf.size);
RecordWiimote(wiimote, rpt.GetDataPtr(), rpt.GetDataSize());
}

void RecordWiimote(int wiimote, const u8* data, u8 size)
@@ -1190,8 +1200,8 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
}

// NOTE: CPU Thread
bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf, int ext,
const wiimote_key key)
bool PlayWiimote(int wiimote, WiimoteCommon::DataReportBuilder& rpt, int ext,
const EncryptionKey& key)
{
if (!IsPlayingInput() || !IsUsingWiimote(wiimote) || s_temp_input.empty())
return false;
@@ -1204,9 +1214,8 @@ bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf,
return false;
}

u8 size = rptf.size;

u8 sizeInMovie = s_temp_input[s_currentByte];
const u8 size = rpt.GetDataSize();
const u8 sizeInMovie = s_temp_input[s_currentByte];

if (size != sizeInMovie)
{
@@ -1230,7 +1239,7 @@ bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf,
return false;
}

memcpy(data, &s_temp_input[s_currentByte], size);
memcpy(rpt.GetDataPtr(), &s_temp_input[s_currentByte], size);
s_currentByte += size;

s_currentInputCount++;
@@ -1349,11 +1358,10 @@ void CallGCInputManip(GCPadStatus* PadStatus, int controllerID)
s_gc_manip_func(PadStatus, controllerID);
}
// NOTE: CPU Thread
void CallWiiInputManip(u8* data, WiimoteEmu::ReportFeatures rptf, int controllerID, int ext,
const wiimote_key key)
void CallWiiInputManip(DataReportBuilder& rpt, int controllerID, int ext, const EncryptionKey& key)
{
if (s_wii_manip_func)
s_wii_manip_func(data, rptf, controllerID, ext, key);
s_wii_manip_func(rpt, controllerID, ext, key);
}

// NOTE: GPU Thread
@@ -15,11 +15,15 @@ struct BootParameters;

struct GCPadStatus;
class PointerWrap;
struct wiimote_key;

namespace WiimoteCommon
{
class DataReportBuilder;
}

namespace WiimoteEmu
{
struct ReportFeatures;
class EncryptionKey;
}

// Per-(video )Movie actions
@@ -165,27 +169,27 @@ bool PlayInput(const std::string& movie_path, std::optional<std::string>* savest
void LoadInput(const std::string& movie_path);
void ReadHeader();
void PlayController(GCPadStatus* PadStatus, int controllerID);
bool PlayWiimote(int wiimote, u8* data, const struct WiimoteEmu::ReportFeatures& rptf, int ext,
const wiimote_key key);
bool PlayWiimote(int wiimote, WiimoteCommon::DataReportBuilder& rpt, int ext,
const WiimoteEmu::EncryptionKey& key);
void EndPlayInput(bool cont);
void SaveRecording(const std::string& filename);
void DoState(PointerWrap& p);
void Shutdown();
void CheckPadStatus(const GCPadStatus* PadStatus, int controllerID);
void CheckWiimoteStatus(int wiimote, const u8* data, const struct WiimoteEmu::ReportFeatures& rptf,
int ext, const wiimote_key key);
void CheckWiimoteStatus(int wiimote, const WiimoteCommon::DataReportBuilder& rpt, int ext,
const WiimoteEmu::EncryptionKey& key);

std::string GetInputDisplay();
std::string GetRTCDisplay();

// Done this way to avoid mixing of core and gui code
using GCManipFunction = std::function<void(GCPadStatus*, int)>;
using WiiManipFunction =
std::function<void(u8*, WiimoteEmu::ReportFeatures, int, int, wiimote_key)>;
using WiiManipFunction = std::function<void(WiimoteCommon::DataReportBuilder&, int, int,
const WiimoteEmu::EncryptionKey&)>;

void SetGCInputManip(GCManipFunction);
void SetWiiInputManip(WiiManipFunction);
void CallGCInputManip(GCPadStatus* PadStatus, int controllerID);
void CallWiiInputManip(u8* core, WiimoteEmu::ReportFeatures rptf, int controllerID, int ext,
const wiimote_key key);
void CallWiiInputManip(WiimoteCommon::DataReportBuilder& rpt, int controllerID, int ext,
const WiimoteEmu::EncryptionKey& key);
} // namespace Movie
@@ -43,6 +43,7 @@
#include "Core/HW/Sram.h"
#include "Core/HW/WiiSave.h"
#include "Core/HW/WiiSaveStructs.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Core/IOS/FS/FileSystem.h"
@@ -52,7 +53,7 @@
#include "Core/Movie.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/WiiRoot.h"
#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/GCAdapter.h"
#include "InputCommon/InputConfig.h"
#include "UICommon/GameFile.h"
@@ -61,6 +62,8 @@

namespace NetPlay
{
using namespace WiimoteCommon;

static std::mutex crit_netplay_client;
static NetPlayClient* netplay_client = nullptr;
static std::unique_ptr<IOS::HLE::FS::FileSystem> s_wii_sync_fs;
@@ -2188,11 +2191,10 @@ void SetupWiimotes()
{
if (wiimote_map[i] > 0)
{
static_cast<ControllerEmu::Extension*>(
static_cast<WiimoteEmu::Wiimote*>(
Wiimote::GetConfig()->GetController(static_cast<int>(i)))
->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Extension))
->switch_extension = netplay_settings.m_WiimoteExtension[i];
static_cast<ControllerEmu::Attachments*>(
static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(int(i)))
->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments))
->SetSelectedAttachment(netplay_settings.m_WiimoteExtension[i]);
}
}
}
@@ -49,7 +49,7 @@
#include "Core/IOS/IOS.h"
#include "Core/NetPlayClient.h" //for NetPlayUI
#include "DiscIO/Enums.h"
#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/GCPadStatus.h"
#include "InputCommon/InputConfig.h"
#include "UICommon/GameFile.h"
@@ -1217,11 +1217,11 @@ bool NetPlayServer::StartGame()

for (size_t i = 0; i < m_settings.m_WiimoteExtension.size(); i++)
{
const int extension = static_cast<ControllerEmu::Extension*>(
static_cast<WiimoteEmu::Wiimote*>(
Wiimote::GetConfig()->GetController(static_cast<int>(i)))
->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Extension))
->switch_extension;
const int extension =
static_cast<ControllerEmu::Attachments*>(
static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(int(i)))
->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments))
->GetSelectedAttachment();
spac << extension;
}

@@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;

// Don't forget to increase this after doing changes on the savestate system
static const u32 STATE_VERSION = 102; // Last changed in PR 7742
static const u32 STATE_VERSION = 103; // Last changed in PR 7674

// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,
@@ -9,6 +9,11 @@
#include <QLabel>

#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/Extension/Classic.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/Turntable.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"

#include "InputCommon/InputConfig.h"
@@ -16,7 +16,7 @@
#include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuExtension.h"

#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/Setting/BooleanSetting.h"
#include "InputCommon/InputConfig.h"

@@ -40,12 +40,13 @@ void WiimoteEmuGeneral::CreateMainLayout()
m_main_layout->addWidget(CreateGroupBox(
tr("Hotkeys"), Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Hotkeys)));

auto* extension_group = Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Extension);
auto* extension_group =
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Attachments);
auto* extension = CreateGroupBox(tr("Extension"), extension_group);
auto* ce_extension = static_cast<ControllerEmu::Extension*>(extension_group);
auto* ce_extension = static_cast<ControllerEmu::Attachments*>(extension_group);
m_extension_combo = new QComboBox();

for (const auto& attachment : ce_extension->attachments)
for (const auto& attachment : ce_extension->GetAttachmentList())
{
// TODO: Figure out how to localize this
m_extension_combo->addItem(QString::fromStdString(attachment->GetName()));
@@ -88,18 +89,18 @@ void WiimoteEmuGeneral::OnAttachmentChanged(int extension)

m_extension_widget->ChangeExtensionType(value_map[value]);

auto* ce_extension = static_cast<ControllerEmu::Extension*>(
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Extension));
ce_extension->switch_extension = extension;
auto* ce_extension = static_cast<ControllerEmu::Attachments*>(
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Attachments));
ce_extension->SetSelectedAttachment(extension);
SaveSettings();
}

void WiimoteEmuGeneral::Update()
{
auto* ce_extension = static_cast<ControllerEmu::Extension*>(
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Extension));
auto* ce_extension = static_cast<ControllerEmu::Attachments*>(
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Attachments));

m_extension_combo->setCurrentIndex(ce_extension->switch_extension);
m_extension_combo->setCurrentIndex(ce_extension->GetSelectedAttachment());
}

void WiimoteEmuGeneral::LoadSettings()
@@ -350,9 +350,9 @@ void MainWindow::CreateComponents()
m_gc_tas_input_windows[controller_id]->GetValues(pad_status);
});

Movie::SetWiiInputManip([this](u8* input_data, WiimoteEmu::ReportFeatures rptf, int controller_id,
int ext, wiimote_key key) {
m_wii_tas_input_windows[controller_id]->GetValues(input_data, rptf, ext, key);
Movie::SetWiiInputManip([this](WiimoteCommon::DataReportBuilder& rpt, int controller_id, int ext,
const WiimoteEmu::EncryptionKey& key) {
m_wii_tas_input_windows[controller_id]->GetValues(rpt, ext, key);
});

m_jit_widget = new JITWidget(this);
@@ -14,10 +14,16 @@
#include "Common/FileUtil.h"

#include "Core/Core.h"
#include "Core/HW/WiimoteEmu/Attachment/Classic.h"
#include "Core/HW/WiimoteEmu/Attachment/Nunchuk.h"
#include "Core/HW/WiimoteCommon/DataReport.h"
#include "Core/HW/WiimoteEmu/Encryption.h"
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"

#include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"

#include "Core/HW/WiimoteEmu/Camera.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"

#include "DolphinQt/QtUtils/AspectRatioWidget.h"
@@ -27,6 +33,8 @@

#include "InputCommon/InputConfig.h"

using namespace WiimoteCommon;

WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(parent), m_num(num)
{
const QKeySequence ir_x_shortcut_key_sequence = QKeySequence(Qt::ALT + Qt::Key_F);
@@ -255,7 +263,7 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
if (Core::IsRunning())
{
ext = static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(num))
->CurrentExtension();
->GetActiveExtensionNumber();
}
else
{
@@ -318,22 +326,20 @@ void WiiTASInputWindow::UpdateExt(u8 ext)
}
}

void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rptf, int ext,
wiimote_key key)
void WiiTASInputWindow::GetValues(DataReportBuilder& rpt, int ext,
const WiimoteEmu::EncryptionKey& key)
{
if (!isVisible())
return;

UpdateExt(ext);

u8* const buttons_data = rptf.core ? (report_data + rptf.core) : nullptr;
u8* const accel_data = rptf.accel ? (report_data + rptf.accel) : nullptr;
u8* const ir_data = rptf.ir ? (report_data + rptf.ir) : nullptr;
u8* const ext_data = rptf.ext ? (report_data + rptf.ext) : nullptr;

if (m_remote_buttons_box->isVisible() && buttons_data)
if (m_remote_buttons_box->isVisible() && rpt.HasCore())
{
u16& buttons = (reinterpret_cast<wm_buttons*>(buttons_data))->hex;
DataReportBuilder::CoreData core;
rpt.GetCoreData(&core);

u16& buttons = core.hex;
GetButton<u16>(m_a_button, buttons, WiimoteEmu::Wiimote::BUTTON_A);
GetButton<u16>(m_b_button, buttons, WiimoteEmu::Wiimote::BUTTON_B);
GetButton<u16>(m_1_button, buttons, WiimoteEmu::Wiimote::BUTTON_ONE);
@@ -345,50 +351,48 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
GetButton<u16>(m_up_button, buttons, WiimoteEmu::Wiimote::PAD_UP);
GetButton<u16>(m_down_button, buttons, WiimoteEmu::Wiimote::PAD_DOWN);
GetButton<u16>(m_right_button, buttons, WiimoteEmu::Wiimote::PAD_RIGHT);

rpt.SetCoreData(core);
}

if (m_remote_orientation_box->isVisible() && accel_data && buttons_data)
if (m_remote_orientation_box->isVisible() && rpt.HasAccel())
{
wm_accel& accel = *reinterpret_cast<wm_accel*>(accel_data);
wm_buttons& buttons = *reinterpret_cast<wm_buttons*>(buttons_data);
// FYI: Interleaved reports may behave funky as not all data is always available.

u16 accel_x = (accel.x << 2) & (buttons.acc_x_lsb & 0b11);
u16 accel_y = (accel.y << 2) & ((buttons.acc_y_lsb & 0b1) << 1);
u16 accel_z = (accel.z << 2) & ((buttons.acc_z_lsb & 0b1) << 1);
DataReportBuilder::AccelData accel;
rpt.GetAccelData(&accel);

GetSpinBoxU16(m_remote_orientation_x_value, accel_x);
GetSpinBoxU16(m_remote_orientation_y_value, accel_y);
GetSpinBoxU16(m_remote_orientation_z_value, accel_z);
GetSpinBoxU16(m_remote_orientation_x_value, accel.x);
GetSpinBoxU16(m_remote_orientation_y_value, accel.y);
GetSpinBoxU16(m_remote_orientation_z_value, accel.z);

accel.x = accel_x >> 2;
accel.y = accel_y >> 2;
accel.z = accel_z >> 2;

buttons.acc_x_lsb = accel_x & 0b11;
buttons.acc_y_lsb = (accel_y >> 1) & 0b1;
buttons.acc_z_lsb = (accel_z >> 1) & 0b1;
rpt.SetAccelData(accel);
}

if (m_ir_box->isVisible() && ir_data && !m_use_controller->isChecked())
if (m_ir_box->isVisible() && rpt.HasIR() && !m_use_controller->isChecked())
{
u8* const ir_data = rpt.GetIRDataPtr();

u16 y = m_ir_y_value->value();
std::array<u16, 4> x;
x[0] = m_ir_x_value->value();
x[1] = x[0] + 100;
x[2] = x[0] - 10;
x[3] = x[1] + 10;

u8 mode;
// Mode 5 not supported in core anyway.
if (rptf.ext)
mode = (rptf.ext - rptf.ir) == 10 ? 1 : 3;
else
mode = (rptf.size - rptf.ir) == 10 ? 1 : 3;
// FYI: This check is not entirely foolproof.
// TODO: IR "full" mode not implemented.
u8 mode = WiimoteEmu::CameraLogic::IR_MODE_BASIC;

if (mode == 1)
if (rpt.GetIRDataSize() == sizeof(WiimoteEmu::IRExtended) * 4)
mode = WiimoteEmu::CameraLogic::IR_MODE_EXTENDED;
else if (rpt.GetIRDataSize() == sizeof(WiimoteEmu::IRFull) * 2)
mode = WiimoteEmu::CameraLogic::IR_MODE_FULL;

if (mode == WiimoteEmu::CameraLogic::IR_MODE_BASIC)
{
memset(ir_data, 0xFF, sizeof(wm_ir_basic) * 2);
wm_ir_basic* const ir_basic = reinterpret_cast<wm_ir_basic*>(ir_data);
memset(ir_data, 0xFF, sizeof(WiimoteEmu::IRBasic) * 2);
auto* const ir_basic = reinterpret_cast<WiimoteEmu::IRBasic*>(ir_data);
for (int i = 0; i < 2; ++i)
{
if (x[i * 2] < 1024 && y < 768)
@@ -413,8 +417,8 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
{
// TODO: this code doesnt work, resulting in no IR TAS inputs in e.g. wii sports menu when no
// remote extension is used
memset(ir_data, 0xFF, sizeof(wm_ir_extended) * 4);
wm_ir_extended* const ir_extended = reinterpret_cast<wm_ir_extended*>(ir_data);
memset(ir_data, 0xFF, sizeof(WiimoteEmu::IRExtended) * 4);
auto* const ir_extended = reinterpret_cast<WiimoteEmu::IRExtended*>(ir_data);
for (size_t i = 0; i < x.size(); ++i)
{
if (x[i] < 1024 && y < 768)
@@ -431,9 +435,11 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
}
}

if (ext_data && m_nunchuk_stick_box->isVisible())
if (rpt.HasExt() && m_nunchuk_stick_box->isVisible())
{
wm_nc& nunchuk = *reinterpret_cast<wm_nc*>(ext_data);
u8* const ext_data = rpt.GetExtDataPtr();

auto& nunchuk = *reinterpret_cast<WiimoteEmu::Nunchuk::DataFormat*>(ext_data);

GetSpinBoxU8(m_nunchuk_stick_x_value, nunchuk.jx);
GetSpinBoxU8(m_nunchuk_stick_y_value, nunchuk.jy);
@@ -459,13 +465,15 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
GetButton<u8>(m_z_button, nunchuk.bt.hex, WiimoteEmu::Nunchuk::BUTTON_Z);
nunchuk.bt.hex ^= 0b11;

WiimoteEncrypt(&key, reinterpret_cast<u8*>(&nunchuk), 0, sizeof(wm_nc));
key.Encrypt(reinterpret_cast<u8*>(&nunchuk), 0, sizeof(nunchuk));
}

if (m_classic_left_stick_box->isVisible())
{
wm_classic_extension& cc = *reinterpret_cast<wm_classic_extension*>(ext_data);
WiimoteDecrypt(&key, reinterpret_cast<u8*>(&cc), 0, sizeof(wm_classic_extension));
u8* const ext_data = rpt.GetExtDataPtr();

auto& cc = *reinterpret_cast<WiimoteEmu::Classic::DataFormat*>(ext_data);
key.Decrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));

cc.bt.hex ^= 0xFFFF;
GetButton<u16>(m_classic_a_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_A);
@@ -495,13 +503,13 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
GetSpinBoxU8(m_classic_right_stick_y_value, ry);
cc.ry = ry;

u8 lx = cc.regular_data.lx;
u8 lx = cc.lx;
GetSpinBoxU8(m_classic_left_stick_x_value, lx);
cc.regular_data.lx = lx;
cc.lx = lx;

u8 ly = cc.regular_data.ly;
u8 ly = cc.ly;
GetSpinBoxU8(m_classic_left_stick_y_value, ly);
cc.regular_data.ly = ly;
cc.ly = ly;

u8 rt = cc.rt;
GetSpinBoxU8(m_right_trigger_value, rt);
@@ -512,6 +520,6 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
cc.lt1 = lt & 0b111;
cc.lt2 = (lt >> 3) & 0b11;

WiimoteEncrypt(&key, reinterpret_cast<u8*>(&cc), 0, sizeof(wm_classic_extension));
key.Encrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
}
}
@@ -6,21 +6,27 @@

#include "DolphinQt/TAS/TASInputWindow.h"

namespace WiimoteCommon
{
class DataReportBuilder;
}

namespace WiimoteEmu
{
struct ReportFeatures;
class EncryptionKey;
}

class QCheckBox;
class QGroupBox;
class QSpinBox;
struct wiimote_key;

class WiiTASInputWindow : public TASInputWindow
{
Q_OBJECT
public:
explicit WiiTASInputWindow(QWidget* parent, int num);
void GetValues(u8* input_data, WiimoteEmu::ReportFeatures rptf, int ext, wiimote_key key);
void GetValues(WiimoteCommon::DataReportBuilder& rpt, int ext,
const WiimoteEmu::EncryptionKey& key);

private:
void UpdateExt(u8 ext);
@@ -7,10 +7,10 @@ add_library(inputcommon
ControllerEmu/Control/Input.cpp
ControllerEmu/Control/Output.cpp
ControllerEmu/ControlGroup/AnalogStick.cpp
ControllerEmu/ControlGroup/Attachments.cpp
ControllerEmu/ControlGroup/Buttons.cpp
ControllerEmu/ControlGroup/ControlGroup.cpp
ControllerEmu/ControlGroup/Cursor.cpp
ControllerEmu/ControlGroup/Extension.cpp
ControllerEmu/ControlGroup/Force.cpp
ControllerEmu/ControlGroup/MixedTriggers.cpp
ControllerEmu/ControlGroup/ModifySettingsButton.cpp
@@ -0,0 +1,33 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"

namespace ControllerEmu
{
Attachments::Attachments(const std::string& name_) : ControlGroup(name_, GroupType::Attachments)
{
}

void Attachments::AddAttachment(std::unique_ptr<EmulatedController> att)
{
m_attachments.emplace_back(std::move(att));
}

u32 Attachments::GetSelectedAttachment() const
{
return m_selected_attachment;
}

void Attachments::SetSelectedAttachment(u32 val)
{
m_selected_attachment = val;
}

const std::vector<std::unique_ptr<EmulatedController>>& Attachments::GetAttachmentList() const
{
return m_attachments;
}

} // namespace ControllerEmu
@@ -0,0 +1,37 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <atomic>
#include <memory>
#include <string>
#include <vector>

#include "Common/CommonTypes.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"

namespace ControllerEmu
{
// A container of the selected and available attachments
// for configuration saving/loading purposes
class Attachments : public ControlGroup
{
public:
explicit Attachments(const std::string& name);

void AddAttachment(std::unique_ptr<EmulatedController> att);

u32 GetSelectedAttachment() const;
void SetSelectedAttachment(u32 val);

const std::vector<std::unique_ptr<EmulatedController>>& GetAttachmentList() const;

private:
std::vector<std::unique_ptr<EmulatedController>> m_attachments;

std::atomic<u32> m_selected_attachment = {};
};
} // namespace ControllerEmu
@@ -9,7 +9,7 @@

#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerEmu/Setting/BooleanSetting.h"
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
@@ -67,22 +67,22 @@ void ControlGroup::LoadConfig(IniFile::Section* sec, const std::string& defdev,
}

// extensions
if (type == GroupType::Extension)
if (type == GroupType::Attachments)
{
Extension* const ext = (Extension*)this;
auto* const ext = static_cast<Attachments*>(this);

ext->switch_extension = 0;
ext->SetSelectedAttachment(0);
u32 n = 0;
std::string extname;
sec->Get(base + name, &extname, "");

for (auto& ai : ext->attachments)
for (auto& ai : ext->GetAttachmentList())
{
ai->SetDefaultDevice(defdev);
ai->LoadConfig(sec, base + ai->GetName() + "/");

if (ai->GetName() == extname)
ext->switch_extension = n;
ext->SetSelectedAttachment(n);

n++;
}
@@ -120,12 +120,13 @@ void ControlGroup::SaveConfig(IniFile::Section* sec, const std::string& defdev,
}

// extensions
if (type == GroupType::Extension)
if (type == GroupType::Attachments)
{
Extension* const ext = (Extension*)this;
sec->Set(base + name, ext->attachments[ext->switch_extension]->GetName(), "None");
auto* const ext = static_cast<Attachments*>(this);
sec->Set(base + name, ext->GetAttachmentList()[ext->GetSelectedAttachment()]->GetName(),
"None");

for (auto& ai : ext->attachments)
for (auto& ai : ext->GetAttachmentList())
ai->SaveConfig(sec, base + ai->GetName() + "/");
}
}
@@ -24,7 +24,7 @@ enum class GroupType
MixedTriggers,
Buttons,
Force,
Extension,
Attachments,
Tilt,
Cursor,
Triggers,

This file was deleted.

This file was deleted.

@@ -19,8 +19,5 @@ class Force : public ControlGroup
explicit Force(const std::string& name);

StateData GetState();

private:
StateData m_swing{};
};
} // namespace ControllerEmu
@@ -12,8 +12,8 @@

#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"

namespace ControllerEmu
@@ -41,10 +41,10 @@ void EmulatedController::UpdateReferences(const ControllerInterface& devi)
for (auto& control : ctrlGroup->controls)
control->control_ref.get()->UpdateReference(devi, GetDefaultDevice());

// extension
if (ctrlGroup->type == GroupType::Extension)
// Attachments:
if (ctrlGroup->type == GroupType::Attachments)
{
for (auto& attachment : ((Extension*)ctrlGroup.get())->attachments)
for (auto& attachment : static_cast<Attachments*>(ctrlGroup.get())->GetAttachmentList())
attachment->UpdateReferences(devi);
}
}
@@ -73,10 +73,10 @@ void EmulatedController::SetDefaultDevice(ciface::Core::DeviceQualifier devq)

for (auto& ctrlGroup : groups)
{
// extension
if (ctrlGroup->type == GroupType::Extension)
// Attachments:
if (ctrlGroup->type == GroupType::Attachments)
{
for (auto& ai : ((Extension*)ctrlGroup.get())->attachments)
for (auto& ai : static_cast<Attachments*>(ctrlGroup.get())->GetAttachmentList())
{
ai->SetDefaultDevice(m_default_device);
}
@@ -45,7 +45,7 @@
<ClCompile Include="ControllerEmu\ControlGroup\Buttons.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\ControlGroup.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\Cursor.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\Extension.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\Attachments.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\Force.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\MixedTriggers.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\ModifySettingsButton.cpp" />
@@ -84,7 +84,7 @@
<ClInclude Include="ControllerEmu\ControlGroup\Buttons.h" />
<ClInclude Include="ControllerEmu\ControlGroup\ControlGroup.h" />
<ClInclude Include="ControllerEmu\ControlGroup\Cursor.h" />
<ClInclude Include="ControllerEmu\ControlGroup\Extension.h" />
<ClInclude Include="ControllerEmu\ControlGroup\Attachments.h" />
<ClInclude Include="ControllerEmu\ControlGroup\Force.h" />
<ClInclude Include="ControllerEmu\ControlGroup\MixedTriggers.h" />
<ClInclude Include="ControllerEmu\ControlGroup\ModifySettingsButton.h" />
@@ -121,4 +121,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
@@ -56,9 +56,6 @@
<ClCompile Include="ControllerEmu\ControlGroup\Cursor.cpp">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClCompile>
<ClCompile Include="ControllerEmu\ControlGroup\Extension.cpp">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClCompile>
<ClCompile Include="ControllerEmu\ControlGroup\Force.cpp">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClCompile>
@@ -114,6 +111,9 @@
<Filter>ControllerInterface</Filter>
</ClCompile>
<ClCompile Include="InputProfile.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\Attachments.cpp">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="GCAdapter.h" />
@@ -146,9 +146,6 @@
<ClInclude Include="ControllerEmu\ControlGroup\Cursor.h">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClInclude>
<ClInclude Include="ControllerEmu\ControlGroup\Extension.h">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClInclude>
<ClInclude Include="ControllerEmu\ControlGroup\Force.h">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClInclude>
@@ -210,8 +207,11 @@
<Filter>ControllerInterface</Filter>
</ClInclude>
<ClInclude Include="InputProfile.h" />
<ClInclude Include="ControllerEmu\ControlGroup\Attachments.h">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
</Project>
</Project>