From 6c40c4747227338d597d3b5723b59d9b48ed29e7 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Thu, 22 Nov 2018 12:14:33 -0600 Subject: [PATCH 01/22] WiimoteEmu: Remove some remnants of hybrid wiimote. --- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 40 ++----------------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 24 +---------- 2 files changed, 5 insertions(+), 59 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 06ccffb0d4..8f385f1916 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -112,8 +112,7 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) break; case RT_REQUEST_STATUS: // 0x15 - if (WIIMOTE_SRC_EMU & g_wiimote_sources[m_index]) - RequestStatus(reinterpret_cast(sr->data)); + RequestStatus(reinterpret_cast(sr->data)); return; // sends its own ack break; @@ -122,13 +121,12 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) break; case RT_READ_DATA: // 0x17 - if (WIIMOTE_SRC_EMU & g_wiimote_sources[m_index]) - ReadData(reinterpret_cast(sr->data)); + ReadData(reinterpret_cast(sr->data)); return; // sends its own ack break; case RT_WRITE_SPEAKER_DATA: // 0x18 - if (WIIMOTE_SRC_EMU & g_wiimote_sources[m_index] && !m_speaker_mute) + if (!m_speaker_mute) Wiimote::SpeakerData(reinterpret_cast(sr->data)); return; // no ack break; @@ -154,9 +152,7 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) break; } - // send ack - if (send_ack && WIIMOTE_SRC_EMU & g_wiimote_sources[m_index]) - SendAck(sr->wm); + SendAck(sr->wm); } /* This will generate the 0x22 acknowledgement for most Input reports. @@ -215,22 +211,6 @@ void Wiimote::RequestStatus(const wm_request_status* const rs) // status values *reinterpret_cast(data + 2) = m_status; - // hybrid Wiimote stuff - if (WIIMOTE_SRC_REAL & g_wiimote_sources[m_index] && (m_extension->switch_extension <= 0)) - { - using namespace WiimoteReal; - - std::lock_guard lk(g_wiimotes_mutex); - - if (g_wiimotes[m_index]) - { - wm_request_status rpt = {}; - g_wiimotes[m_index]->QueueReport(RT_REQUEST_STATUS, &rpt, sizeof(rpt)); - } - - return; - } - // send report Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data)); } @@ -359,18 +339,6 @@ void Wiimote::ReadData(const wm_read_data* const rd) // ignore the 0x010000 bit address &= 0xFEFFFF; - // hybrid Wiimote stuff - // relay the read data request to real-Wiimote - if (WIIMOTE_SRC_REAL & g_wiimote_sources[m_index] && - ((0xA4 != (address >> 16)) || (m_extension->switch_extension <= 0))) - { - WiimoteReal::InterruptChannel(m_index, m_reporting_channel, ((u8*)rd) - 2, - sizeof(wm_read_data) + 2); // hacky - - // don't want emu-Wiimote to send reply - return; - } - ReadRequest rr; u8* const block = new u8[size]; diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index e6c5fdcc2f..de8106797b 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -923,8 +923,6 @@ void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size) // Wii Remote disconnected // reset eeprom/register/reporting mode Reset(); - if (WIIMOTE_SRC_REAL & g_wiimote_sources[m_index]) - WiimoteReal::ControlChannel(m_index, channel_id, data, size); return; } @@ -983,27 +981,7 @@ void Wiimote::InterruptChannel(const u16 channel_id, const void* data, u32 size) case HID_PARAM_OUTPUT: { const wm_report* sr = reinterpret_cast(hidp->data); - - if (WIIMOTE_SRC_REAL & g_wiimote_sources[m_index]) - { - switch (sr->wm) - { - // these two types are handled in RequestStatus() & ReadData() - case RT_REQUEST_STATUS: - case RT_READ_DATA: - if (WIIMOTE_SRC_REAL == g_wiimote_sources[m_index]) - WiimoteReal::InterruptChannel(m_index, channel_id, data, size); - break; - - default: - WiimoteReal::InterruptChannel(m_index, channel_id, data, size); - break; - } - - HidOutputReport(sr, m_extension->switch_extension > 0); - } - else - HidOutputReport(sr); + HidOutputReport(sr); } break; From ebc2e58fa480a51c3a44762e9143aa84e230db7c Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Thu, 22 Nov 2018 15:08:49 -0600 Subject: [PATCH 02/22] WiimoteEmu: Partially emulate i2c bus to more closely simulate the real thing. Transfer most of IR camera logic to the i2c bus. Temporarily break everything else. --- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 116 ++++------------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 70 +++++++---- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 118 ++++++++++++++++-- 3 files changed, 184 insertions(+), 120 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 8f385f1916..eb532fb064 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -261,66 +261,31 @@ void Wiimote::WriteData(const wm_write_data* const wd) { // Write to Control Register - // ignore second byte for extension area - if (0xA4 == (address >> 16)) - address &= 0xFF00FF; + // TODO: generate a writedata error reply for wrong number of bytes written.. + m_i2c_bus.BusWrite(address >> 17, address & 0xff, wd->size, wd->data); - const u8 region_offset = (u8)address; - void* region_ptr = nullptr; - int region_size = 0; + return; - switch (address >> 16) - { - // speaker - case 0xa2: - region_ptr = &m_reg_speaker; - region_size = WIIMOTE_REG_SPEAKER_SIZE; - break; + // TODO: extension register stuff.. - // extension register - case 0xa4: - region_ptr = (void*)&m_reg_ext; - region_size = WIIMOTE_REG_EXT_SIZE; - break; + //if (false)//&m_reg_ext == region_ptr) + //{ + // // Run the key generation on all writes in the key area, it doesn't matter + // // that we send it parts of a key, only the last full key will have an effect + // if (address >= 0xa40040 && address <= 0xa4004c) + // WiimoteGenerateKey(&m_ext_key, m_reg_ext.encryption_key); + //} + //else if (&m_reg_motion_plus == region_ptr) + //{ + // // activate/deactivate motion plus + // if (0x55 == m_reg_motion_plus.activated) + // { + // // maybe hacky + // m_reg_motion_plus.activated = 0; - // motion plus - case 0xa6: - region_ptr = &m_reg_motion_plus; - region_size = WIIMOTE_REG_EXT_SIZE; - break; - - // ir - case 0xB0: - region_ptr = &m_reg_ir; - region_size = WIIMOTE_REG_IR_SIZE; - break; - } - - if (region_ptr && (region_offset + wd->size <= region_size)) - { - memcpy((u8*)region_ptr + region_offset, wd->data, wd->size); - } - else - return; // TODO: generate a writedata error reply - - if (&m_reg_ext == region_ptr) - { - // Run the key generation on all writes in the key area, it doesn't matter - // that we send it parts of a key, only the last full key will have an effect - if (address >= 0xa40040 && address <= 0xa4004c) - WiimoteGenerateKey(&m_ext_key, m_reg_ext.encryption_key); - } - else if (&m_reg_motion_plus == region_ptr) - { - // activate/deactivate motion plus - if (0x55 == m_reg_motion_plus.activated) - { - // maybe hacky - m_reg_motion_plus.activated = 0; - - RequestStatus(); - } - } + // RequestStatus(); + // } + //} } break; @@ -386,42 +351,11 @@ void Wiimote::ReadData(const wm_read_data* const rd) const u8 region_offset = (u8)address; void* region_ptr = nullptr; - int region_size = 0; + //int region_size = 0; - switch (address >> 16) - { - // speaker - case 0xa2: - region_ptr = &m_reg_speaker; - region_size = WIIMOTE_REG_SPEAKER_SIZE; - break; + m_i2c_bus.BusRead(address >> 17, address & 0xff, rd->size, block); - // extension - case 0xa4: - region_ptr = (void*)&m_reg_ext; - region_size = WIIMOTE_REG_EXT_SIZE; - break; - - // motion plus - case 0xa6: - // reading from 0xa6 returns error when mplus is activated - region_ptr = &m_reg_motion_plus; - region_size = WIIMOTE_REG_EXT_SIZE; - break; - - // ir - case 0xb0: - region_ptr = &m_reg_ir; - region_size = WIIMOTE_REG_IR_SIZE; - break; - } - - if (region_ptr && (region_offset + size <= region_size)) - { - memcpy(block, (u8*)region_ptr + region_offset, size); - } - else - size = 0; // generate read error + // TODO: generate read errors if (&m_reg_ext == region_ptr) { @@ -527,7 +461,7 @@ void Wiimote::DoState(PointerWrap& p) p.Do(m_ext_key); p.DoArray(m_eeprom); p.Do(m_reg_motion_plus); - p.Do(m_reg_ir); + p.Do(m_camera_logic.reg_data); p.Do(m_reg_ext); p.Do(m_reg_speaker); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index de8106797b..bddd03de3c 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -82,27 +82,28 @@ static const u8 eeprom_data_16D0[] = {0x00, 0x00, 0x00, 0xFF, 0x11, 0xEE, 0x00, 0x33, 0xCC, 0x44, 0xBB, 0x00, 0x00, 0x66, 0x99, 0x77, 0x88, 0x00, 0x00, 0x2B, 0x01, 0xE8, 0x13}; +// Counts are how many bytes of each feature are in a particular report static const ReportFeatures reporting_mode_features[] = { // 0x30: Core Buttons {2, 0, 0, 0, 4}, // 0x31: Core Buttons and Accelerometer - {2, 4, 0, 0, 7}, + {2, 3, 0, 0, 7}, // 0x32: Core Buttons with 8 Extension bytes - {2, 0, 0, 4, 12}, + {2, 0, 0, 8, 12}, // 0x33: Core Buttons and Accelerometer with 12 IR bytes - {2, 4, 7, 0, 19}, + {2, 3, 12, 0, 19}, // 0x34: Core Buttons with 19 Extension bytes - {2, 0, 0, 4, 23}, + {2, 0, 0, 19, 23}, // 0x35: Core Buttons and Accelerometer with 16 Extension Bytes - {2, 4, 0, 7, 23}, + {2, 3, 0, 16, 23}, // 0x36: Core Buttons with 10 IR bytes and 9 Extension Bytes - {2, 0, 4, 14, 23}, + {2, 0, 10, 9, 23}, // 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes - {2, 4, 7, 17, 23}, + {2, 3, 10, 6, 23}, + // 0x3d: 21 Extension Bytes + {0, 0, 0, 21, 23}, // UNSUPPORTED: - // 0x3d: 21 Extension Bytes - {0, 0, 0, 2, 23}, // 0x3e / 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes {0, 0, 0, 0, 23}, }; @@ -328,7 +329,8 @@ void Wiimote::Reset() // set up the register memset(&m_reg_speaker, 0, sizeof(m_reg_speaker)); - memset(&m_reg_ir, 0, sizeof(m_reg_ir)); + // TODO: kill/move this + memset(&m_camera_logic.reg_data, 0, sizeof(m_camera_logic.reg_data)); memset(&m_reg_ext, 0, sizeof(m_reg_ext)); memset(&m_reg_motion_plus, 0, sizeof(m_reg_motion_plus)); @@ -359,6 +361,9 @@ void Wiimote::Reset() // Yamaha ADPCM state initialize m_adpcm_state.predictor = 0; m_adpcm_state.step = 127; + + // Initialize i2c bus + m_i2c_bus.AddSlave(0x58, &m_camera_logic); } Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1) @@ -602,7 +607,7 @@ void Wiimote::GetButtonData(u8* const data) reinterpret_cast(data)->hex |= m_status.buttons.hex; } -void Wiimote::GetAccelData(u8* const data, const ReportFeatures& rptf) +void Wiimote::GetAccelData(u8* const data) { const bool sideways_modifier_toggle = m_hotkeys->getSettingsModifier()[0]; const bool upright_modifier_toggle = m_hotkeys->getSettingsModifier()[1]; @@ -653,8 +658,9 @@ void Wiimote::GetAccelData(u8* const data, const ReportFeatures& rptf) EmulateDynamicShake(&m_accel, m_shake_dynamic_data, m_shake_dynamic, shake_config, m_shake_dynamic_step.data()); - wm_accel& accel = *reinterpret_cast(data + rptf.accel); - wm_buttons& core = *reinterpret_cast(data + rptf.core); + // TODO: kill these ugly looking offsets + wm_accel& accel = *reinterpret_cast(data + 4); + wm_buttons& core = *reinterpret_cast(data + 2); // We now use 2 bits more precision, so multiply by 4 before converting to int s16 x = (s16)(4 * (m_accel.x * ACCEL_RANGE + ACCEL_ZERO_G)); @@ -683,8 +689,11 @@ inline void LowPassFilter(double& var, double newval, double period) var = newval * alpha + var * (1.0 - alpha); } -void Wiimote::GetIRData(u8* const data, bool use_accel) +void Wiimote::UpdateIRData(bool use_accel) { + // IR data is stored at offset 0x37 + u8* const data = m_camera_logic.reg_data.camera_data; + u16 x[4], y[4]; memset(x, 0xFF, sizeof(x)); @@ -770,9 +779,9 @@ void Wiimote::GetIRData(u8* const data, bool use_accel) } // Fill report with valid data when full handshake was done - if (m_reg_ir.data[0x30]) + if (m_camera_logic.reg_data.data[0x30]) // ir mode - switch (m_reg_ir.mode) + switch (m_camera_logic.reg_data.mode) { // basic case 1: @@ -877,21 +886,40 @@ void Wiimote::Update() // hotkey/settings modifier m_hotkeys->GetState(); // data is later accessed in UpdateButtonsStatus and GetAccelData + // Data starts at byte 2 in the report + u8* feature_ptr = data + 2; + // core buttons if (rptf.core) - GetButtonData(data + rptf.core); + { + GetButtonData(feature_ptr); + feature_ptr += rptf.core; + } // acceleration if (rptf.accel) - GetAccelData(data, rptf); + { + // TODO: GetAccelData has hardcoded payload offsets.. + GetAccelData(data); + feature_ptr += rptf.accel; + } - // IR + // IR Camera + // TODO: kill use_accel param + // TODO: call only if camera logic is enabled? + UpdateIRData(rptf.accel != 0); if (rptf.ir) - GetIRData(data + rptf.ir, (rptf.accel != 0)); + { + m_i2c_bus.BusRead(0x58, 0x37, rptf.ir, feature_ptr); + feature_ptr += rptf.ir; + } // extension if (rptf.ext) - GetExtData(data + rptf.ext); + { + // GetExtData(feature_ptr, rptf.ext); + feature_ptr += rptf.ext; + } Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, m_ext_key); } diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 6b50dc1296..8bf75d2712 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -12,6 +12,7 @@ #include "Core/HW/WiimoteCommon/WiimoteReport.h" #include "Core/HW/WiimoteEmu/Encryption.h" #include "InputCommon/ControllerEmu/ControllerEmu.h" +#include "Common/Logging/Log.h" // Registry sizes #define WIIMOTE_EEPROM_SIZE (16 * 1024) @@ -207,6 +208,84 @@ enum ACCEL_RANGE = (ACCEL_ONE_G - ACCEL_ZERO_G), }; +class I2CSlave +{ +public: + virtual int BusRead(u8 addr, int count, u8* data_out) = 0; + virtual int BusWrite(u8 addr, int count, const u8* data_in) = 0; + + template + static int raw_read(T* reg_data, u8 addr, int count, u8* data_out) + { + static_assert(std::is_pod::value); + + u8* src = reinterpret_cast(reg_data) + addr; + count = std::min(count, int(reinterpret_cast(reg_data + 1) - src)); + + std::copy_n(src, count, data_out); + + return count; + } + + template + static int raw_write(T* reg_data, u8 addr, int count, const u8* data_in) + { + static_assert(std::is_pod::value); + + u8* dst = reinterpret_cast(reg_data) + addr; + count = std::min(count, int(reinterpret_cast(reg_data + 1) - dst)); + + std::copy_n(data_in, count, dst); + + return count; + } +}; + +class I2CBus +{ +public: + void AddSlave(u8 addr, I2CSlave* slave) + { + m_slaves.insert(std::make_pair(addr, slave)); + } + + void RemoveSlave(u8 addr) + { + m_slaves.erase(addr); + } + + void Reset() + { + m_slaves.clear(); + } + + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) + { + INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); + + auto it = m_slaves.find(slave_addr); + if (m_slaves.end() != it) + return it->second->BusRead(addr, count, data_out); + else + return 0; + } + + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) + { + INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); + + auto it = m_slaves.find(slave_addr); + if (m_slaves.end() != it) + return it->second->BusWrite(addr, count, data_in); + else + return 0; + } + +private: + // Organized by slave addr + std::map m_slaves; +}; + class Wiimote : public ControllerEmu::EmulatedController { friend class WiimoteReal::Wiimote; @@ -257,14 +336,41 @@ protected: void UpdateButtonsStatus(); void GetButtonData(u8* data); - void GetAccelData(u8* data, const ReportFeatures& rptf); - void GetIRData(u8* data, bool use_accel); + void GetAccelData(u8* data); + void UpdateIRData(bool use_accel); void GetExtData(u8* data); bool HaveExtension() const; bool WantExtension() const; private: + struct IRCameraLogic : public I2CSlave + { + struct + { + // Contains sensitivity and other unknown data + u8 data[0x33]; + u8 mode; + u8 unk[3]; + // addr 0x37 + u8 camera_data[36]; + u8 unk2[165]; + } reg_data; + + static_assert(0x100 == sizeof(reg_data)); + + int BusRead(u8 addr, int count, u8* data_out) override + { + return raw_read(®_data, addr, count, data_out); + } + + int BusWrite(u8 addr, int count, const u8* data_in) override + { + return raw_write(®_data, addr, count, data_in); + } + + } m_camera_logic; + struct ReadRequest { // u16 channel; @@ -306,6 +412,8 @@ private: DynamicData m_swing_dynamic_data; DynamicData m_shake_dynamic_data; + I2CBus m_i2c_bus; + // Wiimote accel data AccelData m_accel; @@ -354,12 +462,6 @@ private: u8 ext_identifier[6]; } m_reg_motion_plus; - struct IrReg - { - u8 data[0x33]; - u8 mode; - } m_reg_ir; - ExtensionReg m_reg_ext; struct SpeakerReg From 62b66580c3c3031a189607e5b1d57fdbd02659be Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Thu, 22 Nov 2018 19:12:42 -0600 Subject: [PATCH 03/22] WiimoteEmu: Unbreak wiimote extensions. --- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 29 +-------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 46 ++++++------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 65 ++++++++++++------- 3 files changed, 69 insertions(+), 71 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index eb532fb064..bec9920b00 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -266,15 +266,7 @@ void Wiimote::WriteData(const wm_write_data* const wd) return; - // TODO: extension register stuff.. - //if (false)//&m_reg_ext == region_ptr) - //{ - // // Run the key generation on all writes in the key area, it doesn't matter - // // that we send it parts of a key, only the last full key will have an effect - // if (address >= 0xa40040 && address <= 0xa4004c) - // WiimoteGenerateKey(&m_ext_key, m_reg_ext.encryption_key); - //} //else if (&m_reg_motion_plus == region_ptr) //{ // // activate/deactivate motion plus @@ -345,25 +337,8 @@ void Wiimote::ReadData(const wm_read_data* const rd) { // Read from Control Register - // ignore second byte for extension area - if (0xA4 == (address >> 16)) - address &= 0xFF00FF; - - const u8 region_offset = (u8)address; - void* region_ptr = nullptr; - //int region_size = 0; - m_i2c_bus.BusRead(address >> 17, address & 0xff, rd->size, block); - // TODO: generate read errors - - if (&m_reg_ext == region_ptr) - { - // Encrypt data read from extension register - // Check if encrypted reads is on - if (0xaa == m_reg_ext.encryption) - WiimoteEncrypt(&m_ext_key, block, address & 0xffff, (u8)size); - } } break; @@ -458,11 +433,11 @@ void Wiimote::DoState(PointerWrap& p) p.Do(m_sensor_bar_on_top); p.Do(m_status); p.Do(m_adpcm_state); - p.Do(m_ext_key); + p.Do(m_ext_logic.ext_key); p.DoArray(m_eeprom); p.Do(m_reg_motion_plus); p.Do(m_camera_logic.reg_data); - p.Do(m_reg_ext); + p.Do(m_ext_logic.reg_data); p.Do(m_reg_speaker); // Do 'm_read_requests' queue diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index bddd03de3c..93c985b300 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -329,9 +329,11 @@ void Wiimote::Reset() // set up the register memset(&m_reg_speaker, 0, sizeof(m_reg_speaker)); - // TODO: kill/move this + + // TODO: kill/move these memset(&m_camera_logic.reg_data, 0, sizeof(m_camera_logic.reg_data)); - memset(&m_reg_ext, 0, sizeof(m_reg_ext)); + memset(&m_ext_logic.reg_data, 0, sizeof(m_ext_logic.reg_data)); + memset(&m_reg_motion_plus, 0, sizeof(m_reg_motion_plus)); memcpy(&m_reg_motion_plus.ext_identifier, motion_plus_id, sizeof(motion_plus_id)); @@ -363,7 +365,10 @@ void Wiimote::Reset() m_adpcm_state.step = 127; // Initialize i2c bus + // TODO: kill magic numbers + m_i2c_bus.Reset(); m_i2c_bus.AddSlave(0x58, &m_camera_logic); + m_i2c_bus.AddSlave(0x52, &m_ext_logic); } Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1) @@ -421,12 +426,12 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1 // extension groups.emplace_back(m_extension = new ControllerEmu::Extension(_trans("Extension"))); - m_extension->attachments.emplace_back(new WiimoteEmu::None(m_reg_ext)); - m_extension->attachments.emplace_back(new WiimoteEmu::Nunchuk(m_reg_ext)); - m_extension->attachments.emplace_back(new WiimoteEmu::Classic(m_reg_ext)); - m_extension->attachments.emplace_back(new WiimoteEmu::Guitar(m_reg_ext)); - m_extension->attachments.emplace_back(new WiimoteEmu::Drums(m_reg_ext)); - m_extension->attachments.emplace_back(new WiimoteEmu::Turntable(m_reg_ext)); + m_extension->attachments.emplace_back(new WiimoteEmu::None(m_ext_logic.reg_data)); + m_extension->attachments.emplace_back(new WiimoteEmu::Nunchuk(m_ext_logic.reg_data)); + m_extension->attachments.emplace_back(new WiimoteEmu::Classic(m_ext_logic.reg_data)); + m_extension->attachments.emplace_back(new WiimoteEmu::Guitar(m_ext_logic.reg_data)); + m_extension->attachments.emplace_back(new WiimoteEmu::Drums(m_ext_logic.reg_data)); + m_extension->attachments.emplace_back(new WiimoteEmu::Turntable(m_ext_logic.reg_data)); // rumble groups.emplace_back(m_rumble = new ControllerEmu::ControlGroup(_trans("Rumble"))); @@ -835,17 +840,10 @@ void Wiimote::UpdateIRData(bool use_accel) } } -void Wiimote::GetExtData(u8* const data) +void Wiimote::UpdateExtData() { - m_extension->GetState(data); - - // i dont think anything accesses the extension data like this, but ill support it. Indeed, - // commercial games don't do this. - // i think it should be unencrpyted in the register, encrypted when read. - memcpy(m_reg_ext.controller_data, data, sizeof(wm_nc)); // TODO: Should it be nc specific? - - if (0xAA == m_reg_ext.encryption) - WiimoteEncrypt(&m_ext_key, data, 0x00, sizeof(wm_nc)); + // Write extension data to addr 0x00 of extension register + m_extension->GetState(m_ext_logic.reg_data.controller_data); } void Wiimote::Update() @@ -871,7 +869,7 @@ void Wiimote::Update() const ReportFeatures& rptf = reporting_mode_features[m_reporting_mode - RT_REPORT_CORE]; s8 rptf_size = rptf.size; if (Movie::IsPlayingInput() && - Movie::PlayWiimote(m_index, data, rptf, m_extension->active_extension, m_ext_key)) + Movie::PlayWiimote(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key)) { if (rptf.core) m_status.buttons = *reinterpret_cast(data + rptf.core); @@ -910,18 +908,21 @@ void Wiimote::Update() UpdateIRData(rptf.accel != 0); if (rptf.ir) { + // TODO: kill magic numbers m_i2c_bus.BusRead(0x58, 0x37, rptf.ir, feature_ptr); feature_ptr += rptf.ir; } // extension + UpdateExtData(); if (rptf.ext) { - // GetExtData(feature_ptr, rptf.ext); + // TODO: kill magic numbers + m_i2c_bus.BusRead(0x52, 0x00, rptf.ext, feature_ptr); feature_ptr += rptf.ext; } - Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, m_ext_key); + Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, m_ext_logic.ext_key); } if (NetPlay::IsNetPlayRunning()) { @@ -930,7 +931,8 @@ void Wiimote::Update() m_status.buttons = *reinterpret_cast(data + rptf.core); } - Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension, m_ext_key); + // TODO: need to fix usage of rptf probably + Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key); // don't send a data report if auto reporting is off if (false == m_reporting_auto && data[1] >= RT_REPORT_CORE) diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 8bf75d2712..622b6857aa 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -8,11 +8,11 @@ #include #include +#include "Common/Logging/Log.h" #include "Core/HW/WiimoteCommon/WiimoteHid.h" #include "Core/HW/WiimoteCommon/WiimoteReport.h" #include "Core/HW/WiimoteEmu/Encryption.h" #include "InputCommon/ControllerEmu/ControllerEmu.h" -#include "Common/Logging/Log.h" // Registry sizes #define WIIMOTE_EEPROM_SIZE (16 * 1024) @@ -159,11 +159,10 @@ struct ADPCMState struct ExtensionReg { - u8 unknown1[0x08]; + // 16 bytes of possible extension data + u8 controller_data[0x10]; - // address 0x08 - u8 controller_data[0x06]; - u8 unknown2[0x12]; + u8 unknown2[0x10]; // address 0x20 u8 calibration[0x10]; @@ -182,6 +181,8 @@ struct ExtensionReg }; #pragma pack(pop) +static_assert(0x100 == sizeof(ExtensionReg)); + void UpdateCalibrationDataChecksum(std::array& data); void EmulateShake(AccelData* accel, ControllerEmu::Buttons* buttons_group, double intensity, @@ -244,20 +245,11 @@ public: class I2CBus { public: - void AddSlave(u8 addr, I2CSlave* slave) - { - m_slaves.insert(std::make_pair(addr, slave)); - } + void AddSlave(u8 addr, I2CSlave* slave) { m_slaves.insert(std::make_pair(addr, slave)); } - void RemoveSlave(u8 addr) - { - m_slaves.erase(addr); - } + void RemoveSlave(u8 addr) { m_slaves.erase(addr); } - void Reset() - { - m_slaves.clear(); - } + void Reset() { m_slaves.clear(); } int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) { @@ -338,7 +330,7 @@ protected: void GetButtonData(u8* data); void GetAccelData(u8* data); void UpdateIRData(bool use_accel); - void GetExtData(u8* data); + void UpdateExtData(); bool HaveExtension() const; bool WantExtension() const; @@ -371,6 +363,39 @@ private: } m_camera_logic; + struct ExtensionLogic : public I2CSlave + { + ExtensionReg reg_data; + wiimote_key ext_key; + + int BusRead(u8 addr, int count, u8* data_out) override + { + auto const result = raw_read(®_data, addr, count, data_out); + + // Encrypt data read from extension register + // Check if encrypted reads is on + if (0xaa == reg_data.encryption) + WiimoteEncrypt(&ext_key, data_out, addr, (u8)count); + + return result; + } + + int BusWrite(u8 addr, int count, const u8* data_in) override + { + auto const result = raw_write(®_data, addr, count, data_in); + + if (addr + count > 0x40 && addr < 0x50) + { + // Run the key generation on all writes in the key area, it doesn't matter + // that we send it parts of a key, only the last full key will have an effect + WiimoteGenerateKey(&ext_key, reg_data.encryption_key); + } + + return result; + } + + } m_ext_logic; + struct ReadRequest { // u16 channel; @@ -445,8 +470,6 @@ private: // maybe read requests cancel any current requests std::queue m_read_requests; - wiimote_key m_ext_key; - #pragma pack(push, 1) u8 m_eeprom[WIIMOTE_EEPROM_SIZE]; struct MotionPlusReg @@ -462,8 +485,6 @@ private: u8 ext_identifier[6]; } m_reg_motion_plus; - ExtensionReg m_reg_ext; - struct SpeakerReg { u8 unused_0; From ec460da36d212b6d61fb9e5886b42130b630dc52 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Fri, 23 Nov 2018 17:00:41 -0600 Subject: [PATCH 04/22] WiimoteEmu: Add wiimote speaker logic to i2c bus. Temporarily break the "pan" setting. --- .../Core/HW/WiimoteCommon/WiimoteReport.h | 12 +++- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 47 ++++++++------ Source/Core/Core/HW/WiimoteEmu/Speaker.cpp | 40 ++++++------ Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 7 +- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 65 +++++++++++++------ 5 files changed, 106 insertions(+), 65 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h index 3a7da9188a..38b00e89c1 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h @@ -405,7 +405,10 @@ struct wm_write_data u8 rumble : 1; u8 space : 2; // see WM_SPACE_* u8 : 5; - u8 address[3]; + // used only for register space (i2c bus) + u8 slave_address; + // big endian: + u8 address[2]; u8 size; u8 data[16]; }; @@ -424,8 +427,11 @@ struct wm_read_data u8 rumble : 1; u8 space : 2; // see WM_SPACE_* u8 : 5; - u8 address[3]; - u16 size; + // used only for register space (i2c bus) + u8 slave_address; + // big endian: + u8 address[2]; + u8 size[2]; }; static_assert(sizeof(wm_read_data) == 6, "Wrong size"); diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index bec9920b00..2dd95780b1 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -44,8 +44,9 @@ void Wiimote::ReportMode(const wm_report_mode* const dr) // DEBUG_LOG(WIIMOTE, " All The Time: %x", dr->all_the_time); // DEBUG_LOG(WIIMOTE, " Mode: 0x%02x", dr->mode); + // TODO: what does the 'all_the_time' bit really do? // m_reporting_auto = dr->all_the_time; - m_reporting_auto = dr->continuous; // this right? + m_reporting_auto = dr->continuous; m_reporting_mode = dr->mode; // m_reporting_channel = _channelID; // this is set in every Interrupt/Control Channel now @@ -126,8 +127,14 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) break; case RT_WRITE_SPEAKER_DATA: // 0x18 + // Not sure if speaker mute stops the bus write on real hardware, but it's not that important if (!m_speaker_mute) - Wiimote::SpeakerData(reinterpret_cast(sr->data)); + { + auto sd = reinterpret_cast(sr->data); + if (sd->length > 20) + PanicAlert("EmuWiimote: bad speaker data length!"); + m_i2c_bus.BusWrite(0x51, 0x00, sd->length, sd->data); + } return; // no ack break; @@ -218,10 +225,9 @@ void Wiimote::RequestStatus(const wm_request_status* const rs) /* Write data to Wiimote and Extensions registers. */ void Wiimote::WriteData(const wm_write_data* const wd) { - u32 address = Common::swap24(wd->address); + u16 address = Common::swap16(wd->address); - // ignore the 0x010000 bit - address &= ~0x010000; + INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x (%d)", wd->space, address, wd->size); if (wd->size > 16) { @@ -261,8 +267,8 @@ void Wiimote::WriteData(const wm_write_data* const wd) { // Write to Control Register - // TODO: generate a writedata error reply for wrong number of bytes written.. - m_i2c_bus.BusWrite(address >> 17, address & 0xff, wd->size, wd->data); + // TODO: generate a writedata error reply, 7 == no such slave (no ack) + m_i2c_bus.BusWrite(wd->slave_address >> 1, address & 0xff, wd->size, wd->data); return; @@ -290,11 +296,10 @@ void Wiimote::WriteData(const wm_write_data* const wd) /* Read data from Wiimote and Extensions registers. */ void Wiimote::ReadData(const wm_read_data* const rd) { - u32 address = Common::swap24(rd->address); + u16 address = Common::swap16(rd->address); u16 size = Common::swap16(rd->size); - // ignore the 0x010000 bit - address &= 0xFEFFFF; + //INFO_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", rd->space, rd->slave_address, address, size); ReadRequest rr; u8* const block = new u8[size]; @@ -312,7 +317,8 @@ void Wiimote::ReadData(const wm_read_data* const rd) delete[] block; return; } - // generate a read error + + // generate a read error, even if the start of the block is readable a real wiimote just sends error code 8 size = 0; } @@ -337,23 +343,24 @@ void Wiimote::ReadData(const wm_read_data* const rd) { // Read from Control Register - m_i2c_bus.BusRead(address >> 17, address & 0xff, rd->size, block); - // TODO: generate read errors + m_i2c_bus.BusRead(rd->slave_address >> 1, address & 0xff, size, block); + // TODO: generate read errors, 7 == no such slave (no ack) } break; default: - PanicAlert("WmReadData: unimplemented parameters (size: %i, address: 0x%x)!", size, rd->space); + PanicAlert("Wiimote::ReadData: unimplemented address space (space: 0x%x)!", rd->space); break; } - // want the requested address, not the above modified one - rr.address = Common::swap24(rd->address); + rr.address = address; rr.size = size; - // rr.channel = _channelID; rr.position = 0; rr.data = block; + // TODO: read requests suppress normal input reports + // TODO: if there is currently an active read request ignore new ones + // send up to 16 bytes SendReadDataReply(rr); @@ -391,7 +398,7 @@ void Wiimote::SendReadDataReply(ReadRequest& request) { // Limit the amt to 16 bytes // AyuanX: the MTU is 640B though... what a waste! - const int amt = std::min((unsigned int)16, request.size); + const int amt = std::min((u16)16, request.size); // no error reply->error = 0; @@ -432,13 +439,13 @@ void Wiimote::DoState(PointerWrap& p) p.Do(m_shake_step); p.Do(m_sensor_bar_on_top); p.Do(m_status); - p.Do(m_adpcm_state); + p.Do(m_speaker_logic.adpcm_state); p.Do(m_ext_logic.ext_key); p.DoArray(m_eeprom); p.Do(m_reg_motion_plus); p.Do(m_camera_logic.reg_data); p.Do(m_ext_logic.reg_data); - p.Do(m_reg_speaker); + p.Do(m_speaker_logic.reg_data); // Do 'm_read_requests' queue { diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp index fb66bfb093..257f82741c 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp @@ -68,39 +68,40 @@ void stopdamnwav() } #endif -void Wiimote::SpeakerData(const wm_speaker_data* sd) +void Wiimote::SpeakerLogic::SpeakerData(const u8* data, int length) { if (!SConfig::GetInstance().m_WiimoteEnableSpeaker) return; - if (m_reg_speaker.volume == 0 || m_reg_speaker.sample_rate == 0 || sd->length == 0) + if (reg_data.volume == 0 || reg_data.sample_rate == 0 || + length == 0) return; // TODO consider using static max size instead of new - std::unique_ptr samples(new s16[sd->length * 2]); + std::unique_ptr 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 == 0x40) { // 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]) << 8; } // 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 == 0x00) { // 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 @@ -109,19 +110,20 @@ void Wiimote::SpeakerData(const wm_speaker_data* sd) // 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); + // TODO: fix + unsigned int vol = 0;//(unsigned int)(m_options->numeric_settings[0]->GetValue() * 100); - 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 sample_rate = sample_rate_dividend / reg_data.sample_rate; + float speaker_volume_ratio = (float)reg_data.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); @@ -147,12 +149,12 @@ 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++; diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 93c985b300..f71cb55470 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -328,9 +328,9 @@ void Wiimote::Reset() memcpy(m_eeprom + 0x16D0, eeprom_data_16D0, sizeof(eeprom_data_16D0)); // set up the register - memset(&m_reg_speaker, 0, sizeof(m_reg_speaker)); // TODO: kill/move these + memset(&m_speaker_logic.reg_data, 0, sizeof(m_speaker_logic.reg_data)); memset(&m_camera_logic.reg_data, 0, sizeof(m_camera_logic.reg_data)); memset(&m_ext_logic.reg_data, 0, sizeof(m_ext_logic.reg_data)); @@ -361,14 +361,15 @@ void Wiimote::Reset() } // Yamaha ADPCM state initialize - m_adpcm_state.predictor = 0; - m_adpcm_state.step = 127; + m_speaker_logic.adpcm_state.predictor = 0; + m_speaker_logic.adpcm_state.step = 127; // Initialize i2c bus // TODO: kill magic numbers m_i2c_bus.Reset(); m_i2c_bus.AddSlave(0x58, &m_camera_logic); m_i2c_bus.AddSlave(0x52, &m_ext_logic); + m_i2c_bus.AddSlave(0x51, &m_speaker_logic); } Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1) diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 622b6857aa..ccd7337b2d 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -255,6 +255,8 @@ public: { INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); + // TODO: reads loop around at end of address space (0xff) + auto it = m_slaves.find(slave_addr); if (m_slaves.end() != it) return it->second->BusRead(addr, count, data_out); @@ -266,6 +268,8 @@ public: { INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); + // TODO: writes loop around at end of address space (0xff) + auto it = m_slaves.find(slave_addr); if (m_slaves.end() != it) return it->second->BusWrite(addr, count, data_in); @@ -396,10 +400,49 @@ private: } m_ext_logic; + struct SpeakerLogic : public I2CSlave + { + struct + { + // 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 + u16 sample_rate; + u8 volume; + u8 unk_6; + u8 unk_7; + u8 play; + u8 unk_9; + } reg_data; + + ADPCMState adpcm_state; + + void SpeakerData(const u8* data, int length); + + int BusRead(u8 addr, int count, u8* data_out) override + { + return raw_read(®_data, addr, count, data_out); + } + + int BusWrite(u8 addr, int count, const u8* data_in) override + { + if (0x00 == addr) + { + SpeakerData(data_in, count); + return count; + } + else + return raw_write(®_data, addr, count, data_in); + } + + } m_speaker_logic; + struct ReadRequest { - // u16 channel; - u32 address, size, position; + u16 address, size, position; u8* data; }; @@ -409,7 +452,6 @@ private: void ReadData(const wm_read_data* rd); void WriteData(const wm_write_data* wd); void SendReadDataReply(ReadRequest& request); - void SpeakerData(const wm_speaker_data* sd); bool NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size, u8 reporting_mode); // control groups @@ -463,8 +505,6 @@ private: wm_status_report m_status; - ADPCMState m_adpcm_state; - // read data request queue // maybe it isn't actually a queue // maybe read requests cancel any current requests @@ -485,21 +525,6 @@ private: u8 ext_identifier[6]; } m_reg_motion_plus; - struct SpeakerReg - { - u8 unused_0; - 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 - u16 sample_rate; - u8 volume; - u8 unk_6; - u8 unk_7; - u8 play; - u8 unk_9; - } m_reg_speaker; - #pragma pack(pop) }; } // namespace WiimoteEmu From 4dc0aa6f8e287e7aeebbe8fd878d75e817af2945 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Fri, 23 Nov 2018 18:01:56 -0600 Subject: [PATCH 05/22] WiimoteEmu: Process wiimote read data requests like they are on a real wiimote. It's not a queue. New requests are ignored and input is suppressed while processing a request. This simplifies the save state code greatly. --- .../Core/HW/WiimoteCommon/WiimoteReport.h | 2 +- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 246 ++++++++---------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 30 +-- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 26 +- 4 files changed, 125 insertions(+), 179 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h index 38b00e89c1..7845864f1f 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h @@ -439,7 +439,7 @@ struct wm_read_data_reply { wm_buttons buttons; u8 error : 4; // see WM_RDERR_* - u8 size : 4; + u8 size_minus_one : 4; u16 address; u8 data[16]; }; diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 2dd95780b1..010afb57d0 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -119,11 +119,12 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) case RT_WRITE_DATA: // 0x16 WriteData(reinterpret_cast(sr->data)); + return; // sends its own ack break; case RT_READ_DATA: // 0x17 ReadData(reinterpret_cast(sr->data)); - return; // sends its own ack + return; // sends its own ack/reply break; case RT_WRITE_SPEAKER_DATA: // 0x18 @@ -167,7 +168,7 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) The first two bytes are the core buttons data, 00 00 means nothing is pressed. The last byte is the success code 00. */ -void Wiimote::SendAck(u8 report_id) +void Wiimote::SendAck(u8 report_id, u8 error_code) { u8 data[6]; @@ -178,7 +179,7 @@ void Wiimote::SendAck(u8 report_id) ack->buttons = m_status.buttons; ack->reportID = report_id; - ack->errorID = 0; + ack->errorID = error_code; Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data)); } @@ -235,6 +236,8 @@ void Wiimote::WriteData(const wm_write_data* const wd) return; } + u8 error_code = 0; + switch (wd->space) { case WS_EEPROM: @@ -267,13 +270,15 @@ void Wiimote::WriteData(const wm_write_data* const wd) { // Write to Control Register - // TODO: generate a writedata error reply, 7 == no such slave (no ack) - m_i2c_bus.BusWrite(wd->slave_address >> 1, address & 0xff, wd->size, wd->data); + // Top byte of address is ignored on the bus. + auto const bytes_written = m_i2c_bus.BusWrite(wd->slave_address >> 1, (u8)address, wd->size, wd->data); + if (bytes_written != wd->size) + { + // A real wiimote gives error 7 for failed write to i2c bus (mainly a non-existant slave) + error_code = 0x07; + } - return; - - - //else if (&m_reg_motion_plus == region_ptr) + // else if (&m_reg_motion_plus == region_ptr) //{ // // activate/deactivate motion plus // if (0x55 == m_reg_motion_plus.activated) @@ -291,50 +296,95 @@ void Wiimote::WriteData(const wm_write_data* const wd) PanicAlert("WriteData: unimplemented parameters!"); break; } + + SendAck(RT_WRITE_DATA, error_code); } /* Read data from Wiimote and Extensions registers. */ void Wiimote::ReadData(const wm_read_data* const rd) { - u16 address = Common::swap16(rd->address); - u16 size = Common::swap16(rd->size); + if (m_read_request.size) + { + // There is already an active read request. + // a real wiimote ignores the new one. + return; + } - //INFO_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", rd->space, rd->slave_address, address, size); + // Save the request and process it on the next "Update()" calls + m_read_request.space = rd->space; + m_read_request.slave_address = rd->slave_address; + m_read_request.address = Common::swap16(rd->address); + // A zero size request is just ignored, like on the real wiimote. + m_read_request.size = Common::swap16(rd->size); - ReadRequest rr; - u8* const block = new u8[size]; + INFO_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", m_read_request.space, + m_read_request.slave_address, m_read_request.address, m_read_request.size); - switch (rd->space) + // Send up to one read-data-reply. + // If more data needs to be sent it will happen on the next "Update()" + ProcessReadDataRequest(); +} + +bool Wiimote::ProcessReadDataRequest() +{ + // Limit the amt to 16 bytes + // AyuanX: the MTU is 640B though... what a waste! + u16 const bytes_to_read = std::min((u16)16, m_read_request.size); + + if (0 == bytes_to_read) + { + // No active request: + return false; + } + + u8 data[23] = {}; + data[0] = 0xA1; + data[1] = RT_READ_DATA_REPLY; + + wm_read_data_reply* const reply = reinterpret_cast(data + 2); + reply->buttons = m_status.buttons; + reply->address = Common::swap16(m_read_request.address); + + switch (m_read_request.space) { case WS_EEPROM: { // Read from EEPROM - if (address + size >= WIIMOTE_EEPROM_FREE_SIZE) + if (m_read_request.address + m_read_request.size >= WIIMOTE_EEPROM_FREE_SIZE) { - if (address + size > WIIMOTE_EEPROM_SIZE) + if (m_read_request.address + m_read_request.size > WIIMOTE_EEPROM_SIZE) { PanicAlert("ReadData: address + size out of bounds"); - delete[] block; - return; } - // generate a read error, even if the start of the block is readable a real wiimote just sends error code 8 - size = 0; - } + // generate a read error, even if the start of the block is readable a real wiimote just sends + // error code 8 - // read mii data from file - if (address >= 0x0FCA && address < 0x12C0) + // The real Wiimote generate an error for the first + // request to 0x1770 if we dont't replicate that the game will never + // read the calibration data at the beginning of Eeprom. I think this + // error is supposed to occur when we try to read above the freely + // usable space that ends at 0x16ff. + reply->error = 0x08; + } + else { - // TODO Only read the Mii block parts required - std::ifstream file; - File::OpenFStream(file, (File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/mii.bin").c_str(), - std::ios::binary | std::ios::in); - file.read((char*)m_eeprom + 0x0FCA, 0x02f0); - file.close(); - } + // Mii block handling: + // TODO: different filename for each wiimote? + if (m_read_request.address >= 0x0FCA && m_read_request.address < 0x12C0) + { + // TODO: Only read the Mii block parts required + std::ifstream file; + File::OpenFStream(file, (File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/mii.bin").c_str(), + std::ios::binary | std::ios::in); + file.read((char*)m_eeprom + 0x0FCA, 0x02f0); + file.close(); + } - // read memory to be sent to Wii - memcpy(block, m_eeprom + address, size); + // read memory to be sent to Wii + std::copy_n(m_eeprom + m_read_request.address, bytes_to_read, reply->data); + reply->size_minus_one = bytes_to_read - 1; + } } break; @@ -343,83 +393,33 @@ void Wiimote::ReadData(const wm_read_data* const rd) { // Read from Control Register - m_i2c_bus.BusRead(rd->slave_address >> 1, address & 0xff, size, block); - // TODO: generate read errors, 7 == no such slave (no ack) + // Top byte of address is ignored on the bus, but it IS maintained in the read-reply. + auto const bytes_read = m_i2c_bus.BusRead(m_read_request.slave_address >> 1, + (u8)m_read_request.address, bytes_to_read, reply->data); + + reply->size_minus_one = bytes_read - 1; + + if (bytes_read != bytes_to_read) + { + // generate read error, 7 == no such slave (no ack) + reply->error = 0x07; + } } break; default: - PanicAlert("Wiimote::ReadData: unimplemented address space (space: 0x%x)!", rd->space); + PanicAlert("Wiimote::ReadData: unimplemented address space (space: 0x%x)!", m_read_request.space); break; } - rr.address = address; - rr.size = size; - rr.position = 0; - rr.data = block; + // Modify the read request, zero size == complete + m_read_request.address += bytes_to_read; + m_read_request.size -= bytes_to_read; - // TODO: read requests suppress normal input reports - // TODO: if there is currently an active read request ignore new ones - - // send up to 16 bytes - SendReadDataReply(rr); - - // if there is more data to be sent, add it to the queue - if (rr.size) - m_read_requests.push(rr); - else - delete[] rr.data; -} - -void Wiimote::SendReadDataReply(ReadRequest& request) -{ - u8 data[23]; - data[0] = 0xA1; - data[1] = RT_READ_DATA_REPLY; - - wm_read_data_reply* const reply = reinterpret_cast(data + 2); - reply->buttons = m_status.buttons; - reply->address = Common::swap16(request.address); - - // generate a read error - // Out of bounds. The real Wiimote generate an error for the first - // request to 0x1770 if we dont't replicate that the game will never - // read the calibration data at the beginning of Eeprom. I think this - // error is supposed to occur when we try to read above the freely - // usable space that ends at 0x16ff. - if (0 == request.size) - { - reply->size = 0x0f; - reply->error = 0x08; - - memset(reply->data, 0, sizeof(reply->data)); - } - else - { - // Limit the amt to 16 bytes - // AyuanX: the MTU is 640B though... what a waste! - const int amt = std::min((u16)16, request.size); - - // no error - reply->error = 0; - - // 0x1 means two bytes, 0xf means 16 bytes - reply->size = amt - 1; - - // Clear the mem first - memset(reply->data, 0, sizeof(reply->data)); - - // copy piece of mem - memcpy(reply->data, request.data + request.position, amt); - - // update request struct - request.size -= amt; - request.position += amt; - request.address += amt; - } - - // Send a piece + // Send the data Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data)); + + return true; } void Wiimote::DoState(PointerWrap& p) @@ -446,47 +446,7 @@ void Wiimote::DoState(PointerWrap& p) p.Do(m_camera_logic.reg_data); p.Do(m_ext_logic.reg_data); p.Do(m_speaker_logic.reg_data); - - // Do 'm_read_requests' queue - { - u32 size = 0; - if (p.mode == PointerWrap::MODE_READ) - { - // clear - while (!m_read_requests.empty()) - { - delete[] m_read_requests.front().data; - m_read_requests.pop(); - } - - p.Do(size); - while (size--) - { - ReadRequest tmp; - p.Do(tmp.address); - p.Do(tmp.position); - p.Do(tmp.size); - tmp.data = new u8[tmp.size]; - p.DoArray(tmp.data, tmp.size); - m_read_requests.push(tmp); - } - } - else - { - std::queue tmp_queue(m_read_requests); - size = (u32)(m_read_requests.size()); - p.Do(size); - while (!tmp_queue.empty()) - { - ReadRequest tmp = tmp_queue.front(); - p.Do(tmp.address); - p.Do(tmp.position); - p.Do(tmp.size); - p.DoArray(tmp.data, tmp.size); - tmp_queue.pop(); - } - } - } + p.Do(m_read_request); p.DoMarker("Wiimote"); if (p.GetMode() == PointerWrap::MODE_READ) @@ -504,4 +464,4 @@ void Wiimote::RealState() g_wiimotes[m_index]->EnableDataReporting(m_reporting_mode); } } -} +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index f71cb55470..61347c2b4a 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -353,12 +353,7 @@ void Wiimote::Reset() m_swing_dynamic_data = {}; m_shake_dynamic_data = {}; - // clear read request queue - while (!m_read_requests.empty()) - { - delete[] m_read_requests.front().data; - m_read_requests.pop(); - } + m_read_request.size = 0; // Yamaha ADPCM state initialize m_speaker_logic.adpcm_state.predictor = 0; @@ -552,21 +547,10 @@ bool Wiimote::Step() UpdateButtonsStatus(); } - // check if there is a read data request - if (!m_read_requests.empty()) + if (ProcessReadDataRequest()) { - ReadRequest& rr = m_read_requests.front(); - // send up to 16 bytes to the Wii - SendReadDataReply(rr); - - // if there is no more data, remove from queue - if (0 == rr.size) - { - delete[] rr.data; - m_read_requests.pop(); - } - - // don't send any other reports + // Read requests suppress normal input reports + // Don't send any other reports return true; } @@ -923,7 +907,8 @@ void Wiimote::Update() feature_ptr += rptf.ext; } - Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, m_ext_logic.ext_key); + Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, + m_ext_logic.ext_key); } if (NetPlay::IsNetPlayRunning()) { @@ -933,7 +918,8 @@ void Wiimote::Update() } // TODO: need to fix usage of rptf probably - Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key); + Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension, + m_ext_logic.ext_key); // don't send a data report if auto reporting is off if (false == m_reporting_auto && data[1] >= RT_REPORT_CORE) diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index ccd7337b2d..6ab0659ca7 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -5,7 +5,6 @@ #pragma once #include -#include #include #include "Common/Logging/Log.h" @@ -220,6 +219,8 @@ public: { static_assert(std::is_pod::value); + // TODO: addr wraps around after 0xff + u8* src = reinterpret_cast(reg_data) + addr; count = std::min(count, int(reinterpret_cast(reg_data + 1) - src)); @@ -233,6 +234,8 @@ public: { static_assert(std::is_pod::value); + // TODO: addr wraps around after 0xff + u8* dst = reinterpret_cast(reg_data) + addr; count = std::min(count, int(reinterpret_cast(reg_data + 1) - dst)); @@ -440,18 +443,12 @@ private: } m_speaker_logic; - struct ReadRequest - { - u16 address, size, position; - u8* data; - }; - void ReportMode(const wm_report_mode* dr); - void SendAck(u8 report_id); + void SendAck(u8 report_id, u8 error_code = 0x0); void RequestStatus(const wm_request_status* rs = nullptr); void ReadData(const wm_read_data* rd); void WriteData(const wm_write_data* wd); - void SendReadDataReply(ReadRequest& request); + bool ProcessReadDataRequest(); bool NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size, u8 reporting_mode); // control groups @@ -505,10 +502,13 @@ private: wm_status_report m_status; - // read data request queue - // maybe it isn't actually a queue - // maybe read requests cancel any current requests - std::queue m_read_requests; + struct ReadRequest + { + u8 space; + u8 slave_address; + u16 address; + u16 size; + } m_read_request; #pragma pack(push, 1) u8 m_eeprom[WIIMOTE_EEPROM_SIZE]; From 84d32f66455c7138e831ce8223fed03b9b3820d4 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Fri, 23 Nov 2018 19:31:58 -0600 Subject: [PATCH 06/22] WiimoteEmu: Fix usage of ReportFeatures in TAS related code. --- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 40 +++++++++++-------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 14 ++++--- Source/Core/Core/Movie.cpp | 12 +++--- .../Core/DolphinQt/TAS/WiiTASInputWindow.cpp | 15 +++---- 4 files changed, 46 insertions(+), 35 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 61347c2b4a..587031f2b0 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -849,15 +849,16 @@ void Wiimote::Update() Movie::SetPolledDevice(); + // TODO: Is max battery really 100 and not 0xff? m_status.battery = (u8)(m_battery_setting->GetValue() * 100); const ReportFeatures& rptf = reporting_mode_features[m_reporting_mode - RT_REPORT_CORE]; - s8 rptf_size = rptf.size; + s8 rptf_size = rptf.total_size; if (Movie::IsPlayingInput() && Movie::PlayWiimote(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key)) { - if (rptf.core) - m_status.buttons = *reinterpret_cast(data + rptf.core); + if (rptf.core_size) + m_status.buttons = *reinterpret_cast(data + 2); } else { @@ -873,38 +874,43 @@ void Wiimote::Update() u8* feature_ptr = data + 2; // core buttons - if (rptf.core) + if (rptf.core_size) { GetButtonData(feature_ptr); - feature_ptr += rptf.core; + feature_ptr += rptf.core_size; } // acceleration - if (rptf.accel) + if (rptf.accel_size) { // TODO: GetAccelData has hardcoded payload offsets.. GetAccelData(data); - feature_ptr += rptf.accel; + feature_ptr += rptf.accel_size; } // IR Camera // TODO: kill use_accel param // TODO: call only if camera logic is enabled? - UpdateIRData(rptf.accel != 0); - if (rptf.ir) + UpdateIRData(rptf.accel_size != 0); + if (rptf.ir_size) { // TODO: kill magic numbers - m_i2c_bus.BusRead(0x58, 0x37, rptf.ir, feature_ptr); - feature_ptr += rptf.ir; + m_i2c_bus.BusRead(0x58, 0x37, rptf.ir_size, feature_ptr); + feature_ptr += rptf.ir_size; } // extension UpdateExtData(); - if (rptf.ext) + if (rptf.ext_size) { // TODO: kill magic numbers - m_i2c_bus.BusRead(0x52, 0x00, rptf.ext, feature_ptr); - feature_ptr += rptf.ext; + m_i2c_bus.BusRead(0x52, 0x00, rptf.ext_size, feature_ptr); + feature_ptr += rptf.ext_size; + } + + if (feature_ptr != data + rptf_size) + { + PanicAlert("Wiimote input report is the wrong size!"); } Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, @@ -912,9 +918,9 @@ void Wiimote::Update() } if (NetPlay::IsNetPlayRunning()) { - NetPlay_GetWiimoteData(m_index, data, rptf.size, m_reporting_mode); - if (rptf.core) - m_status.buttons = *reinterpret_cast(data + rptf.core); + NetPlay_GetWiimoteData(m_index, data, rptf.total_size, m_reporting_mode); + if (rptf.core_size) + m_status.buttons = *reinterpret_cast(data + rptf.core_size); } // TODO: need to fix usage of rptf probably diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 6ab0659ca7..9a41765626 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -16,9 +16,6 @@ // Registry sizes #define WIIMOTE_EEPROM_SIZE (16 * 1024) #define WIIMOTE_EEPROM_FREE_SIZE 0x1700 -#define WIIMOTE_REG_SPEAKER_SIZE 10 -#define WIIMOTE_REG_EXT_SIZE 0x100 -#define WIIMOTE_REG_IR_SIZE 0x34 class PointerWrap; @@ -116,7 +113,14 @@ enum class TurntableGroup struct ReportFeatures { - u8 core, accel, ir, ext, size; + // Byte counts: + // Features are always in the following order in an input report: + u8 core_size, accel_size, ir_size, ext_size, total_size; + + int GetCoreOffset() const { return 2; } + int GetAccelOffset() const { return GetCoreOffset() + core_size; } + int GetIROffset() const { return GetAccelOffset() + accel_size; } + int GetExtOffset() const { return GetIROffset() + ir_size; } }; struct AccelData @@ -403,7 +407,7 @@ private: } m_ext_logic; - struct SpeakerLogic : public I2CSlave + struct SpeakerLogic : public I2CSlave { struct { diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index 85e4b47e52..36d18f8be1 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -648,10 +648,10 @@ static void SetWiiInputDisplayString(int remoteID, const u8* const data, 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; + const u8* const coreData = rptf.core_size ? (data + rptf.GetCoreOffset()) : nullptr; + const u8* const accelData = rptf.accel_size ? (data + rptf.GetAccelOffset()) : nullptr; + const u8* const irData = rptf.ir_size ? (data + rptf.GetIROffset()) : nullptr; + const u8* const extData = rptf.ext_size ? (data + rptf.GetExtOffset()) : nullptr; if (coreData) { @@ -821,7 +821,7 @@ void CheckWiimoteStatus(int wiimote, const u8* data, const WiimoteEmu::ReportFea SetWiiInputDisplayString(wiimote, data, rptf, ext, key); if (IsRecordingInput()) - RecordWiimote(wiimote, data, rptf.size); + RecordWiimote(wiimote, data, rptf.total_size); } void RecordWiimote(int wiimote, const u8* data, u8 size) @@ -1204,7 +1204,7 @@ bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf, return false; } - u8 size = rptf.size; + u8 size = rptf.total_size; u8 sizeInMovie = s_temp_input[s_currentByte]; diff --git a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp index 03a09f83a9..71d62f5fe3 100644 --- a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp +++ b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp @@ -326,10 +326,10 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp 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; + u8* const buttons_data = rptf.core_size ? (report_data + rptf.GetCoreOffset()) : nullptr; + u8* const accel_data = rptf.accel_size ? (report_data + rptf.GetAccelOffset()) : nullptr; + u8* const ir_data = rptf.ir_size ? (report_data + rptf.GetIROffset()) : nullptr; + u8* const ext_data = rptf.ext_size ? (report_data + rptf.GetExtOffset()) : nullptr; if (m_remote_buttons_box->isVisible() && buttons_data) { @@ -380,10 +380,11 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp u8 mode; // Mode 5 not supported in core anyway. - if (rptf.ext) - mode = (rptf.ext - rptf.ir) == 10 ? 1 : 3; + // TODO: Can just use ir_size to determine mode + if (rptf.ext_size) + mode = (rptf.GetExtOffset() - rptf.GetIROffset()) == 10 ? 1 : 3; else - mode = (rptf.size - rptf.ir) == 10 ? 1 : 3; + mode = (rptf.total_size - rptf.GetIROffset()) == 10 ? 1 : 3; if (mode == 1) { From ba936923bd75e1ba1572d44c905c18300bf0d814 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 24 Nov 2018 10:01:39 -0600 Subject: [PATCH 07/22] WiimoteEmu: Tweak the i2c bus code to better support motion plus and its passthrough port. --- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 2 +- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 31 ++- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 258 ++++++++++++++---- 3 files changed, 232 insertions(+), 59 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 010afb57d0..e2b53cb95b 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -442,7 +442,7 @@ void Wiimote::DoState(PointerWrap& p) p.Do(m_speaker_logic.adpcm_state); p.Do(m_ext_logic.ext_key); p.DoArray(m_eeprom); - p.Do(m_reg_motion_plus); + p.Do(m_motion_plus_logic.reg_data); p.Do(m_camera_logic.reg_data); p.Do(m_ext_logic.reg_data); p.Do(m_speaker_logic.reg_data); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 587031f2b0..0a3735560a 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -100,10 +100,9 @@ static const ReportFeatures reporting_mode_features[] = { {2, 0, 10, 9, 23}, // 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes {2, 3, 10, 6, 23}, + // UNSUPPORTED (but should be easy enough to implement): // 0x3d: 21 Extension Bytes {0, 0, 0, 21, 23}, - - // UNSUPPORTED: // 0x3e / 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes {0, 0, 0, 0, 23}, }; @@ -334,9 +333,8 @@ void Wiimote::Reset() memset(&m_camera_logic.reg_data, 0, sizeof(m_camera_logic.reg_data)); memset(&m_ext_logic.reg_data, 0, sizeof(m_ext_logic.reg_data)); - memset(&m_reg_motion_plus, 0, sizeof(m_reg_motion_plus)); - - memcpy(&m_reg_motion_plus.ext_identifier, motion_plus_id, sizeof(motion_plus_id)); + memset(&m_motion_plus_logic.reg_data, 0, sizeof(m_motion_plus_logic.reg_data)); + memcpy(&m_motion_plus_logic.reg_data.ext_identifier, motion_plus_id, sizeof(motion_plus_id)); // status memset(&m_status, 0, sizeof(m_status)); @@ -362,9 +360,20 @@ void Wiimote::Reset() // Initialize i2c bus // TODO: kill magic numbers m_i2c_bus.Reset(); - m_i2c_bus.AddSlave(0x58, &m_camera_logic); - m_i2c_bus.AddSlave(0x52, &m_ext_logic); - m_i2c_bus.AddSlave(0x51, &m_speaker_logic); + // Address 0x51 + m_i2c_bus.AddSlave(&m_speaker_logic); + + // TODO: only add to bus when enabled + // Address 0x53 (or 0x52 when activated) + m_i2c_bus.AddSlave(&m_motion_plus_logic); + // Address 0x58 + m_i2c_bus.AddSlave(&m_camera_logic); + + // TODO: add directly to wiimote bus when mplus is disabled + // TODO: only add to bus when connected: + // Address 0x52 (when motion plus is not activated) + // Connected to motion plus i2c_bus (with passthrough by default) + m_motion_plus_logic.i2c_bus.AddSlave(&m_ext_logic); } Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1) @@ -422,6 +431,7 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1 // extension groups.emplace_back(m_extension = new ControllerEmu::Extension(_trans("Extension"))); + m_ext_logic.extension = m_extension; m_extension->attachments.emplace_back(new WiimoteEmu::None(m_ext_logic.reg_data)); m_extension->attachments.emplace_back(new WiimoteEmu::Nunchuk(m_ext_logic.reg_data)); m_extension->attachments.emplace_back(new WiimoteEmu::Classic(m_ext_logic.reg_data)); @@ -908,6 +918,11 @@ void Wiimote::Update() feature_ptr += rptf.ext_size; } + // motion plus + auto* mplus_data = reinterpret_cast(m_motion_plus_logic.reg_data.controller_data); + *mplus_data = wm_motionplus_data(); + mplus_data->is_mp_data = true; + if (feature_ptr != data + rptf_size) { PanicAlert("Wiimote input report is the wrong size!"); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 9a41765626..b2e6218acc 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -215,11 +215,14 @@ enum class I2CSlave { public: - virtual int BusRead(u8 addr, int count, u8* data_out) = 0; - virtual int BusWrite(u8 addr, int count, const u8* data_in) = 0; + // Kill MSVC warning: + virtual ~I2CSlave() = default; + + virtual int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) = 0; + virtual int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) = 0; template - static int raw_read(T* reg_data, u8 addr, int count, u8* data_out) + static int RawRead(T* reg_data, u8 addr, int count, u8* data_out) { static_assert(std::is_pod::value); @@ -234,7 +237,7 @@ public: } template - static int raw_write(T* reg_data, u8 addr, int count, const u8* data_in) + static int RawWrite(T* reg_data, u8 addr, int count, const u8* data_in) { static_assert(std::is_pod::value); @@ -252,9 +255,15 @@ public: class I2CBus { public: - void AddSlave(u8 addr, I2CSlave* slave) { m_slaves.insert(std::make_pair(addr, slave)); } + void AddSlave(I2CSlave* slave) + { + m_slaves.emplace_back(slave); + } - void RemoveSlave(u8 addr) { m_slaves.erase(addr); } + void RemoveSlave(I2CSlave* slave) + { + m_slaves.erase(std::remove(m_slaves.begin(), m_slaves.end(), slave), m_slaves.end()); + } void Reset() { m_slaves.clear(); } @@ -262,31 +271,71 @@ public: { INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); - // TODO: reads loop around at end of address space (0xff) + for (auto& slave : m_slaves) + { + auto const bytes_read = slave->BusRead(slave_addr, addr, count, data_out); - auto it = m_slaves.find(slave_addr); - if (m_slaves.end() != it) - return it->second->BusRead(addr, count, data_out); - else - return 0; + // A slave responded, we are done. + if (bytes_read) + return bytes_read; + } + + return 0; } int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) { INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); - // TODO: writes loop around at end of address space (0xff) + for (auto& slave : m_slaves) + { + auto const bytes_written = slave->BusWrite(slave_addr, addr, count, data_in); - auto it = m_slaves.find(slave_addr); - if (m_slaves.end() != it) - return it->second->BusWrite(addr, count, data_in); - else - return 0; + // A slave responded, we are done. + if (bytes_written) + return bytes_written; + } + + return 0; } private: - // Organized by slave addr - std::map m_slaves; + std::vector m_slaves; +}; + +class ExtensionAttachment : public I2CSlave +{ +public: + virtual bool ReadDeviceDetectPin() = 0; +}; + +class ExtensionPort +{ +public: + ExtensionPort(I2CBus& _i2c_bus) + : m_i2c_bus(_i2c_bus) + {} + + // Simulates the "device-detect" pin. + // Wiimote uses this to detect extension change.. + // and then send a status report.. + bool IsDeviceConnected() + { + if (m_attachment) + return m_attachment->ReadDeviceDetectPin(); + else + return false; + } + + void SetAttachment(ExtensionAttachment* dev) + { + m_i2c_bus.RemoveSlave(m_attachment); + m_i2c_bus.AddSlave(m_attachment = dev); + } + +private: + ExtensionAttachment* m_attachment; + I2CBus& m_i2c_bus; }; class Wiimote : public ControllerEmu::EmulatedController @@ -347,6 +396,10 @@ protected: bool WantExtension() const; private: + I2CBus m_i2c_bus; + + ExtensionPort m_extension_port{m_i2c_bus}; + struct IRCameraLogic : public I2CSlave { struct @@ -362,26 +415,41 @@ private: static_assert(0x100 == sizeof(reg_data)); - int BusRead(u8 addr, int count, u8* data_out) override + static const u8 DEVICE_ADDR = 0x58; + + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override { - return raw_read(®_data, addr, count, data_out); + if (DEVICE_ADDR != slave_addr) + return 0; + + return RawRead(®_data, addr, count, data_out); } - int BusWrite(u8 addr, int count, const u8* data_in) override + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override { - return raw_write(®_data, addr, count, data_in); + if (DEVICE_ADDR != slave_addr) + return 0; + + return RawWrite(®_data, addr, count, data_in); } } m_camera_logic; - struct ExtensionLogic : public I2CSlave + struct ExtensionLogic : public ExtensionAttachment { ExtensionReg reg_data; wiimote_key ext_key; - int BusRead(u8 addr, int count, u8* data_out) override + ControllerEmu::Extension* extension; + + static const u8 DEVICE_ADDR = 0x52; + + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override { - auto const result = raw_read(®_data, addr, count, data_out); + if (DEVICE_ADDR != slave_addr) + return 0; + + auto const result = RawRead(®_data, addr, count, data_out); // Encrypt data read from extension register // Check if encrypted reads is on @@ -391,9 +459,12 @@ private: return result; } - int BusWrite(u8 addr, int count, const u8* data_in) override + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override { - auto const result = raw_write(®_data, addr, count, data_in); + if (DEVICE_ADDR != slave_addr) + return 0; + + auto const result = RawWrite(®_data, addr, count, data_in); if (addr + count > 0x40 && addr < 0x50) { @@ -405,6 +476,11 @@ private: return result; } + bool ReadDeviceDetectPin() override + { + return true; + } + } m_ext_logic; struct SpeakerLogic : public I2CSlave @@ -427,26 +503,125 @@ private: ADPCMState adpcm_state; + static const u8 DEVICE_ADDR = 0x51; + void SpeakerData(const u8* data, int length); - int BusRead(u8 addr, int count, u8* data_out) override + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override { - return raw_read(®_data, addr, count, data_out); + if (DEVICE_ADDR != slave_addr) + return 0; + + return RawRead(®_data, addr, count, data_out); } - int BusWrite(u8 addr, int count, const u8* data_in) override + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override { + if (DEVICE_ADDR != slave_addr) + return 0; + if (0x00 == addr) { SpeakerData(data_in, count); return count; } else - return raw_write(®_data, addr, count, data_in); + return RawWrite(®_data, addr, count, data_in); } } m_speaker_logic; + struct MotionPlusLogic : public ExtensionAttachment + { + // The bus on the end of the motion plus: + I2CBus i2c_bus; + + // The port on the end of the motion plus: + ExtensionPort extension_port{i2c_bus}; + +#pragma pack(push, 1) + struct MotionPlusRegister + { + u8 controller_data[0x10]; + u8 unknown[0x10]; + u8 calibration_data[0x20]; + u8 unknown2[0xb0]; + + // address 0xF0 + // TODO: bad name + u8 activated; + + u8 unknown3[9]; + + // address 0xFA + u8 ext_identifier[6]; + } reg_data; +#pragma pack(pop) + + static_assert(0x100 == sizeof(reg_data)); + + static const u8 DEVICE_ADDR = 0x53; + static const u8 EXT_DEVICE_ADDR = 0x52; + + bool IsActive() const { return reg_data.activated; } + + u8 GetPassthroughMode() const { return reg_data.ext_identifier[4]; } + + // Return the status of the "device detect" pin + // used to product status reports on device change + bool GetDevicePresent() const + { + if (IsActive()) + { + return true; + } + else + { + // TODO: passthrough other extension attachment status + return false; + } + } + + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override + { + // if (DEVICE_ADDR != slave_addr) + // return 0; + + return i2c_bus.BusRead(slave_addr, addr, count, data_out); + + auto const result = RawRead(®_data, addr, count, data_out); + + return result; + } + + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override + { + // if (DEVICE_ADDR != slave_addr) + // return 0; + + return i2c_bus.BusWrite(slave_addr, addr, count, data_in); + + auto const result = RawWrite(®_data, addr, count, data_in); + + if (0xfe == addr) + { + if (true) // 0x55 == reg_data.activated) + { + // i2c_bus.SetSlave(0x52, this); + // i2c_bus.RemoveSlave(0x53); + } + } + + return result; + } + + bool ReadDeviceDetectPin() override + { + return true; + } + + } m_motion_plus_logic; + void ReportMode(const wm_report_mode* dr); void SendAck(u8 report_id, u8 error_code = 0x0); void RequestStatus(const wm_request_status* rs = nullptr); @@ -480,8 +655,6 @@ private: DynamicData m_swing_dynamic_data; DynamicData m_shake_dynamic_data; - I2CBus m_i2c_bus; - // Wiimote accel data AccelData m_accel; @@ -514,21 +687,6 @@ private: u16 size; } m_read_request; -#pragma pack(push, 1) u8 m_eeprom[WIIMOTE_EEPROM_SIZE]; - struct MotionPlusReg - { - u8 unknown[0xF0]; - - // address 0xF0 - u8 activated; - - u8 unknown2[9]; - - // address 0xFA - u8 ext_identifier[6]; - } m_reg_motion_plus; - -#pragma pack(pop) }; } // namespace WiimoteEmu From 372b12c67fe2afbe749d6c6c8de3629a1b942c62 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 24 Nov 2018 11:49:08 -0600 Subject: [PATCH 08/22] WiimoteEmu: Emulated motion plus is detected by game now. --- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 36 ++--- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 51 +++---- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 131 ++++++++++-------- 3 files changed, 114 insertions(+), 104 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index e2b53cb95b..5158c59bbf 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -33,6 +33,7 @@ #include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include "Core/HW/WiimoteReal/WiimoteReal.h" #include "InputCommon/ControllerEmu/ControlGroup/Extension.h" +#include "InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.h" namespace WiimoteEmu { @@ -146,9 +147,6 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) break; case RT_IR_LOGIC: // 0x1a - // comment from old plugin: - // This enables or disables the IR lights, we update the global variable g_IR - // so that WmRequestStatus() knows about it m_status.ir = sr->enable; if (false == sr->ack) return; @@ -206,10 +204,18 @@ void Wiimote::HandleExtensionSwap() void Wiimote::RequestStatus(const wm_request_status* const rs) { - HandleExtensionSwap(); + INFO_LOG(WIIMOTE, "Wiimote::RequestStatus"); // update status struct - m_status.extension = m_extension->active_extension ? 1 : 0; + m_status.extension = m_extension_port.IsDeviceConnected(); + // Battery levels in voltage + // 0x00 - 0x32: level 1 + // 0x33 - 0x43: level 2 + // 0x33 - 0x54: level 3 + // 0x55 - 0xff: level 4 + m_status.battery = (u8)(m_battery_setting->GetValue() * 0xff); + // TODO: this right? + m_status.battery_low = m_status.battery < 0x33; // set up report u8 data[8]; @@ -228,7 +234,13 @@ void Wiimote::WriteData(const wm_write_data* const wd) { u16 address = Common::swap16(wd->address); - INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x (%d)", wd->space, address, wd->size); + if (0 == wd->size) + { + // Ignore requests of zero size + return; + } + + INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x @ 0x%02x (%d)", wd->space, wd->slave_address, address, wd->size); if (wd->size > 16) { @@ -277,18 +289,6 @@ void Wiimote::WriteData(const wm_write_data* const wd) // A real wiimote gives error 7 for failed write to i2c bus (mainly a non-existant slave) error_code = 0x07; } - - // else if (&m_reg_motion_plus == region_ptr) - //{ - // // activate/deactivate motion plus - // if (0x55 == m_reg_motion_plus.activated) - // { - // // maybe hacky - // m_reg_motion_plus.activated = 0; - - // RequestStatus(); - // } - //} } break; diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 0a3735560a..af98f7ec45 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -308,16 +308,13 @@ static const char* const named_buttons[] = { void Wiimote::Reset() { m_reporting_mode = RT_REPORT_CORE; - // i think these two are good m_reporting_channel = 0; m_reporting_auto = false; m_rumble_on = false; m_speaker_mute = false; - // will make the first Update() call send a status request - // the first call to RequestStatus() will then set up the status struct extension bit - m_extension->active_extension = -1; + m_extension->active_extension = 0; // eeprom memset(m_eeprom, 0, sizeof(m_eeprom)); @@ -338,12 +335,6 @@ void Wiimote::Reset() // status memset(&m_status, 0, sizeof(m_status)); - // Battery levels in voltage - // 0x00 - 0x32: level 1 - // 0x33 - 0x43: level 2 - // 0x33 - 0x54: level 3 - // 0x55 - 0xff: level 4 - m_status.battery = (u8)(m_battery_setting->GetValue() * 100); m_shake_step = {}; m_shake_soft_step = {}; @@ -358,22 +349,22 @@ void Wiimote::Reset() m_speaker_logic.adpcm_state.step = 127; // Initialize i2c bus - // TODO: kill magic numbers m_i2c_bus.Reset(); // Address 0x51 m_i2c_bus.AddSlave(&m_speaker_logic); - - // TODO: only add to bus when enabled - // Address 0x53 (or 0x52 when activated) - m_i2c_bus.AddSlave(&m_motion_plus_logic); // Address 0x58 m_i2c_bus.AddSlave(&m_camera_logic); + // TODO: only add to bus when enabled + // This also adds the motion plus to the i2c bus + // Address 0x53 (or 0x52 when activated) + m_extension_port.SetAttachment(&m_motion_plus_logic); + // TODO: add directly to wiimote bus when mplus is disabled // TODO: only add to bus when connected: // Address 0x52 (when motion plus is not activated) // Connected to motion plus i2c_bus (with passthrough by default) - m_motion_plus_logic.i2c_bus.AddSlave(&m_ext_logic); + m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic); } Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1) @@ -564,19 +555,22 @@ bool Wiimote::Step() return true; } - // check if a status report needs to be sent - // this happens on Wii Remote sync and when extensions are switched - if (m_extension->active_extension != m_extension->switch_extension) - { - RequestStatus(); + // If an extension change is requested in the GUI it will first be disconnected here. + // causing IsDeviceConnected() to return false below: + HandleExtensionSwap(); + // check if a status report needs to be sent + // this happens when extensions are switched + if (m_status.extension != m_extension_port.IsDeviceConnected()) + { // WiiBrew: Following a connection or disconnection event on the Extension Port, // data reporting is disabled and the Data Reporting Mode must be reset before new data can // arrive. - // after a game receives an unrequested status report, - // it expects data reports to stop until it sets the reporting mode again + m_reporting_mode = RT_REPORT_CORE; m_reporting_auto = false; + RequestStatus(); + return true; } @@ -859,9 +853,6 @@ void Wiimote::Update() Movie::SetPolledDevice(); - // TODO: Is max battery really 100 and not 0xff? - m_status.battery = (u8)(m_battery_setting->GetValue() * 100); - const ReportFeatures& rptf = reporting_mode_features[m_reporting_mode - RT_REPORT_CORE]; s8 rptf_size = rptf.total_size; if (Movie::IsPlayingInput() && @@ -1113,13 +1104,9 @@ int Wiimote::CurrentExtension() const return m_extension->active_extension; } -bool Wiimote::HaveExtension() const +bool Wiimote::ExtensionLogic::ReadDeviceDetectPin() { - return m_extension->active_extension > 0; + return extension->active_extension ? true : false; } -bool Wiimote::WantExtension() const -{ - return m_extension->switch_extension != 0; -} } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index b2e6218acc..b44a6f81a5 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -225,6 +225,7 @@ public: static int RawRead(T* reg_data, u8 addr, int count, u8* data_out) { static_assert(std::is_pod::value); + static_assert(0x100 == sizeof(T)); // TODO: addr wraps around after 0xff @@ -240,6 +241,7 @@ public: static int RawWrite(T* reg_data, u8 addr, int count, const u8* data_in) { static_assert(std::is_pod::value); + static_assert(0x100 == sizeof(T)); // TODO: addr wraps around after 0xff @@ -255,10 +257,7 @@ public: class I2CBus { public: - void AddSlave(I2CSlave* slave) - { - m_slaves.emplace_back(slave); - } + void AddSlave(I2CSlave* slave) { m_slaves.emplace_back(slave); } void RemoveSlave(I2CSlave* slave) { @@ -270,7 +269,6 @@ public: int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) { INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); - for (auto& slave : m_slaves) { auto const bytes_read = slave->BusRead(slave_addr, addr, count, data_out); @@ -286,7 +284,6 @@ public: int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) { INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); - for (auto& slave : m_slaves) { auto const bytes_written = slave->BusWrite(slave_addr, addr, count, data_in); @@ -312,9 +309,7 @@ public: class ExtensionPort { public: - ExtensionPort(I2CBus& _i2c_bus) - : m_i2c_bus(_i2c_bus) - {} + ExtensionPort(I2CBus& _i2c_bus) : m_i2c_bus(_i2c_bus) {} // Simulates the "device-detect" pin. // Wiimote uses this to detect extension change.. @@ -392,9 +387,6 @@ protected: void UpdateIRData(bool use_accel); void UpdateExtData(); - bool HaveExtension() const; - bool WantExtension() const; - private: I2CBus m_i2c_bus; @@ -435,13 +427,15 @@ private: } m_camera_logic; - struct ExtensionLogic : public ExtensionAttachment + class ExtensionLogic : public ExtensionAttachment { + public: ExtensionReg reg_data; wiimote_key ext_key; ControllerEmu::Extension* extension; + private: static const u8 DEVICE_ADDR = 0x52; int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override @@ -476,10 +470,7 @@ private: return result; } - bool ReadDeviceDetectPin() override - { - return true; - } + bool ReadDeviceDetectPin() override; } m_ext_logic; @@ -499,8 +490,11 @@ private: u8 unk_7; u8 play; u8 unk_9; + u8 unknown[0xf4]; } reg_data; + static_assert(0x100 == sizeof(reg_data)); + ADPCMState adpcm_state; static const u8 DEVICE_ADDR = 0x51; @@ -526,7 +520,10 @@ private: return count; } else + { + // TODO: should address wrap around after 0xff result in processing speaker data? return RawWrite(®_data, addr, count, data_in); + } } } m_speaker_logic; @@ -560,67 +557,93 @@ private: static_assert(0x100 == sizeof(reg_data)); - static const u8 DEVICE_ADDR = 0x53; - static const u8 EXT_DEVICE_ADDR = 0x52; + static const u8 INACTIVE_DEVICE_ADDR = 0x53; + static const u8 ACTIVE_DEVICE_ADDR = 0x52; - bool IsActive() const { return reg_data.activated; } + bool IsActive() const { return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; } u8 GetPassthroughMode() const { return reg_data.ext_identifier[4]; } - // Return the status of the "device detect" pin - // used to product status reports on device change - bool GetDevicePresent() const + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override { if (IsActive()) { - return true; + // TODO: does mplus respond to reads on 0xa6 when activated? + if (ACTIVE_DEVICE_ADDR == slave_addr || INACTIVE_DEVICE_ADDR == slave_addr) + return RawRead(®_data, addr, count, data_out); + else + return 0; } else { - // TODO: passthrough other extension attachment status - return false; + if (INACTIVE_DEVICE_ADDR == slave_addr) + return RawRead(®_data, addr, count, data_out); + else + { + // Passthrough to the connected extension (if any) + return i2c_bus.BusRead(slave_addr, addr, count, data_out); + } } } - int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override - { - // if (DEVICE_ADDR != slave_addr) - // return 0; - - return i2c_bus.BusRead(slave_addr, addr, count, data_out); - - auto const result = RawRead(®_data, addr, count, data_out); - - return result; - } - int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override { - // if (DEVICE_ADDR != slave_addr) - // return 0; - - return i2c_bus.BusWrite(slave_addr, addr, count, data_in); - - auto const result = RawWrite(®_data, addr, count, data_in); - - if (0xfe == addr) + if (IsActive()) { - if (true) // 0x55 == reg_data.activated) + // TODO: does mplus respond to reads on 0xa6 when activated? + if (ACTIVE_DEVICE_ADDR == slave_addr || INACTIVE_DEVICE_ADDR == slave_addr) { - // i2c_bus.SetSlave(0x52, this); - // i2c_bus.RemoveSlave(0x53); + auto const result = RawWrite(®_data, addr, count, data_in); + return result; + + // TODO: Should any write (of any value) trigger activation? + if (0xf0 == addr) + { + // Deactivate motion plus: + reg_data.ext_identifier[2] = INACTIVE_DEVICE_ADDR << 1; + } + } + else + return 0; + } + else + { + if (INACTIVE_DEVICE_ADDR == slave_addr) + { + auto const result = RawWrite(®_data, addr, count, data_in); + + // TODO: Should any write (of any value) trigger activation? + if (0xfe == addr) + { + // Activate motion plus: + reg_data.ext_identifier[2] = ACTIVE_DEVICE_ADDR << 1; + } + + return result; + } + else + { + // Passthrough to the connected extension (if any) + return i2c_bus.BusWrite(slave_addr, addr, count, data_in); } } - - return result; } + private: bool ReadDeviceDetectPin() override { - return true; + if (IsActive()) + { + // TODO: logic for when motion plus deactivates + return true; + } + else + { + return extension_port.IsDeviceConnected(); + } } - - } m_motion_plus_logic; + } + m_motion_plus_logic; void ReportMode(const wm_report_mode* dr); void SendAck(u8 report_id, u8 error_code = 0x0); From a0721b256f3c54169be9641575f1322140f37c75 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 24 Nov 2018 13:09:32 -0600 Subject: [PATCH 09/22] WiimoteEmu: Cleanup, kill some magic numbers, and unbreak wiimote speaker pan setting. --- .../Core/Core/HW/WiimoteCommon/WiimoteHid.h | 21 ++++++ .../Core/HW/WiimoteCommon/WiimoteReport.h | 6 +- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 65 +++++++++++-------- Source/Core/Core/HW/WiimoteEmu/Speaker.cpp | 21 +++--- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 32 +++++---- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 34 ++++++---- 6 files changed, 117 insertions(+), 62 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h index 09673b5f8a..a467d9017b 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h @@ -22,6 +22,27 @@ struct hid_packet u8 data[0]; }; +template +struct TypedHidPacket +{ + u8 param : 4; + u8 type : 4; + + // TODO: auto set this from the T-type: + u8 report_id; + + T data; + + static_assert(std::is_pod::value); + + const u8* GetData() const { return reinterpret_cast(this); } + u32 GetSize() const + { + static_assert(sizeof(*this) == sizeof(T) + 2); + return sizeof(*this); + } +}; + constexpr u8 HID_TYPE_HANDSHAKE = 0; constexpr u8 HID_TYPE_SET_REPORT = 5; constexpr u8 HID_TYPE_DATA = 0xA; diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h index 7845864f1f..1bdeb0a15b 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h @@ -32,6 +32,10 @@ enum ReportType RT_STATUS_REPORT = 0x20, RT_READ_DATA_REPLY = 0x21, RT_ACK_DATA = 0x22, + + // Not a real value on the wiimote, just a state to disable reports: + RT_REPORT_DISABLED = 0x00, + RT_REPORT_CORE = 0x30, RT_REPORT_CORE_ACCEL = 0x31, RT_REPORT_CORE_EXT8 = 0x32, @@ -40,7 +44,7 @@ enum ReportType RT_REPORT_CORE_ACCEL_EXT16 = 0x35, RT_REPORT_CORE_IR10_EXT9 = 0x36, RT_REPORT_CORE_ACCEL_IR10_EXT6 = 0x37, - RT_REPORT_EXT21 = 0x3d, // never used? + RT_REPORT_EXT21 = 0x3d, RT_REPORT_INTERLEAVE1 = 0x3e, RT_REPORT_INTERLEAVE2 = 0x3f }; diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 5158c59bbf..38f10babec 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -39,6 +39,21 @@ namespace WiimoteEmu { void Wiimote::ReportMode(const wm_report_mode* const dr) { + if (dr->mode < RT_REPORT_CORE || dr->mode > RT_REPORT_INTERLEAVE2 || + (dr->mode > RT_REPORT_CORE_ACCEL_IR10_EXT6 && dr->mode < RT_REPORT_EXT21)) + { + // A real wiimote ignores the entire message if the mode is invalid. + WARN_LOG(WIIMOTE, "Game requested invalid report mode: 0x%02x", dr->mode); + return; + } + else if (dr->mode > RT_REPORT_CORE_ACCEL_IR10_EXT6) + { + PanicAlert("Wiimote: Unsupported Reporting mode: 0x%02x", dr->mode); + } + + // TODO: A real wiimote sends a report immediately. + // even on REPORT_CORE and continuous off when the buttons haven't changed. + // INFO_LOG(WIIMOTE, "Set data report mode"); // DEBUG_LOG(WIIMOTE, " Rumble: %x", dr->rumble); // DEBUG_LOG(WIIMOTE, " Continuous: %x", dr->continuous); @@ -49,15 +64,6 @@ void Wiimote::ReportMode(const wm_report_mode* const dr) // m_reporting_auto = dr->all_the_time; m_reporting_auto = dr->continuous; m_reporting_mode = dr->mode; - // m_reporting_channel = _channelID; // this is set in every Interrupt/Control Channel now - - // reset IR camera - // memset(m_reg_ir, 0, sizeof(*m_reg_ir)); //ugly hack - - if (dr->mode > 0x37) - PanicAlert("Wiimote: Unsupported Reporting mode."); - else if (dr->mode < RT_REPORT_CORE) - PanicAlert("Wiimote: Reporting mode < 0x30."); } /* Here we process the Output Reports that the Wii sends. Our response will be @@ -100,6 +106,7 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) case RT_IR_PIXEL_CLOCK: // 0x13 // INFO_LOG(WIIMOTE, "WM IR Clock: 0x%02x", sr->data[0]); + // Camera data is currently always updated. Ignoring pixel clock status. // m_ir_clock = sr->enable; if (false == sr->ack) return; @@ -135,7 +142,7 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) auto sd = reinterpret_cast(sr->data); if (sd->length > 20) PanicAlert("EmuWiimote: bad speaker data length!"); - m_i2c_bus.BusWrite(0x51, 0x00, sd->length, sd->data); + SpeakerData(sd->data, sd->length); } return; // no ack break; @@ -147,6 +154,7 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) break; case RT_IR_LOGIC: // 0x1a + // Camera data is currently always updated. Just saving this for status requests. m_status.ir = sr->enable; if (false == sr->ack) return; @@ -168,18 +176,19 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) The last byte is the success code 00. */ void Wiimote::SendAck(u8 report_id, u8 error_code) { - u8 data[6]; + TypedHidPacket rpt; + rpt.type = HID_TYPE_DATA; + rpt.param = HID_PARAM_INPUT; + rpt.report_id = RT_ACK_DATA; - data[0] = 0xA1; - data[1] = RT_ACK_DATA; - - wm_acknowledge* ack = reinterpret_cast(data + 2); + auto ack = &rpt.data; ack->buttons = m_status.buttons; ack->reportID = report_id; ack->errorID = error_code; - Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data)); + Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), + rpt.GetSize()); } void Wiimote::HandleExtensionSwap() @@ -218,15 +227,17 @@ void Wiimote::RequestStatus(const wm_request_status* const rs) m_status.battery_low = m_status.battery < 0x33; // set up report - u8 data[8]; - data[0] = 0xA1; - data[1] = RT_STATUS_REPORT; + TypedHidPacket rpt; + rpt.type = HID_TYPE_DATA; + rpt.param = HID_PARAM_INPUT; + rpt.report_id = RT_STATUS_REPORT; // status values - *reinterpret_cast(data + 2) = m_status; + rpt.data = m_status; // send report - Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data)); + Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), + rpt.GetSize()); } /* Write data to Wiimote and Extensions registers. */ @@ -337,11 +348,12 @@ bool Wiimote::ProcessReadDataRequest() return false; } - u8 data[23] = {}; - data[0] = 0xA1; - data[1] = RT_READ_DATA_REPLY; + TypedHidPacket rpt; + rpt.type = HID_TYPE_DATA; + rpt.param = HID_PARAM_INPUT; + rpt.report_id = RT_READ_DATA_REPLY; - wm_read_data_reply* const reply = reinterpret_cast(data + 2); + auto reply = &rpt.data; reply->buttons = m_status.buttons; reply->address = Common::swap16(m_read_request.address); @@ -417,7 +429,8 @@ bool Wiimote::ProcessReadDataRequest() m_read_request.size -= bytes_to_read; // Send the data - Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data)); + Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), + rpt.GetSize()); return true; } diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp index 257f82741c..2f81af1a0f 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp @@ -68,11 +68,11 @@ void stopdamnwav() } #endif -void Wiimote::SpeakerLogic::SpeakerData(const u8* data, int length) +void Wiimote::SpeakerData(const u8* data, int length) { if (!SConfig::GetInstance().m_WiimoteEnableSpeaker) return; - if (reg_data.volume == 0 || reg_data.sample_rate == 0 || + if (m_speaker_logic.reg_data.volume == 0 || m_speaker_logic.reg_data.sample_rate == 0 || length == 0) return; @@ -82,7 +82,7 @@ void Wiimote::SpeakerLogic::SpeakerData(const u8* data, int length) unsigned int sample_rate_dividend, sample_length; u8 volume_divisor; - if (reg_data.format == 0x40) + if (m_speaker_logic.reg_data.format == SpeakerLogic::DATA_FORMAT_PCM) { // 8 bit PCM for (int i = 0; i < length; ++i) @@ -95,13 +95,14 @@ void Wiimote::SpeakerLogic::SpeakerData(const u8* data, int length) volume_divisor = 0xff; sample_length = (unsigned int)length; } - else if (reg_data.format == 0x00) + else if (m_speaker_logic.reg_data.format == SpeakerLogic::DATA_FORMAT_ADPCM) { // 4 bit Yamaha ADPCM (same as dreamcast) for (int i = 0; i < length; ++i) { - 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); + samples[i * 2] = + adpcm_yamaha_expand_nibble(m_speaker_logic.adpcm_state, (data[i] >> 4) & 0xf); + samples[i * 2 + 1] = adpcm_yamaha_expand_nibble(m_speaker_logic.adpcm_state, data[i] & 0xf); } // Following details from http://wiibrew.org/wiki/Wiimote#Speaker @@ -114,16 +115,16 @@ void Wiimote::SpeakerLogic::SpeakerData(const u8* data, int length) } else { - ERROR_LOG(IOS_WIIMOTE, "Unknown speaker format %x", reg_data.format); + ERROR_LOG(IOS_WIIMOTE, "Unknown speaker format %x", m_speaker_logic.reg_data.format); return; } // Speaker Pan // TODO: fix - unsigned int vol = 0;//(unsigned int)(m_options->numeric_settings[0]->GetValue() * 100); + unsigned int vol = (unsigned int)(m_options->numeric_settings[0]->GetValue() * 100); - unsigned int sample_rate = sample_rate_dividend / reg_data.sample_rate; - float speaker_volume_ratio = (float)reg_data.volume / volume_divisor; + unsigned int sample_rate = sample_rate_dividend / m_speaker_logic.reg_data.sample_rate; + float speaker_volume_ratio = (float)m_speaker_logic.reg_data.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); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index af98f7ec45..cb80af1f4a 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -307,7 +307,7 @@ static const char* const named_buttons[] = { void Wiimote::Reset() { - m_reporting_mode = RT_REPORT_CORE; + m_reporting_mode = RT_REPORT_DISABLED; m_reporting_channel = 0; m_reporting_auto = false; @@ -566,8 +566,7 @@ bool Wiimote::Step() // WiiBrew: Following a connection or disconnection event on the Extension Port, // data reporting is disabled and the Data Reporting Mode must be reset before new data can // arrive. - m_reporting_mode = RT_REPORT_CORE; - m_reporting_auto = false; + m_reporting_mode = RT_REPORT_DISABLED; RequestStatus(); @@ -853,6 +852,13 @@ void Wiimote::Update() Movie::SetPolledDevice(); + if (RT_REPORT_DISABLED == m_reporting_mode) + { + // The wiimote is in this disabled state on boot and after an extension change. + // Input reports are not sent, even on button change. + return; + } + const ReportFeatures& rptf = reporting_mode_features[m_reporting_mode - RT_REPORT_CORE]; s8 rptf_size = rptf.total_size; if (Movie::IsPlayingInput() && @@ -863,7 +869,7 @@ void Wiimote::Update() } else { - data[0] = 0xA1; + data[0] = HID_TYPE_DATA << 4 | HID_PARAM_INPUT; data[1] = m_reporting_mode; const auto lock = GetStateLock(); @@ -895,25 +901,25 @@ void Wiimote::Update() UpdateIRData(rptf.accel_size != 0); if (rptf.ir_size) { - // TODO: kill magic numbers - m_i2c_bus.BusRead(0x58, 0x37, rptf.ir_size, feature_ptr); + m_i2c_bus.BusRead(IRCameraLogic::DEVICE_ADDR, offsetof(IRCameraLogic::RegData, camera_data), + rptf.ir_size, feature_ptr); feature_ptr += rptf.ir_size; } + // motion plus + auto* mplus_data = + reinterpret_cast(m_motion_plus_logic.reg_data.controller_data); + *mplus_data = wm_motionplus_data(); + mplus_data->is_mp_data = true; + // extension UpdateExtData(); if (rptf.ext_size) { - // TODO: kill magic numbers - m_i2c_bus.BusRead(0x52, 0x00, rptf.ext_size, feature_ptr); + m_i2c_bus.BusRead(ExtensionLogic::DEVICE_ADDR, 0x00, rptf.ext_size, feature_ptr); feature_ptr += rptf.ext_size; } - // motion plus - auto* mplus_data = reinterpret_cast(m_motion_plus_logic.reg_data.controller_data); - *mplus_data = wm_motionplus_data(); - mplus_data->is_mp_data = true; - if (feature_ptr != data + rptf_size) { PanicAlert("Wiimote input report is the wrong size!"); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index b44a6f81a5..4b46e2ccbd 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -6,6 +6,7 @@ #include #include +#include #include "Common/Logging/Log.h" #include "Core/HW/WiimoteCommon/WiimoteHid.h" @@ -215,7 +216,6 @@ enum class I2CSlave { public: - // Kill MSVC warning: virtual ~I2CSlave() = default; virtual int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) = 0; @@ -366,6 +366,7 @@ public: void Update(); void InterruptChannel(u16 channel_id, const void* data, u32 size); void ControlChannel(u16 channel_id, const void* data, u32 size); + void SpeakerData(const u8* data, int length); bool CheckForButtonPress(); void Reset(); @@ -394,7 +395,7 @@ private: struct IRCameraLogic : public I2CSlave { - struct + struct RegData { // Contains sensitivity and other unknown data u8 data[0x33]; @@ -435,9 +436,9 @@ private: ControllerEmu::Extension* extension; - private: static const u8 DEVICE_ADDR = 0x52; + private: int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override { if (DEVICE_ADDR != slave_addr) @@ -476,6 +477,9 @@ private: struct SpeakerLogic : public I2CSlave { + static const u8 DATA_FORMAT_ADPCM = 0x00; + static const u8 DATA_FORMAT_PCM = 0x40; + struct { // Speaker reports result in a write of samples to addr 0x00 (which also plays sound) @@ -499,8 +503,6 @@ private: static const u8 DEVICE_ADDR = 0x51; - void SpeakerData(const u8* data, int length); - int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override { if (DEVICE_ADDR != slave_addr) @@ -516,7 +518,7 @@ private: if (0x00 == addr) { - SpeakerData(data_in, count); + ERROR_LOG(WIIMOTE, "Writing of speaker data to address 0x00 is unimplemented!"); return count; } else @@ -564,12 +566,19 @@ private: u8 GetPassthroughMode() const { return reg_data.ext_identifier[4]; } + // TODO: when activated it seems the motion plus continuously reads from the ext port even when not in passthrough mode + // 16 bytes it seems + + // It also sends the 0x55 to 0xf0 activation + // It also writes 0x00 to slave:0x52 addr:0xfa for some reason + // And starts a write to 0xfa but never writes bytes.. + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override { if (IsActive()) { - // TODO: does mplus respond to reads on 0xa6 when activated? - if (ACTIVE_DEVICE_ADDR == slave_addr || INACTIVE_DEVICE_ADDR == slave_addr) + // Motion plus does not respond to 0x53 when activated + if (ACTIVE_DEVICE_ADDR == slave_addr) return RawRead(®_data, addr, count, data_out); else return 0; @@ -590,8 +599,8 @@ private: { if (IsActive()) { - // TODO: does mplus respond to reads on 0xa6 when activated? - if (ACTIVE_DEVICE_ADDR == slave_addr || INACTIVE_DEVICE_ADDR == slave_addr) + // Motion plus does not respond to 0x53 when activated + if (ACTIVE_DEVICE_ADDR == slave_addr) { auto const result = RawWrite(®_data, addr, count, data_in); return result; @@ -604,7 +613,9 @@ private: } } else + { return 0; + } } else { @@ -642,8 +653,7 @@ private: return extension_port.IsDeviceConnected(); } } - } - m_motion_plus_logic; + } m_motion_plus_logic; void ReportMode(const wm_report_mode* dr); void SendAck(u8 report_id, u8 error_code = 0x0); From a25e8cb516289e6f1a04aef791e02afdc4f379e2 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 25 Nov 2018 09:14:06 -0600 Subject: [PATCH 10/22] Advance save state version. --- Source/Core/Core/State.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 8e3020b0da..97c3b020db 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -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, From 42b9392784b5aae840d818c7c4d349c184e6f035 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 25 Nov 2018 11:08:42 -0600 Subject: [PATCH 11/22] WiimoteEmu: Cleanups and work towards making motion plus functional. --- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 5 + Source/Core/Core/HW/WiimoteEmu/MatrixMath.h | 2 + Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 182 ++++++++++++++++-- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 89 +++++++-- 4 files changed, 244 insertions(+), 34 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 38f10babec..3d3a88460c 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -354,6 +354,7 @@ bool Wiimote::ProcessReadDataRequest() rpt.report_id = RT_READ_DATA_REPLY; auto reply = &rpt.data; + reply->error = 0; reply->buttons = m_status.buttons; reply->address = Common::swap16(m_read_request.address); @@ -377,6 +378,7 @@ bool Wiimote::ProcessReadDataRequest() // read the calibration data at the beginning of Eeprom. I think this // error is supposed to occur when we try to read above the freely // usable space that ends at 0x16ff. + INFO_LOG(WIIMOTE, "Responding with read error 8."); reply->error = 0x08; } else @@ -414,6 +416,7 @@ bool Wiimote::ProcessReadDataRequest() if (bytes_read != bytes_to_read) { // generate read error, 7 == no such slave (no ack) + INFO_LOG(WIIMOTE, "Responding with read error 7."); reply->error = 0x07; } } @@ -464,6 +467,8 @@ void Wiimote::DoState(PointerWrap& p) if (p.GetMode() == PointerWrap::MODE_READ) RealState(); + + // TODO: rebuild i2c bus state after state-change } // load real Wiimote state diff --git a/Source/Core/Core/HW/WiimoteEmu/MatrixMath.h b/Source/Core/Core/HW/WiimoteEmu/MatrixMath.h index e4daaad923..c3fa0d498d 100644 --- a/Source/Core/Core/HW/WiimoteEmu/MatrixMath.h +++ b/Source/Core/Core/HW/WiimoteEmu/MatrixMath.h @@ -6,6 +6,8 @@ #include +// TODO: kill this whole file, we have Matrix functions in MathUtil.h + #ifndef M_PI #define M_PI 3.14159265358979323846 #endif diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index cb80af1f4a..a121ced085 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -18,6 +18,7 @@ #include "Common/Config/Config.h" #include "Common/MathUtil.h" #include "Common/MsgHandler.h" +#include "Common/BitUtils.h" #include "Core/Config/SYSCONFSettings.h" #include "Core/Config/WiimoteInputSettings.h" @@ -330,9 +331,18 @@ void Wiimote::Reset() memset(&m_camera_logic.reg_data, 0, sizeof(m_camera_logic.reg_data)); memset(&m_ext_logic.reg_data, 0, sizeof(m_ext_logic.reg_data)); - memset(&m_motion_plus_logic.reg_data, 0, sizeof(m_motion_plus_logic.reg_data)); + memset(&m_motion_plus_logic.reg_data, 0x00, sizeof(m_motion_plus_logic.reg_data)); memcpy(&m_motion_plus_logic.reg_data.ext_identifier, motion_plus_id, sizeof(motion_plus_id)); + // calibration hackery + static const u8 c1[16] = {0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, 0xcf, 0xf0, 0x31, 0xad, + 0xc8, 0x0b, 0x5e, 0x39}; + static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60, 0xc9, 0xf5, 0x37, 0xc1, + 0x2d, 0xe9, 0x15, 0x8d}; + + //std::copy(std::begin(c1), std::end(c1), m_motion_plus_logic.reg_data.calibration_data); + //std::copy(std::begin(c2), std::end(c2), m_motion_plus_logic.reg_data.calibration_data + 0x10); + // status memset(&m_status, 0, sizeof(m_status)); @@ -828,12 +838,6 @@ void Wiimote::UpdateIRData(bool use_accel) } } -void Wiimote::UpdateExtData() -{ - // Write extension data to addr 0x00 of extension register - m_extension->GetState(m_ext_logic.reg_data.controller_data); -} - void Wiimote::Update() { // no channel == not connected i guess @@ -896,26 +900,27 @@ void Wiimote::Update() } // IR Camera - // TODO: kill use_accel param - // TODO: call only if camera logic is enabled? - UpdateIRData(rptf.accel_size != 0); + // TODO: kill use_accel param, I think it exists for TAS reasons.. + if (m_status.ir) + UpdateIRData(rptf.accel_size != 0); + if (rptf.ir_size) { + if (!m_status.ir) + WARN_LOG(WIIMOTE, "Game is reading IR data without enabling IR logic first."); + m_i2c_bus.BusRead(IRCameraLogic::DEVICE_ADDR, offsetof(IRCameraLogic::RegData, camera_data), rptf.ir_size, feature_ptr); feature_ptr += rptf.ir_size; } - // motion plus - auto* mplus_data = - reinterpret_cast(m_motion_plus_logic.reg_data.controller_data); - *mplus_data = wm_motionplus_data(); - mplus_data->is_mp_data = true; - - // extension - UpdateExtData(); + // extension / motion-plus if (rptf.ext_size) { + // Update extension first as motion-plus will read from it. + m_ext_logic.Update(); + m_motion_plus_logic.Update(); + m_i2c_bus.BusRead(ExtensionLogic::DEVICE_ADDR, 0x00, rptf.ext_size, feature_ptr); feature_ptr += rptf.ext_size; } @@ -1115,4 +1120,145 @@ bool Wiimote::ExtensionLogic::ReadDeviceDetectPin() return extension->active_extension ? true : false; } +void Wiimote::ExtensionLogic::Update() +{ + // Update controller data from user input: + // Write data to addr 0x00 of extension register + extension->GetState(reg_data.controller_data); +} + +// TODO: move this to Common if it doesn't exist already? +template +void SetBit(T& value, u32 bit_number, bool bit_value) +{ + bit_value ? value |= (1 << bit_number) : value &= ~(1 << bit_number); +} + +void Wiimote::MotionPlusLogic::Update() +{ + if (!IsActive()) + { + return; + } + + // TODO: clean up this hackery: + // the value seems to increase based on time starting after the first read of 0x00 + if (IsActive() && times_updated_since_activation < 0xff) + { + ++times_updated_since_activation; + + // TODO: wtf is this value actually.. + if (times_updated_since_activation == 9) + reg_data.initialization_status = 0x4; + else if (times_updated_since_activation == 10) + reg_data.initialization_status = 0x8; + else if (times_updated_since_activation == 18) + reg_data.initialization_status = 0xc; + else if (times_updated_since_activation == 53) + reg_data.initialization_status = 0xe; + } + + auto& mplus_data = *reinterpret_cast(reg_data.controller_data); + auto& data = reg_data.controller_data; + + // TODO: make sure a motion plus report is sent first after init + + // On real mplus: + // For some reason the first read seems to have garbage data + // is_mp_data and extension_connected are set, but the data is junk + // it does seem to have some sort of pattern though, byte 5 is always 2 + // something like: d5, b0, 4e, 6e, fc, 2 + // When a passthrough mode is set: + // the second read is valid mplus data, which then triggers a read from the extension + // the third read is finally extension data + // If an extension is not attached the data is always mplus data + // even when passthrough is enabled + + switch (GetPassthroughMode()) + { + case PassthroughMode::PASSTHROUGH_DISABLED: + { + mplus_data.is_mp_data = true; + break; + } + case PassthroughMode::PASSTHROUGH_NUNCHUK: + { + // If we sent mplus data last time now we will try to send ext data. + if (mplus_data.is_mp_data) + { + // The real mplus seems to only ever read 6 bytes from the extension + // bytes after 6 seem to be zero filled + // The real hardware uses these 6 bytes for the next frame, + // but we aren't going to do that + if (6 == i2c_bus.BusRead(ACTIVE_DEVICE_ADDR, 0x00, 6, data)) + { + // Passthrough data modifications via wiibrew.org + // Data passing through drops the least significant bit of the three accelerometer values + // Bit 7 of byte 5 is moved to bit 6 of byte 5, overwriting it + SetBit(data[5], 6, Common::ExtractBit(data[5], 7)); + // Bit 0 of byte 4 is moved to bit 7 of byte 5 + SetBit(data[5], 7, Common::ExtractBit(data[4], 0)); + // Bit 3 of byte 5 is moved to bit 4 of byte 5, overwriting it + SetBit(data[5], 4, Common::ExtractBit(data[5], 3)); + // Bit 1 of byte 5 is moved to bit 3 of byte 5 + SetBit(data[5], 3, Common::ExtractBit(data[5], 1)); + // Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it + SetBit(data[5], 2, Common::ExtractBit(data[5], 0)); + + mplus_data.is_mp_data = false; + } + } + break; + } + case PassthroughMode::PASSTHROUGH_CLASSIC: + { + // If we sent mplus data last time now we will try to send ext data. + if (mplus_data.is_mp_data) + { + if (6 == i2c_bus.BusRead(ACTIVE_DEVICE_ADDR, 0x00, 6, data)) + { + // Passthrough data modifications via wiibrew.org + // Data passing through drops the least significant bit of the axes of the left (or only) joystick + // Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] + // Bits 0 and 1 of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before + SetBit(data[0], 0, Common::ExtractBit(data[5], 0)); + SetBit(data[1], 0, Common::ExtractBit(data[5], 1)); + + mplus_data.is_mp_data = false; + } + } + break; + } + default: + PanicAlert("MotionPlus unknown passthrough-mode %d", GetPassthroughMode()); + break; + } + + // If the above logic determined this should be mp data, update it here + if (mplus_data.is_mp_data) + { + // Wiibrew: "While the Wiimote is still, the values will be about 0x1F7F (8,063)" + u16 yaw_value = 0x1F7F; + u16 roll_value = 0x1F7F; + u16 pitch_value = 0x1F7F; + + mplus_data.yaw_slow = 1; + mplus_data.roll_slow = 1; + mplus_data.pitch_slow = 1; + + // Bits 0-7 + mplus_data.yaw1 = yaw_value & 0xff; + mplus_data.roll1 = roll_value & 0xff; + mplus_data.pitch1 = pitch_value & 0xff; + + // Bits 8-13 + mplus_data.yaw1 = yaw_value >> 8; + mplus_data.roll1 = roll_value >> 8; + mplus_data.pitch1 = pitch_value >> 8; + } + + mplus_data.extension_connected = extension_port.IsDeviceConnected(); + mplus_data.zero = 0; +} + } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 4b46e2ccbd..cd6a283d1b 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -4,9 +4,9 @@ #pragma once +#include #include #include -#include #include "Common/Logging/Log.h" #include "Core/HW/WiimoteCommon/WiimoteHid.h" @@ -266,9 +266,10 @@ public: void Reset() { m_slaves.clear(); } + // TODO: change int to u16 or something int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) { - INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); + // INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); for (auto& slave : m_slaves) { auto const bytes_read = slave->BusRead(slave_addr, addr, count, data_out); @@ -281,9 +282,13 @@ public: return 0; } + // TODO: change int to u16 or something int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) { - INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); + // TODO: write in blocks of 6 to simulate the real bus + // this might trigger activation writes more accurately + + // INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); for (auto& slave : m_slaves) { auto const bytes_written = slave->BusWrite(slave_addr, addr, count, data_in); @@ -386,7 +391,6 @@ protected: void GetButtonData(u8* data); void GetAccelData(u8* data); void UpdateIRData(bool use_accel); - void UpdateExtData(); private: I2CBus m_i2c_bus; @@ -438,18 +442,35 @@ private: static const u8 DEVICE_ADDR = 0x52; + void Update(); + private: int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override { if (DEVICE_ADDR != slave_addr) return 0; + if (0x00 == addr) + { + // Here we should sample user input and update controller data + // Moved into Update() function for TAS determinism + // TAS code fails to sync data reads and such.. + // extension->GetState(reg_data.controller_data); + } + auto const result = RawRead(®_data, addr, count, data_out); // Encrypt data read from extension register // Check if encrypted reads is on if (0xaa == reg_data.encryption) + { + INFO_LOG(WIIMOTE, "Encrypted read."); WiimoteEncrypt(&ext_key, data_out, addr, (u8)count); + } + else + { + INFO_LOG(WIIMOTE, "Unencrypted read."); + } return result; } @@ -538,19 +559,30 @@ private: // The port on the end of the motion plus: ExtensionPort extension_port{i2c_bus}; + void Update(); + #pragma pack(push, 1) struct MotionPlusRegister { - u8 controller_data[0x10]; - u8 unknown[0x10]; + u8 controller_data[21]; + u8 unknown[11]; + + // address 0x20 u8 calibration_data[0x20]; u8 unknown2[0xb0]; // address 0xF0 - // TODO: bad name - u8 activated; + u8 initialized; - u8 unknown3[9]; + u8 unknown3[6]; + + // address 0xf7 + // Wii Sports Resort reads regularly and claims mplus is disconnected if not to its liking + // Value starts at 0x00 and goes up after activation (not initialization) + // Immediately returns 0x02, even still after 15 and 30 seconds + u8 initialization_status; + + u8 unknown4[2]; // address 0xFA u8 ext_identifier[6]; @@ -559,19 +591,35 @@ private: static_assert(0x100 == sizeof(reg_data)); + private: static const u8 INACTIVE_DEVICE_ADDR = 0x53; static const u8 ACTIVE_DEVICE_ADDR = 0x52; + enum class PassthroughMode : u8 + { + PASSTHROUGH_DISABLED = 0x04, + PASSTHROUGH_NUNCHUK = 0x05, + PASSTHROUGH_CLASSIC = 0x07, + }; + + // TODO: savestate + u8 times_updated_since_activation = 0; + bool IsActive() const { return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; } - u8 GetPassthroughMode() const { return reg_data.ext_identifier[4]; } + PassthroughMode GetPassthroughMode() const + { + return static_cast(reg_data.ext_identifier[4]); + } - // TODO: when activated it seems the motion plus continuously reads from the ext port even when not in passthrough mode - // 16 bytes it seems - - // It also sends the 0x55 to 0xf0 activation + // 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 { @@ -605,15 +653,17 @@ private: auto const result = RawWrite(®_data, addr, count, data_in); return result; - // TODO: Should any write (of any value) trigger activation? + // It seems a write of any value triggers deactivation. if (0xf0 == addr) { // Deactivate motion plus: reg_data.ext_identifier[2] = INACTIVE_DEVICE_ADDR << 1; + times_updated_since_activation = 0; } } else { + // No i2c passthrough when activated. return 0; } } @@ -623,11 +673,18 @@ private: { auto const result = RawWrite(®_data, addr, count, data_in); - // TODO: Should any write (of any value) trigger activation? + // It seems a write of any value triggers activation. if (0xfe == addr) { + INFO_LOG(WIIMOTE, "Motion Plus has been activated with value: %d", data_in[0]); + // Activate motion plus: reg_data.ext_identifier[2] = ACTIVE_DEVICE_ADDR << 1; + times_updated_since_activation = 0x2; + + // Test some hax + std::array data = {0x55}; + i2c_bus.BusWrite(ACTIVE_DEVICE_ADDR, 0xf0, (int)data.size(), data.data()); } return result; From 10c2101e72de7f764f183ebd33fad0125550c92c Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 25 Nov 2018 16:24:55 -0600 Subject: [PATCH 12/22] WiimoteReal: Unbreak. --- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 9 ++------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index a121ced085..ac039daa15 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -966,7 +966,6 @@ void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size) return; } - // this all good? m_reporting_channel = channel_id; const hid_packet* hidp = reinterpret_cast(data); @@ -1008,7 +1007,6 @@ void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size) void Wiimote::InterruptChannel(const u16 channel_id, const void* data, u32 size) { - // this all good? m_reporting_channel = channel_id; const hid_packet* hidp = reinterpret_cast(data); @@ -1019,11 +1017,8 @@ void Wiimote::InterruptChannel(const u16 channel_id, const void* data, u32 size) switch (hidp->param) { case HID_PARAM_OUTPUT: - { - const wm_report* sr = reinterpret_cast(hidp->data); - HidOutputReport(sr); - } - break; + HidOutputReport(reinterpret_cast(hidp->data)); + break; default: PanicAlert("HidInput: HID_TYPE_DATA - param 0x%02x", hidp->param); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index cd6a283d1b..97c4fbd327 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -680,9 +680,9 @@ private: // Activate motion plus: reg_data.ext_identifier[2] = ACTIVE_DEVICE_ADDR << 1; - times_updated_since_activation = 0x2; + reg_data.initialization_status = 0x2; - // Test some hax + // Some hax to disable encryption: std::array data = {0x55}; i2c_bus.BusWrite(ACTIVE_DEVICE_ADDR, 0xf0, (int)data.size(), data.data()); } From 0b4329e077c6c2c376a5fd83e1009361df109233 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 25 Nov 2018 18:28:32 -0600 Subject: [PATCH 13/22] Implement the rest of the wiimote input reports. --- .../Core/HW/WiimoteCommon/WiimoteReport.h | 22 ++- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 4 - Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 174 ++++++++++++------ Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 11 +- Source/Core/Core/Movie.cpp | 3 +- .../Core/DolphinQt/TAS/WiiTASInputWindow.cpp | 16 +- 6 files changed, 160 insertions(+), 70 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h index 1bdeb0a15b..5a3cd89ee0 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h @@ -63,7 +63,7 @@ union wm_buttons // also just called "core data" u8 down : 1; u8 up : 1; u8 plus : 1; - u8 acc_x_lsb : 2; // LSB of accelerometer (10 bits in total) + u8 acc_bits : 2; u8 unknown : 1; u8 two : 1; @@ -71,8 +71,7 @@ union wm_buttons // also just called "core data" u8 b : 1; u8 a : 1; u8 minus : 1; - u8 acc_y_lsb : 1; // LSB of accelerometer (9 bits in total) - u8 acc_z_lsb : 1; // LSB of accelerometer (9 bits in total) + u8 acc_bits2 : 2; u8 home : 1; }; }; @@ -109,6 +108,23 @@ struct wm_ir_extended }; static_assert(sizeof(wm_ir_extended) == 3, "Wrong size"); +// Nine bytes for one object +// first 3 bytes are the same as extended +struct wm_ir_full : wm_ir_extended +{ + u8 xmin : 7; + u8 : 1; + u8 ymin : 7; + u8 : 1; + u8 xmax : 7; + u8 : 1; + u8 ymax : 7; + u8 : 1; + u8 zero; + u8 intensity; +}; +static_assert(sizeof(wm_ir_full) == 9, "Wrong size"); + // Nunchuk union wm_nc_core { diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 3d3a88460c..9275eaeb46 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -46,10 +46,6 @@ void Wiimote::ReportMode(const wm_report_mode* const dr) WARN_LOG(WIIMOTE, "Game requested invalid report mode: 0x%02x", dr->mode); return; } - else if (dr->mode > RT_REPORT_CORE_ACCEL_IR10_EXT6) - { - PanicAlert("Wiimote: Unsupported Reporting mode: 0x%02x", dr->mode); - } // TODO: A real wiimote sends a report immediately. // even on REPORT_CORE and continuous off when the buttons haven't changed. diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index ac039daa15..461a5aef55 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -13,12 +13,12 @@ #include #include +#include "Common/BitUtils.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/Config/Config.h" #include "Common/MathUtil.h" #include "Common/MsgHandler.h" -#include "Common/BitUtils.h" #include "Core/Config/SYSCONFSettings.h" #include "Core/Config/WiimoteInputSettings.h" @@ -101,11 +101,16 @@ static const ReportFeatures reporting_mode_features[] = { {2, 0, 10, 9, 23}, // 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes {2, 3, 10, 6, 23}, - // UNSUPPORTED (but should be easy enough to implement): + // Ugly padding members so 0x3d,3e,3f are properly placed in the array: + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, // 0x3d: 21 Extension Bytes {0, 0, 0, 21, 23}, // 0x3e / 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes - {0, 0, 0, 0, 23}, + {2, 1, 0, 18, 23}, }; // Used for extension calibration data: @@ -335,13 +340,13 @@ void Wiimote::Reset() memcpy(&m_motion_plus_logic.reg_data.ext_identifier, motion_plus_id, sizeof(motion_plus_id)); // calibration hackery - static const u8 c1[16] = {0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, 0xcf, 0xf0, 0x31, 0xad, - 0xc8, 0x0b, 0x5e, 0x39}; - static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60, 0xc9, 0xf5, 0x37, 0xc1, - 0x2d, 0xe9, 0x15, 0x8d}; + static const u8 c1[16] = {0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, + 0xcf, 0xf0, 0x31, 0xad, 0xc8, 0x0b, 0x5e, 0x39}; + static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60, + 0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d}; - //std::copy(std::begin(c1), std::end(c1), m_motion_plus_logic.reg_data.calibration_data); - //std::copy(std::begin(c2), std::end(c2), m_motion_plus_logic.reg_data.calibration_data + 0x10); + // std::copy(std::begin(c1), std::end(c1), m_motion_plus_logic.reg_data.calibration_data); + // std::copy(std::begin(c2), std::end(c2), m_motion_plus_logic.reg_data.calibration_data + 0x10); // status memset(&m_status, 0, sizeof(m_status)); @@ -610,7 +615,8 @@ void Wiimote::GetButtonData(u8* const data) reinterpret_cast(data)->hex |= m_status.buttons.hex; } -void Wiimote::GetAccelData(u8* const data) +// Produces 10-bit precision values. +void Wiimote::GetAccelData(s16* x, s16* y, s16* z) { const bool sideways_modifier_toggle = m_hotkeys->getSettingsModifier()[0]; const bool upright_modifier_toggle = m_hotkeys->getSettingsModifier()[1]; @@ -661,26 +667,14 @@ void Wiimote::GetAccelData(u8* const data) EmulateDynamicShake(&m_accel, m_shake_dynamic_data, m_shake_dynamic, shake_config, m_shake_dynamic_step.data()); - // TODO: kill these ugly looking offsets - wm_accel& accel = *reinterpret_cast(data + 4); - wm_buttons& core = *reinterpret_cast(data + 2); - // We now use 2 bits more precision, so multiply by 4 before converting to int - s16 x = (s16)(4 * (m_accel.x * ACCEL_RANGE + ACCEL_ZERO_G)); - s16 y = (s16)(4 * (m_accel.y * ACCEL_RANGE + ACCEL_ZERO_G)); - s16 z = (s16)(4 * (m_accel.z * ACCEL_RANGE + ACCEL_ZERO_G)); + *x = (s16)(4 * (m_accel.x * ACCEL_RANGE + ACCEL_ZERO_G)); + *y = (s16)(4 * (m_accel.y * ACCEL_RANGE + ACCEL_ZERO_G)); + *z = (s16)(4 * (m_accel.z * ACCEL_RANGE + ACCEL_ZERO_G)); - x = MathUtil::Clamp(x, 0, 0x3ff); - y = MathUtil::Clamp(y, 0, 0x3ff); - z = MathUtil::Clamp(z, 0, 0x3ff); - - accel.x = (x >> 2) & 0xFF; - accel.y = (y >> 2) & 0xFF; - accel.z = (z >> 2) & 0xFF; - - core.acc_x_lsb = x & 0x3; - core.acc_y_lsb = (y >> 1) & 0x1; - core.acc_z_lsb = (z >> 1) & 0x1; + *x = MathUtil::Clamp(*x, 0, 0x3ff); + *y = MathUtil::Clamp(*y, 0, 0x3ff); + *z = MathUtil::Clamp(*z, 0, 0x3ff); } inline void LowPassFilter(double& var, double newval, double period) @@ -694,9 +688,6 @@ inline void LowPassFilter(double& var, double newval, double period) void Wiimote::UpdateIRData(bool use_accel) { - // IR data is stored at offset 0x37 - u8* const data = m_camera_logic.reg_data.camera_data; - u16 x[4], y[4]; memset(x, 0xFF, sizeof(x)); @@ -781,15 +772,20 @@ void Wiimote::UpdateIRData(bool use_accel) y[i] = static_cast(lround((v[i].y + 1) / 2 * (camHeight - 1))); } + // IR data is read from offset 0x37 on real hardware + u8* const data = m_camera_logic.reg_data.camera_data; + // A maximum of 36 bytes. + std::fill_n(data, sizeof(wm_ir_full) * 4, 0xff); + // Fill report with valid data when full handshake was done if (m_camera_logic.reg_data.data[0x30]) + { // ir mode switch (m_camera_logic.reg_data.mode) { // basic case 1: { - memset(data, 0xFF, 10); wm_ir_basic* const irdata = reinterpret_cast(data); for (unsigned int i = 0; i < 2; ++i) { @@ -810,12 +806,11 @@ void Wiimote::UpdateIRData(bool use_accel) irdata[i].y2hi = y[i * 2 + 1] >> 8; } } + break; } - break; // extended case 3: { - memset(data, 0xFF, 12); wm_ir_extended* const irdata = reinterpret_cast(data); for (unsigned int i = 0; i < 4; ++i) if (x[i] < 1024 && y[i] < 768) @@ -828,14 +823,39 @@ void Wiimote::UpdateIRData(bool use_accel) irdata[i].size = 10; } - } - break; - // full - case 5: - PanicAlert("Full IR report"); - // UNSUPPORTED break; } + // full + case 5: + { + wm_ir_full* const irdata = reinterpret_cast(data); + for (unsigned int i = 0; i < 4; ++i) + if (x[i] < 1024 && y[i] < 768) + { + irdata[i].x = static_cast(x[i]); + irdata[i].xhi = x[i] >> 8; + + irdata[i].y = static_cast(y[i]); + irdata[i].yhi = y[i] >> 8; + + irdata[i].size = 10; + + // TODO: implement these sensibly: + // TODO: do high bits of x/y min/max need to be set to zero? + irdata[i].xmin = 0; + irdata[i].ymin = 0; + irdata[i].xmax = 0; + irdata[i].ymax = 0; + irdata[i].zero = 0; + irdata[i].intensity = 0; + } + break; + } + default: + WARN_LOG(WIIMOTE, "Game is requesting IR data before setting IR mode."); + break; + } + } } void Wiimote::Update() @@ -863,13 +883,21 @@ void Wiimote::Update() return; } + if (RT_REPORT_CORE == m_reporting_mode && !m_reporting_auto) + { + // TODO: we only need to send a report if the data changed when reporting_auto is disabled + // probably only sensible to check this with RT_REPORT_CORE + // and just always send a report with the other modes + // return; + } + const ReportFeatures& rptf = reporting_mode_features[m_reporting_mode - RT_REPORT_CORE]; s8 rptf_size = rptf.total_size; if (Movie::IsPlayingInput() && Movie::PlayWiimote(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key)) { if (rptf.core_size) - m_status.buttons = *reinterpret_cast(data + 2); + m_status.buttons = *reinterpret_cast(data + rptf.GetCoreOffset()); } else { @@ -881,8 +909,7 @@ void Wiimote::Update() // hotkey/settings modifier m_hotkeys->GetState(); // data is later accessed in UpdateButtonsStatus and GetAccelData - // Data starts at byte 2 in the report - u8* feature_ptr = data + 2; + u8* feature_ptr = data + rptf.GetCoreOffset(); // core buttons if (rptf.core_size) @@ -895,7 +922,38 @@ void Wiimote::Update() if (rptf.accel_size) { // TODO: GetAccelData has hardcoded payload offsets.. - GetAccelData(data); + s16 x, y, z; + GetAccelData(&x, &y, &z); + + auto& core = *reinterpret_cast(data + rptf.GetCoreOffset()); + + // Special cases for the interleaved reports: + // only 8 bits of precision. + if (RT_REPORT_INTERLEAVE1 == m_reporting_mode) + { + *feature_ptr = (x >> 2) & 0xff; + core.acc_bits = (z >> 4) & 0b11; + core.acc_bits2 = (z >> 6) & 0b11; + } + if (RT_REPORT_INTERLEAVE2 == m_reporting_mode) + { + *feature_ptr = (y >> 2) & 0xff; + core.acc_bits = (z >> 0) & 0b11; + core.acc_bits2 = (z >> 2) & 0b11; + } + else + { + auto& accel = *reinterpret_cast(feature_ptr); + accel.x = (x >> 2) & 0xFF; + accel.y = (y >> 2) & 0xFF; + accel.z = (z >> 2) & 0xFF; + + // Set the LSBs + core.acc_bits = x & 0b11; + core.acc_bits2 = (y >> 1) & 0x1; + core.acc_bits2 |= ((z >> 1) & 0x1) << 1; + } + feature_ptr += rptf.accel_size; } @@ -909,8 +967,16 @@ void Wiimote::Update() if (!m_status.ir) WARN_LOG(WIIMOTE, "Game is reading IR data without enabling IR logic first."); - m_i2c_bus.BusRead(IRCameraLogic::DEVICE_ADDR, offsetof(IRCameraLogic::RegData, camera_data), - rptf.ir_size, feature_ptr); + u8 camera_data_offset = offsetof(IRCameraLogic::RegData, camera_data); + + if (RT_REPORT_INTERLEAVE2 == m_reporting_mode) + { + // Interleave2 reads the 2nd half of the "FULL" IR data + camera_data_offset += sizeof(wm_ir_full) * 2; + } + + m_i2c_bus.BusRead(IRCameraLogic::DEVICE_ADDR, camera_data_offset, rptf.ir_size, feature_ptr); + feature_ptr += rptf.ir_size; } @@ -944,15 +1010,17 @@ void Wiimote::Update() Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key); - // don't send a data report if auto reporting is off - if (false == m_reporting_auto && data[1] >= RT_REPORT_CORE) - return; - // send data report if (rptf_size) { Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, rptf_size); } + + // The interleaved reporting modes toggle back and forth: + if (RT_REPORT_INTERLEAVE1 == m_reporting_mode) + m_reporting_mode = RT_REPORT_INTERLEAVE2; + else if (RT_REPORT_INTERLEAVE2 == m_reporting_mode) + m_reporting_mode = RT_REPORT_INTERLEAVE1; } void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size) @@ -1213,9 +1281,9 @@ void Wiimote::MotionPlusLogic::Update() if (6 == i2c_bus.BusRead(ACTIVE_DEVICE_ADDR, 0x00, 6, data)) { // Passthrough data modifications via wiibrew.org - // Data passing through drops the least significant bit of the axes of the left (or only) joystick - // Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] - // Bits 0 and 1 of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before + // Data passing through drops the least significant bit of the axes of the left (or only) + // joystick Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] Bits 0 and 1 + // of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before SetBit(data[0], 0, Common::ExtractBit(data[5], 0)); SetBit(data[1], 0, Common::ExtractBit(data[5], 1)); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 97c4fbd327..573f1b6bc2 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -269,6 +269,9 @@ public: // TODO: change int to u16 or something int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) { + // TODO: the real bus seems to read in blocks of 8. + // peripherals NACK after each 8 bytes. + // INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); for (auto& slave : m_slaves) { @@ -286,6 +289,7 @@ public: int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) { // TODO: write in blocks of 6 to simulate the real bus + // sometimes it writes in blocks of 8, (speaker data) // this might trigger activation writes more accurately // INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); @@ -389,7 +393,7 @@ protected: void UpdateButtonsStatus(); void GetButtonData(u8* data); - void GetAccelData(u8* data); + void GetAccelData(s16* x, s16* y, s16* z); void UpdateIRData(bool use_accel); private: @@ -399,6 +403,8 @@ private: struct IRCameraLogic : public I2CSlave { + // TODO: some of this memory is write-only and should return error 7. + struct RegData { // Contains sensitivity and other unknown data @@ -501,6 +507,8 @@ private: static const u8 DATA_FORMAT_ADPCM = 0x00; static const u8 DATA_FORMAT_PCM = 0x40; + // TODO: It seems reading address 0x00 should always return 0xff. + struct { // Speaker reports result in a write of samples to addr 0x00 (which also plays sound) @@ -746,6 +754,7 @@ private: DynamicData m_shake_dynamic_data; // Wiimote accel data + // TODO: can this member be eliminated? AccelData m_accel; // Wiimote index, 0-3 diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index 36d18f8be1..ceb1cf5a01 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -688,8 +688,7 @@ static void SetWiiInputDisplayString(int remoteID, const u8* const data, std::memcpy(&dt, accelData, sizeof(dt)); 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); + StringFromFormat(" ACC:%d,%d,%d (LSB not shown)", dt.x << 2, dt.y << 2, dt.z << 2); } } diff --git a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp index 71d62f5fe3..bc7ab87703 100644 --- a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp +++ b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp @@ -350,11 +350,12 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp if (m_remote_orientation_box->isVisible() && accel_data && buttons_data) { wm_accel& accel = *reinterpret_cast(accel_data); - wm_buttons& buttons = *reinterpret_cast(buttons_data); + //wm_buttons& buttons = *reinterpret_cast(buttons_data); - 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); + // TODO: lsb + 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); GetSpinBoxU16(m_remote_orientation_x_value, accel_x); GetSpinBoxU16(m_remote_orientation_y_value, accel_y); @@ -364,9 +365,10 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp 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; + // TODO: lsb + //buttons.acc_x_lsb = accel_x & 0b11; + //buttons.acc_y_lsb = (accel_y >> 1) & 0b1; + //buttons.acc_z_lsb = (accel_z >> 1) & 0b1; } if (m_ir_box->isVisible() && ir_data && !m_use_controller->isChecked()) From 86c94b8b22e547a9445683258e9f36a1a1b793ef Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Thu, 29 Nov 2018 17:42:59 -0600 Subject: [PATCH 14/22] WiimoteEmu: Motion plus now works half of the time in Wii Sports Resort. --- .../Core/HW/WiimoteCommon/WiimoteReport.h | 17 ++- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 58 +++++--- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 140 +++++++++++++----- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 68 ++++++--- 4 files changed, 205 insertions(+), 78 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h index 5a3cd89ee0..51290a5c6a 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h @@ -63,6 +63,8 @@ union wm_buttons // also just called "core data" u8 down : 1; u8 up : 1; u8 plus : 1; + // For most input reports this is the 2 LSbs of accel.x: + // For interleaved reports this is alternating bits of accel.z: u8 acc_bits : 2; u8 unknown : 1; @@ -71,6 +73,8 @@ union wm_buttons // also just called "core data" u8 b : 1; u8 a : 1; u8 minus : 1; + // For most input reports this is bits of accel.y/z: + // For interleaved reports this is alternating bits of accel.z: u8 acc_bits2 : 2; u8 home : 1; }; @@ -425,8 +429,10 @@ struct wm_write_data u8 rumble : 1; u8 space : 2; // see WM_SPACE_* u8 : 5; - // used only for register space (i2c bus) - u8 slave_address; + // A real wiimote ignores the i2c read/write bit. + u8 i2c_rw_ignored : 1; + // Used only for register space (i2c bus) (7-bits): + u8 slave_address : 7; // big endian: u8 address[2]; u8 size; @@ -447,8 +453,10 @@ struct wm_read_data u8 rumble : 1; u8 space : 2; // see WM_SPACE_* u8 : 5; - // used only for register space (i2c bus) - u8 slave_address; + // A real wiimote ignores the i2c read/write bit. + u8 i2c_rw_ignored : 1; + // Used only for register space (i2c bus) (7-bits): + u8 slave_address : 7; // big endian: u8 address[2]; u8 size[2]; @@ -460,6 +468,7 @@ struct wm_read_data_reply wm_buttons buttons; u8 error : 4; // see WM_RDERR_* u8 size_minus_one : 4; + // big endian: u16 address; u8 data[16]; }; diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 9275eaeb46..8bc69c7141 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -40,7 +40,7 @@ namespace WiimoteEmu void Wiimote::ReportMode(const wm_report_mode* const dr) { if (dr->mode < RT_REPORT_CORE || dr->mode > RT_REPORT_INTERLEAVE2 || - (dr->mode > RT_REPORT_CORE_ACCEL_IR10_EXT6 && dr->mode < RT_REPORT_EXT21)) + (dr->mode > RT_REPORT_CORE_ACCEL_IR10_EXT6 && dr->mode < RT_REPORT_EXT21)) { // A real wiimote ignores the entire message if the mode is invalid. WARN_LOG(WIIMOTE, "Game requested invalid report mode: 0x%02x", dr->mode); @@ -177,11 +177,11 @@ void Wiimote::SendAck(u8 report_id, u8 error_code) rpt.param = HID_PARAM_INPUT; rpt.report_id = RT_ACK_DATA; - auto ack = &rpt.data; + auto& ack = rpt.data; - ack->buttons = m_status.buttons; - ack->reportID = report_id; - ack->errorID = error_code; + ack.buttons = m_status.buttons; + ack.reportID = report_id; + ack.errorID = error_code; Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), rpt.GetSize()); @@ -194,12 +194,22 @@ void Wiimote::HandleExtensionSwap() { // if an extension is currently connected and we want to switch to a different extension if ((m_extension->active_extension > 0) && m_extension->switch_extension) + { // detach extension first, wait til next Update() or RequestStatus() call to change to the new // extension m_extension->active_extension = 0; + } else + { // set the wanted extension m_extension->active_extension = m_extension->switch_extension; + } + + // TODO: this is a bit ugly: + if (m_extension->active_extension != 0) + m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic); + else + m_motion_plus_logic.extension_port.SetAttachment(nullptr); // reset register ((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension].get()) @@ -209,7 +219,7 @@ void Wiimote::HandleExtensionSwap() void Wiimote::RequestStatus(const wm_request_status* const rs) { - INFO_LOG(WIIMOTE, "Wiimote::RequestStatus"); + // INFO_LOG(WIIMOTE, "Wiimote::RequestStatus"); // update status struct m_status.extension = m_extension_port.IsDeviceConnected(); @@ -247,7 +257,8 @@ void Wiimote::WriteData(const wm_write_data* const wd) return; } - INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x @ 0x%02x (%d)", wd->space, wd->slave_address, address, wd->size); + INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x @ 0x%02x (%d)", wd->space, + wd->slave_address, address, wd->size); if (wd->size > 16) { @@ -290,7 +301,8 @@ void Wiimote::WriteData(const wm_write_data* const wd) // Write to Control Register // Top byte of address is ignored on the bus. - auto const bytes_written = m_i2c_bus.BusWrite(wd->slave_address >> 1, (u8)address, wd->size, wd->data); + auto const bytes_written = + m_i2c_bus.BusWrite(wd->slave_address, (u8)address, wd->size, wd->data); if (bytes_written != wd->size) { // A real wiimote gives error 7 for failed write to i2c bus (mainly a non-existant slave) @@ -325,7 +337,7 @@ void Wiimote::ReadData(const wm_read_data* const rd) m_read_request.size = Common::swap16(rd->size); INFO_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", m_read_request.space, - m_read_request.slave_address, m_read_request.address, m_read_request.size); + m_read_request.slave_address, m_read_request.address, m_read_request.size); // Send up to one read-data-reply. // If more data needs to be sent it will happen on the next "Update()" @@ -336,7 +348,7 @@ bool Wiimote::ProcessReadDataRequest() { // Limit the amt to 16 bytes // AyuanX: the MTU is 640B though... what a waste! - u16 const bytes_to_read = std::min((u16)16, m_read_request.size); + const u16 bytes_to_read = std::min(16, m_read_request.size); if (0 == bytes_to_read) { @@ -404,28 +416,40 @@ bool Wiimote::ProcessReadDataRequest() // Read from Control Register // Top byte of address is ignored on the bus, but it IS maintained in the read-reply. - auto const bytes_read = m_i2c_bus.BusRead(m_read_request.slave_address >> 1, - (u8)m_read_request.address, bytes_to_read, reply->data); + auto const bytes_read = m_i2c_bus.BusRead( + m_read_request.slave_address, (u8)m_read_request.address, bytes_to_read, reply->data); reply->size_minus_one = bytes_read - 1; if (bytes_read != bytes_to_read) { // generate read error, 7 == no such slave (no ack) - INFO_LOG(WIIMOTE, "Responding with read error 7."); + INFO_LOG(WIIMOTE, "Responding with read error 7 @ 0x%x @ 0x%x (%d)", + m_read_request.slave_address, m_read_request.address, m_read_request.size); reply->error = 0x07; } } break; default: - PanicAlert("Wiimote::ReadData: unimplemented address space (space: 0x%x)!", m_read_request.space); + PanicAlert("Wiimote::ReadData: invalid address space (space: 0x%x)!", m_read_request.space); break; } - // Modify the read request, zero size == complete - m_read_request.address += bytes_to_read; - m_read_request.size -= bytes_to_read; + if (reply->error) + { + // Stop processing request on read error: + m_read_request.size = 0; + // TODO: what size does a real wiimote return on read error? + // it's 10 minus one (9) for some reason?? + // reply->size_minus_one = 0; + } + else + { + // Modify the read request, zero size == complete + m_read_request.address += bytes_to_read; + m_read_request.size -= bytes_to_read; + } // Send the data Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 461a5aef55..d6f8191291 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -101,7 +101,7 @@ static const ReportFeatures reporting_mode_features[] = { {2, 0, 10, 9, 23}, // 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes {2, 3, 10, 6, 23}, - // Ugly padding members so 0x3d,3e,3f are properly placed in the array: + // 0x38 - 0x3c: Nothing {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, @@ -109,7 +109,8 @@ static const ReportFeatures reporting_mode_features[] = { {0, 0, 0, 0, 0}, // 0x3d: 21 Extension Bytes {0, 0, 0, 21, 23}, - // 0x3e / 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes + // 0x3e - 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes + {2, 1, 0, 18, 23}, {2, 1, 0, 18, 23}, }; @@ -313,6 +314,7 @@ static const char* const named_buttons[] = { void Wiimote::Reset() { + // TODO: Is a wiimote in CORE or DISABLED reporting mode on sync? m_reporting_mode = RT_REPORT_DISABLED; m_reporting_channel = 0; m_reporting_auto = false; @@ -345,8 +347,26 @@ void Wiimote::Reset() static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60, 0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d}; + static const u8 mp_cert[64] = { + //0x50, 0xc3, 0x0c, 0xab, 0x16, 0x07, 0xf6, 0x89, 0x51, 0x93, 0xbe, 0xa5, 0xb2, + //0xbb, 0xbb, 0x35, 0x49, 0x32, 0x04, 0xfd, 0x29, 0x1d, 0xc1, 0xb7, 0x5a, 0x7c, + //0x85, 0xb9, 0x78, 0x14, 0xf4, 0xfe, 0x21, 0x30, 0xa2, 0x5f, 0xb2, 0xc2, 0x8b, + //0x72, 0x02, 0xf8, 0x60, 0xdf, 0x03, 0x30, 0xdc, 0xb6, 0x86, 0xa4, 0x41, 0xdd, + //0x49, 0x01, 0x7b, 0x2f, 0xb2, 0xc8, 0x5b, 0x12, 0x92, 0x47, 0xb8, 0x23 + + //0xf0, 0x89, 0x3b, 0xf7, 0x1b, 0x6c, 0x92, 0x95, 0xa0, 0x05, 0xd4, 0x03, 0x82, + //0x8e, 0xae, 0x73, 0x15, 0xc7, 0x95, 0xfb, 0xae, 0xee, 0xc0, 0x68, 0xbd, 0x49, + //0xf5, 0x32, 0x48, 0x8d, 0x33, 0x00, 0x94, 0x32, 0xf5, 0xf1, 0x30, 0x66, 0x68, + //0x9e, 0xf3, 0xe5, 0xfa, 0x9b, 0xb6, 0xe3, 0x0b, 0xa8, 0x07, 0xd5, 0x25, 0x38, + + 0x99, 0x1a, 0x07, 0x1b, 0x97, 0xf1, 0x11, 0x78, 0x0c, 0x42, 0x2b, 0x68, 0xdf, 0x44, 0x38, 0x0d, + 0x2b, 0x7e, 0xd6, 0x84, 0x84, 0x58, 0x65, 0xc9, 0xf2, 0x95, 0xd9, 0xaf, 0xb6, 0xc4, 0x87, 0xd5, + 0x18, 0xdb, 0x67, 0x3a, 0xc0, 0x71, 0xec, 0x3e, 0xf4, 0xe6, 0x7e, 0x35, 0xa3, 0x29, 0xf8, 0x1f, + 0xc5, 0x7c, 0x3d, 0xb9, 0x56, 0x22, 0x95, 0x98, 0x8f, 0xfb, 0x66, 0x3e, 0x9a, 0xdd, 0xeb, 0x7e, + }; + // std::copy(std::begin(c1), std::end(c1), m_motion_plus_logic.reg_data.calibration_data); - // std::copy(std::begin(c2), std::end(c2), m_motion_plus_logic.reg_data.calibration_data + 0x10); + std::copy(std::begin(mp_cert), std::end(mp_cert), m_motion_plus_logic.reg_data.cert_data); // status memset(&m_status, 0, sizeof(m_status)); @@ -379,7 +399,7 @@ void Wiimote::Reset() // TODO: only add to bus when connected: // Address 0x52 (when motion plus is not activated) // Connected to motion plus i2c_bus (with passthrough by default) - m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic); + //m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic); } Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1) @@ -563,19 +583,12 @@ bool Wiimote::Step() UpdateButtonsStatus(); } - if (ProcessReadDataRequest()) - { - // Read requests suppress normal input reports - // Don't send any other reports - return true; - } - // If an extension change is requested in the GUI it will first be disconnected here. // causing IsDeviceConnected() to return false below: HandleExtensionSwap(); - // check if a status report needs to be sent - // this happens when extensions are switched + // Check if a status report needs to be sent. This happens when extensions are switched. + // ..even during read requests which continue after the status report is sent. if (m_status.extension != m_extension_port.IsDeviceConnected()) { // WiiBrew: Following a connection or disconnection event on the Extension Port, @@ -583,11 +596,20 @@ bool Wiimote::Step() // arrive. m_reporting_mode = RT_REPORT_DISABLED; + INFO_LOG(WIIMOTE, "Sending status report due to extension status change."); + RequestStatus(); return true; } + if (ProcessReadDataRequest()) + { + // Read requests suppress normal input reports + // Don't send any other reports + return true; + } + return false; } @@ -878,7 +900,7 @@ void Wiimote::Update() if (RT_REPORT_DISABLED == m_reporting_mode) { - // The wiimote is in this disabled state on boot and after an extension change. + // The wiimote is in this disabled after an extension change. // Input reports are not sent, even on button change. return; } @@ -932,14 +954,14 @@ void Wiimote::Update() if (RT_REPORT_INTERLEAVE1 == m_reporting_mode) { *feature_ptr = (x >> 2) & 0xff; - core.acc_bits = (z >> 4) & 0b11; - core.acc_bits2 = (z >> 6) & 0b11; + core.acc_bits = (z >> 6) & 0b11; + core.acc_bits2 = (z >> 8) & 0b11; } if (RT_REPORT_INTERLEAVE2 == m_reporting_mode) { *feature_ptr = (y >> 2) & 0xff; - core.acc_bits = (z >> 0) & 0b11; - core.acc_bits2 = (z >> 2) & 0b11; + core.acc_bits = (z >> 2) & 0b11; + core.acc_bits2 = (z >> 4) & 0b11; } else { @@ -999,6 +1021,7 @@ void Wiimote::Update() Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, m_ext_logic.ext_key); } + if (NetPlay::IsNetPlayRunning()) { NetPlay_GetWiimoteData(m_index, data, rptf.total_size, m_reporting_mode); @@ -1006,7 +1029,6 @@ void Wiimote::Update() m_status.buttons = *reinterpret_cast(data + rptf.core_size); } - // TODO: need to fix usage of rptf probably Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key); @@ -1180,7 +1202,7 @@ int Wiimote::CurrentExtension() const bool Wiimote::ExtensionLogic::ReadDeviceDetectPin() { - return extension->active_extension ? true : false; + return extension->active_extension != 0; } void Wiimote::ExtensionLogic::Update() @@ -1204,25 +1226,54 @@ void Wiimote::MotionPlusLogic::Update() return; } - // TODO: clean up this hackery: - // the value seems to increase based on time starting after the first read of 0x00 - if (IsActive() && times_updated_since_activation < 0xff) - { - ++times_updated_since_activation; + auto& data = reg_data.controller_data; + auto& mplus_data = *reinterpret_cast(data); - // TODO: wtf is this value actually.. - if (times_updated_since_activation == 9) - reg_data.initialization_status = 0x4; - else if (times_updated_since_activation == 10) - reg_data.initialization_status = 0x8; - else if (times_updated_since_activation == 18) - reg_data.initialization_status = 0xc; - else if (times_updated_since_activation == 53) - reg_data.initialization_status = 0xe; + if (0x0 == reg_data.cert_ready) + { + + // Without sending this nonsense, inputs are unresponsive.. even regular buttons + // Device still operates when changing the data slightly so its not any sort of encrpytion + // It even works when removing the is_mp_data bit in the last byte + static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc, 0x02}; + //std::copy(std::begin(init_data), std::end(init_data), data); + reg_data.cert_ready = 0x2; + //return; } - auto& mplus_data = *reinterpret_cast(reg_data.controller_data); - auto& data = reg_data.controller_data; + if (0x2 == reg_data.cert_ready) + { + // A real wiimote takes about 2 seconds to reach this state: + reg_data.cert_ready = 0xe; + } + + if (0x18 == reg_data.cert_ready) + { + const u8 mp_cert2[64] = { + //0x39, 0x7c, 0xe9, 0x79, 0x15, 0x52, 0x0e, 0x4f, 0x28, 0x4d, 0x9d, 0x2c, 0xd3, + //0x2a, 0x1a, 0x28, 0xa1, 0x25, 0x55, 0xb4, 0x4e, 0xb1, 0xd5, 0xae, 0x9d, 0x99, + //0x96, 0x96, 0x1d, 0x94, 0xd1, 0x22, 0xca, 0x1f, 0x51, 0x1d, 0x55, 0xee, 0x4d, + //0x58, 0x97, 0xd4, 0xb9, 0x3f, 0x0d, 0x0a, 0x04, 0xd8, 0x01, 0x21, 0xf9, 0x17, + //0x45, 0xe4, 0x42, 0x58, 0x3f, 0x7c, 0x3c, 0x2c, 0x3a, 0xcd, 0xbd, 0x27, 0x3b, + + //0xea, 0x7c, 0x25, 0xaf, 0xcc, 0xc8, 0xef, 0x22, 0x99, 0xb3, 0x79, 0x72, 0x60, + //0xe8, 0x16, 0x4f, 0x5a, 0x47, 0x07, 0x04, 0x02, 0x14, 0x7b, 0xd0, 0xf6, 0xc9, + //0x77, 0x28, 0x9f, 0x77, 0x78, 0xce, 0x19, 0x74, 0x89, 0xe3, 0x56, 0x3a, 0x23, + //0x13, 0x63, 0xbb, 0x86, 0xf9, 0x13, 0x0e, 0x62, 0xfb, 0x61, 0xf5, 0x42, 0x65, + //0x48, 0x8e, 0xed, 0xc2, 0xc4, 0xc1, 0x18, 0xd0, 0x19, 0x9c, 0xe5, 0x1e + + 0xa5, 0x84, 0x1f, 0xd6, 0xbd, 0xdc, 0x7a, 0x4c, 0xf3, 0xc0, 0x24, 0xe0, 0x92, 0xef, 0x19, 0x28, + 0x65, 0xe0, 0x62, 0x7c, 0x9b, 0x41, 0x6f, 0x12, 0xc3, 0xac, 0x78, 0xe4, 0xfc, 0x6b, 0x7b, 0x0a, + 0xb4, 0x50, 0xd6, 0xf2, 0x45, 0xf7, 0x93, 0x04, 0xaf, 0xf2, 0xb7, 0x26, 0x94, 0xee, 0xad, 0x92, + 0x05, 0x6d, 0xe5, 0xc6, 0xd6, 0x36, 0xdc, 0xa5, 0x69, 0x0f, 0xc8, 0x99, 0xf2, 0x1c, 0x4e, 0x0d, + }; + + std::copy(std::begin(mp_cert2), std::end(mp_cert2), reg_data.cert_data); + + // A real wiimote takes about 2 seconds to reach this state: + reg_data.cert_ready = 0x1a; + INFO_LOG(WIIMOTE, "M+ cert 2 ready!", reg_data.cert_ready); + } // TODO: make sure a motion plus report is sent first after init @@ -1268,6 +1319,7 @@ void Wiimote::MotionPlusLogic::Update() // Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it SetBit(data[5], 2, Common::ExtractBit(data[5], 0)); + // Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below. mplus_data.is_mp_data = false; } } @@ -1284,9 +1336,11 @@ void Wiimote::MotionPlusLogic::Update() // Data passing through drops the least significant bit of the axes of the left (or only) // joystick Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] Bits 0 and 1 // of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before + SetBit(data[0], 0, Common::ExtractBit(data[5], 0)); SetBit(data[1], 0, Common::ExtractBit(data[5], 1)); + // Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below. mplus_data.is_mp_data = false; } } @@ -1315,9 +1369,17 @@ void Wiimote::MotionPlusLogic::Update() mplus_data.pitch1 = pitch_value & 0xff; // Bits 8-13 - mplus_data.yaw1 = yaw_value >> 8; - mplus_data.roll1 = roll_value >> 8; - mplus_data.pitch1 = pitch_value >> 8; + mplus_data.yaw2 = yaw_value >> 8; + mplus_data.roll2 = roll_value >> 8; + mplus_data.pitch2 = pitch_value >> 8; + + // hax: + //data[0] = 0x6d; + //data[1] = 0xfa; + //data[2] = 0x13; + //data[3] = 0x7f; + //data[4] = 0x83; + //data[5] = 0x7e; } mplus_data.extension_connected = extension_port.IsDeviceConnected(); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 573f1b6bc2..4b335af3de 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -306,6 +306,7 @@ public: } private: + // Pointers are unowned: std::vector m_slaves; }; @@ -334,7 +335,10 @@ public: void SetAttachment(ExtensionAttachment* dev) { m_i2c_bus.RemoveSlave(m_attachment); - m_i2c_bus.AddSlave(m_attachment = dev); + m_attachment = dev; + + if (dev) + m_i2c_bus.AddSlave(dev); } private: @@ -470,12 +474,12 @@ private: // Check if encrypted reads is on if (0xaa == reg_data.encryption) { - INFO_LOG(WIIMOTE, "Encrypted read."); + //INFO_LOG(WIIMOTE, "Encrypted read."); WiimoteEncrypt(&ext_key, data_out, addr, (u8)count); } else { - INFO_LOG(WIIMOTE, "Unencrypted read."); + //INFO_LOG(WIIMOTE, "Unencrypted read."); } return result; @@ -561,9 +565,11 @@ private: struct MotionPlusLogic : public ExtensionAttachment { + private: // The bus on the end of the motion plus: I2CBus i2c_bus; + public: // The port on the end of the motion plus: ExtensionPort extension_port{i2c_bus}; @@ -573,24 +579,37 @@ private: struct MotionPlusRegister { u8 controller_data[21]; - u8 unknown[11]; + u8 unknown_0x15[11]; // address 0x20 u8 calibration_data[0x20]; - u8 unknown2[0xb0]; + + u8 unknown_0x40[0x10]; + + // address 0x50 + u8 cert_data[0x40]; + + u8 unknown_0x90[0x60]; // address 0xF0 u8 initialized; - u8 unknown3[6]; + // address 0xF1 + u8 cert_enable; + + u8 unknown_0xf2[5]; // address 0xf7 - // Wii Sports Resort reads regularly and claims mplus is disconnected if not to its liking + // 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 - u8 initialization_status; + // 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 unknown4[2]; + u8 unknown_0xf8[2]; // address 0xFA u8 ext_identifier[6]; @@ -610,9 +629,6 @@ private: PASSTHROUGH_CLASSIC = 0x07, }; - // TODO: savestate - u8 times_updated_since_activation = 0; - bool IsActive() const { return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; } PassthroughMode GetPassthroughMode() const @@ -659,15 +675,29 @@ private: if (ACTIVE_DEVICE_ADDR == slave_addr) { auto const result = RawWrite(®_data, addr, count, data_in); - return result; // It seems a write of any value triggers deactivation. + // TODO: kill magic number if (0xf0 == addr) { // Deactivate motion plus: reg_data.ext_identifier[2] = INACTIVE_DEVICE_ADDR << 1; - times_updated_since_activation = 0; + reg_data.cert_ready = 0x0; + + // Pass through the activation write to the attached extension: + // The M+ deactivation signal is cleverly the same as EXT activation: + i2c_bus.BusWrite(slave_addr, addr, count, data_in); } + // TODO: kill magic number + else if (0xf1 == addr) + { + INFO_LOG(WIIMOTE, "M+ cert activation: 0x%x", reg_data.cert_enable); + // 0x14,0x18 is also a valid value + // 0x1a is final value + reg_data.cert_ready = 0x18; + } + + return result; } else { @@ -684,13 +714,15 @@ private: // It seems a write of any value triggers activation. if (0xfe == addr) { - INFO_LOG(WIIMOTE, "Motion Plus has been activated with value: %d", data_in[0]); + INFO_LOG(WIIMOTE, "M+ has been activated: %d", data_in[0]); // Activate motion plus: reg_data.ext_identifier[2] = ACTIVE_DEVICE_ADDR << 1; - reg_data.initialization_status = 0x2; + // TODO: kill magic number + //reg_data.cert_ready = 0x2; - // Some hax to disable encryption: + // TODO: activate extension and disable encrption + // also do this if an extension is attached after activation. std::array data = {0x55}; i2c_bus.BusWrite(ACTIVE_DEVICE_ADDR, 0xf0, (int)data.size(), data.data()); } @@ -710,11 +742,11 @@ private: { if (IsActive()) { - // TODO: logic for when motion plus deactivates return true; } else { + // When inactive the device detect pin reads from ext port: return extension_port.IsDeviceConnected(); } } From 3a889c35ad3169f05c7d01cc48dbb7e3b1f34262 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 2 Dec 2018 12:32:52 -0600 Subject: [PATCH 15/22] WiimoteEmu: Cleanups, Kill more magic numbers and use some enum classes. --- .../Core/HW/WiimoteCommon/WiimoteConstants.h | 17 +- .../Core/HW/WiimoteCommon/WiimoteReport.h | 6 +- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 47 +++-- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 186 +++++++++--------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 15 +- 5 files changed, 149 insertions(+), 122 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h index 6e8d4332c2..8c732fac3e 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h @@ -41,17 +41,20 @@ enum WiimoteLED LED_4 = 0x80 }; -enum WiimoteSpace +enum class WiimoteAddressSpace : u8 { - WS_EEPROM = 0x00, - WS_REGS1 = 0x01, - WS_REGS2 = 0x02 + EEPROM = 0x00, + // 0x01 is never used but it does function on a real wiimote: + I2C_BUS_ALT = 0x01, + I2C_BUS = 0x02, }; -enum WiimoteReadError +enum class WiimoteErrorCode : u8 { - RDERR_WOREG = 7, - RDERR_NOMEM = 8 + SUCCESS = 0, + NACK = 7, + // TODO: Can there be a better name for this? + INVALID = 8, }; constexpr u8 MAX_PAYLOAD = 23; diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h index 51290a5c6a..282d9ddd29 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h @@ -427,7 +427,7 @@ static_assert(sizeof(wm_status_report) == 6, "Wrong size"); struct wm_write_data { u8 rumble : 1; - u8 space : 2; // see WM_SPACE_* + u8 space : 2; u8 : 5; // A real wiimote ignores the i2c read/write bit. u8 i2c_rw_ignored : 1; @@ -451,7 +451,7 @@ static_assert(sizeof(wm_acknowledge) == 4, "Wrong size"); struct wm_read_data { u8 rumble : 1; - u8 space : 2; // see WM_SPACE_* + u8 space : 2; u8 : 5; // A real wiimote ignores the i2c read/write bit. u8 i2c_rw_ignored : 1; @@ -466,7 +466,7 @@ static_assert(sizeof(wm_read_data) == 6, "Wrong size"); struct wm_read_data_reply { wm_buttons buttons; - u8 error : 4; // see WM_RDERR_* + u8 error : 4; u8 size_minus_one : 4; // big endian: u16 address; diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 8bc69c7141..b514ea5767 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -170,7 +170,7 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) The first two bytes are the core buttons data, 00 00 means nothing is pressed. The last byte is the success code 00. */ -void Wiimote::SendAck(u8 report_id, u8 error_code) +void Wiimote::SendAck(u8 report_id, WiimoteErrorCode error_code) { TypedHidPacket rpt; rpt.type = HID_TYPE_DATA; @@ -181,7 +181,7 @@ void Wiimote::SendAck(u8 report_id, u8 error_code) ack.buttons = m_status.buttons; ack.reportID = report_id; - ack.errorID = error_code; + ack.errorID = static_cast(error_code); Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), rpt.GetSize()); @@ -266,11 +266,11 @@ void Wiimote::WriteData(const wm_write_data* const wd) return; } - u8 error_code = 0; + WiimoteErrorCode error_code = WiimoteErrorCode::SUCCESS; - switch (wd->space) + switch (static_cast(wd->space)) { - case WS_EEPROM: + case WiimoteAddressSpace::EEPROM: { // Write to EEPROM @@ -295,8 +295,8 @@ void Wiimote::WriteData(const wm_write_data* const wd) } break; - case WS_REGS1: - case WS_REGS2: + case WiimoteAddressSpace::I2C_BUS: + case WiimoteAddressSpace::I2C_BUS_ALT: { // Write to Control Register @@ -306,7 +306,7 @@ void Wiimote::WriteData(const wm_write_data* const wd) if (bytes_written != wd->size) { // A real wiimote gives error 7 for failed write to i2c bus (mainly a non-existant slave) - error_code = 0x07; + error_code = WiimoteErrorCode::NACK; } } break; @@ -330,7 +330,7 @@ void Wiimote::ReadData(const wm_read_data* const rd) } // Save the request and process it on the next "Update()" calls - m_read_request.space = rd->space; + m_read_request.space = static_cast(rd->space); m_read_request.slave_address = rd->slave_address; m_read_request.address = Common::swap16(rd->address); // A zero size request is just ignored, like on the real wiimote. @@ -362,20 +362,24 @@ bool Wiimote::ProcessReadDataRequest() rpt.report_id = RT_READ_DATA_REPLY; auto reply = &rpt.data; - reply->error = 0; reply->buttons = m_status.buttons; reply->address = Common::swap16(m_read_request.address); + // Pre-fill with zeros in case of read-error or read < 16-bytes: + std::fill(std::begin(reply->data), std::end(reply->data), 0x00); + + WiimoteErrorCode error_code = WiimoteErrorCode::SUCCESS; + switch (m_read_request.space) { - case WS_EEPROM: + case WiimoteAddressSpace::EEPROM: { // Read from EEPROM if (m_read_request.address + m_read_request.size >= WIIMOTE_EEPROM_FREE_SIZE) { if (m_read_request.address + m_read_request.size > WIIMOTE_EEPROM_SIZE) { - PanicAlert("ReadData: address + size out of bounds"); + ERROR_LOG(WIIMOTE, "ReadData: address + size out of bounds"); } // generate a read error, even if the start of the block is readable a real wiimote just sends @@ -386,8 +390,8 @@ bool Wiimote::ProcessReadDataRequest() // read the calibration data at the beginning of Eeprom. I think this // error is supposed to occur when we try to read above the freely // usable space that ends at 0x16ff. - INFO_LOG(WIIMOTE, "Responding with read error 8."); - reply->error = 0x08; + //INFO_LOG(WIIMOTE, "Responding with read error 8."); + error_code = WiimoteErrorCode::INVALID; } else { @@ -410,8 +414,8 @@ bool Wiimote::ProcessReadDataRequest() } break; - case WS_REGS1: - case WS_REGS2: + case WiimoteAddressSpace::I2C_BUS: + case WiimoteAddressSpace::I2C_BUS_ALT: { // Read from Control Register @@ -426,7 +430,7 @@ bool Wiimote::ProcessReadDataRequest() // generate read error, 7 == no such slave (no ack) INFO_LOG(WIIMOTE, "Responding with read error 7 @ 0x%x @ 0x%x (%d)", m_read_request.slave_address, m_read_request.address, m_read_request.size); - reply->error = 0x07; + error_code = WiimoteErrorCode::NACK; } } break; @@ -436,13 +440,12 @@ bool Wiimote::ProcessReadDataRequest() break; } - if (reply->error) + if (WiimoteErrorCode::SUCCESS != error_code) { // Stop processing request on read error: m_read_request.size = 0; - // TODO: what size does a real wiimote return on read error? - // it's 10 minus one (9) for some reason?? - // reply->size_minus_one = 0; + // Real wiimote seems to set size to max value on read errors: + reply->size_minus_one = 0xf; } else { @@ -451,6 +454,8 @@ bool Wiimote::ProcessReadDataRequest() m_read_request.size -= bytes_to_read; } + reply->error = static_cast(error_code); + // Send the data Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), rpt.GetSize()); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index d6f8191291..88ad56871e 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -341,31 +341,25 @@ void Wiimote::Reset() memset(&m_motion_plus_logic.reg_data, 0x00, sizeof(m_motion_plus_logic.reg_data)); memcpy(&m_motion_plus_logic.reg_data.ext_identifier, motion_plus_id, sizeof(motion_plus_id)); - // calibration hackery - static const u8 c1[16] = {0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, - 0xcf, 0xf0, 0x31, 0xad, 0xc8, 0x0b, 0x5e, 0x39}; - static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60, - 0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d}; - - static const u8 mp_cert[64] = { - //0x50, 0xc3, 0x0c, 0xab, 0x16, 0x07, 0xf6, 0x89, 0x51, 0x93, 0xbe, 0xa5, 0xb2, - //0xbb, 0xbb, 0x35, 0x49, 0x32, 0x04, 0xfd, 0x29, 0x1d, 0xc1, 0xb7, 0x5a, 0x7c, - //0x85, 0xb9, 0x78, 0x14, 0xf4, 0xfe, 0x21, 0x30, 0xa2, 0x5f, 0xb2, 0xc2, 0x8b, - //0x72, 0x02, 0xf8, 0x60, 0xdf, 0x03, 0x30, 0xdc, 0xb6, 0x86, 0xa4, 0x41, 0xdd, - //0x49, 0x01, 0x7b, 0x2f, 0xb2, 0xc8, 0x5b, 0x12, 0x92, 0x47, 0xb8, 0x23 - - //0xf0, 0x89, 0x3b, 0xf7, 0x1b, 0x6c, 0x92, 0x95, 0xa0, 0x05, 0xd4, 0x03, 0x82, - //0x8e, 0xae, 0x73, 0x15, 0xc7, 0x95, 0xfb, 0xae, 0xee, 0xc0, 0x68, 0xbd, 0x49, - //0xf5, 0x32, 0x48, 0x8d, 0x33, 0x00, 0x94, 0x32, 0xf5, 0xf1, 0x30, 0x66, 0x68, - //0x9e, 0xf3, 0xe5, 0xfa, 0x9b, 0xb6, 0xe3, 0x0b, 0xa8, 0x07, 0xd5, 0x25, 0x38, - - 0x99, 0x1a, 0x07, 0x1b, 0x97, 0xf1, 0x11, 0x78, 0x0c, 0x42, 0x2b, 0x68, 0xdf, 0x44, 0x38, 0x0d, - 0x2b, 0x7e, 0xd6, 0x84, 0x84, 0x58, 0x65, 0xc9, 0xf2, 0x95, 0xd9, 0xaf, 0xb6, 0xc4, 0x87, 0xd5, - 0x18, 0xdb, 0x67, 0x3a, 0xc0, 0x71, 0xec, 0x3e, 0xf4, 0xe6, 0x7e, 0x35, 0xa3, 0x29, 0xf8, 0x1f, - 0xc5, 0x7c, 0x3d, 0xb9, 0x56, 0x22, 0x95, 0x98, 0x8f, 0xfb, 0x66, 0x3e, 0x9a, 0xdd, 0xeb, 0x7e, + // TODO: determine meaning of calibration data: + static const u8 mplus_cdata[32] = { + 0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, 0xcf, 0xf0, 0x31, + 0xad, 0xc8, 0x0b, 0x5e, 0x39, 0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, + 0x33, 0x60, 0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d, + }; + + std::copy(std::begin(mplus_cdata), std::end(mplus_cdata), + m_motion_plus_logic.reg_data.calibration_data); + + // TODO: determine the meaning behind this: + static const u8 mp_cert[64] = { + 0x99, 0x1a, 0x07, 0x1b, 0x97, 0xf1, 0x11, 0x78, 0x0c, 0x42, 0x2b, 0x68, 0xdf, + 0x44, 0x38, 0x0d, 0x2b, 0x7e, 0xd6, 0x84, 0x84, 0x58, 0x65, 0xc9, 0xf2, 0x95, + 0xd9, 0xaf, 0xb6, 0xc4, 0x87, 0xd5, 0x18, 0xdb, 0x67, 0x3a, 0xc0, 0x71, 0xec, + 0x3e, 0xf4, 0xe6, 0x7e, 0x35, 0xa3, 0x29, 0xf8, 0x1f, 0xc5, 0x7c, 0x3d, 0xb9, + 0x56, 0x22, 0x95, 0x98, 0x8f, 0xfb, 0x66, 0x3e, 0x9a, 0xdd, 0xeb, 0x7e, }; - // std::copy(std::begin(c1), std::end(c1), m_motion_plus_logic.reg_data.calibration_data); std::copy(std::begin(mp_cert), std::end(mp_cert), m_motion_plus_logic.reg_data.cert_data); // status @@ -399,7 +393,7 @@ void Wiimote::Reset() // TODO: only add to bus when connected: // Address 0x52 (when motion plus is not activated) // Connected to motion plus i2c_bus (with passthrough by default) - //m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic); + // m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic); } Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1) @@ -795,11 +789,12 @@ void Wiimote::UpdateIRData(bool use_accel) } // IR data is read from offset 0x37 on real hardware - u8* const data = m_camera_logic.reg_data.camera_data; - // A maximum of 36 bytes. - std::fill_n(data, sizeof(wm_ir_full) * 4, 0xff); + auto& data = m_camera_logic.reg_data.camera_data; + // A maximum of 36 bytes: + std::fill(std::begin(data), std::end(data), 0xff); // Fill report with valid data when full handshake was done + // TODO: kill magic number: if (m_camera_logic.reg_data.data[0x30]) { // ir mode @@ -874,7 +869,8 @@ void Wiimote::UpdateIRData(bool use_accel) break; } default: - WARN_LOG(WIIMOTE, "Game is requesting IR data before setting IR mode."); + // This is fairly normal, 0xff data is sent in this case: + // WARN_LOG(WIIMOTE, "Game is requesting IR data before setting IR mode."); break; } } @@ -1009,7 +1005,13 @@ void Wiimote::Update() m_ext_logic.Update(); m_motion_plus_logic.Update(); - m_i2c_bus.BusRead(ExtensionLogic::DEVICE_ADDR, 0x00, rptf.ext_size, feature_ptr); + const u8 slave_addr = ExtensionLogic::DEVICE_ADDR; + if (rptf.ext_size != m_i2c_bus.BusRead(slave_addr, 0x00, rptf.ext_size, feature_ptr)) + { + // Real wiimote seems to fill with 0xff on failed bus read + std::fill_n(feature_ptr, rptf.ext_size, 0xff); + } + feature_ptr += rptf.ext_size; } @@ -1231,14 +1233,14 @@ void Wiimote::MotionPlusLogic::Update() if (0x0 == reg_data.cert_ready) { - // Without sending this nonsense, inputs are unresponsive.. even regular buttons // Device still operates when changing the data slightly so its not any sort of encrpytion // It even works when removing the is_mp_data bit in the last byte - static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc, 0x02}; - //std::copy(std::begin(init_data), std::end(init_data), data); + // My M+ non-inside gives: 61,46,45,aa,0,2 or b6,46,45,9a,0,2 + static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc | 0x01, 0x02}; + std::copy(std::begin(init_data), std::end(init_data), data); reg_data.cert_ready = 0x2; - //return; + return; } if (0x2 == reg_data.cert_ready) @@ -1249,27 +1251,23 @@ void Wiimote::MotionPlusLogic::Update() if (0x18 == reg_data.cert_ready) { + // TODO: determine the meaning of this const u8 mp_cert2[64] = { - //0x39, 0x7c, 0xe9, 0x79, 0x15, 0x52, 0x0e, 0x4f, 0x28, 0x4d, 0x9d, 0x2c, 0xd3, - //0x2a, 0x1a, 0x28, 0xa1, 0x25, 0x55, 0xb4, 0x4e, 0xb1, 0xd5, 0xae, 0x9d, 0x99, - //0x96, 0x96, 0x1d, 0x94, 0xd1, 0x22, 0xca, 0x1f, 0x51, 0x1d, 0x55, 0xee, 0x4d, - //0x58, 0x97, 0xd4, 0xb9, 0x3f, 0x0d, 0x0a, 0x04, 0xd8, 0x01, 0x21, 0xf9, 0x17, - //0x45, 0xe4, 0x42, 0x58, 0x3f, 0x7c, 0x3c, 0x2c, 0x3a, 0xcd, 0xbd, 0x27, 0x3b, - - //0xea, 0x7c, 0x25, 0xaf, 0xcc, 0xc8, 0xef, 0x22, 0x99, 0xb3, 0x79, 0x72, 0x60, - //0xe8, 0x16, 0x4f, 0x5a, 0x47, 0x07, 0x04, 0x02, 0x14, 0x7b, 0xd0, 0xf6, 0xc9, - //0x77, 0x28, 0x9f, 0x77, 0x78, 0xce, 0x19, 0x74, 0x89, 0xe3, 0x56, 0x3a, 0x23, - //0x13, 0x63, 0xbb, 0x86, 0xf9, 0x13, 0x0e, 0x62, 0xfb, 0x61, 0xf5, 0x42, 0x65, - //0x48, 0x8e, 0xed, 0xc2, 0xc4, 0xc1, 0x18, 0xd0, 0x19, 0x9c, 0xe5, 0x1e - - 0xa5, 0x84, 0x1f, 0xd6, 0xbd, 0xdc, 0x7a, 0x4c, 0xf3, 0xc0, 0x24, 0xe0, 0x92, 0xef, 0x19, 0x28, - 0x65, 0xe0, 0x62, 0x7c, 0x9b, 0x41, 0x6f, 0x12, 0xc3, 0xac, 0x78, 0xe4, 0xfc, 0x6b, 0x7b, 0x0a, - 0xb4, 0x50, 0xd6, 0xf2, 0x45, 0xf7, 0x93, 0x04, 0xaf, 0xf2, 0xb7, 0x26, 0x94, 0xee, 0xad, 0x92, - 0x05, 0x6d, 0xe5, 0xc6, 0xd6, 0x36, 0xdc, 0xa5, 0x69, 0x0f, 0xc8, 0x99, 0xf2, 0x1c, 0x4e, 0x0d, + 0xa5, 0x84, 0x1f, 0xd6, 0xbd, 0xdc, 0x7a, 0x4c, 0xf3, 0xc0, 0x24, 0xe0, 0x92, + 0xef, 0x19, 0x28, 0x65, 0xe0, 0x62, 0x7c, 0x9b, 0x41, 0x6f, 0x12, 0xc3, 0xac, + 0x78, 0xe4, 0xfc, 0x6b, 0x7b, 0x0a, 0xb4, 0x50, 0xd6, 0xf2, 0x45, 0xf7, 0x93, + 0x04, 0xaf, 0xf2, 0xb7, 0x26, 0x94, 0xee, 0xad, 0x92, 0x05, 0x6d, 0xe5, 0xc6, + 0xd6, 0x36, 0xdc, 0xa5, 0x69, 0x0f, 0xc8, 0x99, 0xf2, 0x1c, 0x4e, 0x0d, }; std::copy(std::begin(mp_cert2), std::end(mp_cert2), reg_data.cert_data); + if (0x01 != reg_data.cert_enable) + { + PanicAlert("M+ Failure! Game requested cert2 with value other than 0x01. M+ will disconnect " + "shortly unfortunately. Reconnect wiimote and hope for the best."); + } + // A real wiimote takes about 2 seconds to reach this state: reg_data.cert_ready = 0x1a; INFO_LOG(WIIMOTE, "M+ cert 2 ready!", reg_data.cert_ready); @@ -1288,23 +1286,32 @@ void Wiimote::MotionPlusLogic::Update() // If an extension is not attached the data is always mplus data // even when passthrough is enabled - switch (GetPassthroughMode()) + // Real M+ seems to only ever read 6 bytes from the extension. + // Data after 6 bytes seems to be zero-filled. + // After reading, the real M+ uses that data for the next frame. + // But we are going to use it for the current frame instead. + const int ext_amt = 6; + // Always read from 0x52 @ 0x00: + const u8 ext_slave = ACTIVE_DEVICE_ADDR; + const u8 ext_addr = 0x00; + + // Try to alternate between M+ and EXT data: + mplus_data.is_mp_data ^= true; + + // If the last frame had M+ data try to send some non-M+ data: + if (!mplus_data.is_mp_data) { - case PassthroughMode::PASSTHROUGH_DISABLED: - { - mplus_data.is_mp_data = true; - break; - } - case PassthroughMode::PASSTHROUGH_NUNCHUK: - { - // If we sent mplus data last time now we will try to send ext data. - if (mplus_data.is_mp_data) + switch (GetPassthroughMode()) { - // The real mplus seems to only ever read 6 bytes from the extension - // bytes after 6 seem to be zero filled - // The real hardware uses these 6 bytes for the next frame, - // but we aren't going to do that - if (6 == i2c_bus.BusRead(ACTIVE_DEVICE_ADDR, 0x00, 6, data)) + case PassthroughMode::PASSTHROUGH_DISABLED: + { + // Passthrough disabled, always send M+ data: + mplus_data.is_mp_data = true; + break; + } + case PassthroughMode::PASSTHROUGH_NUNCHUK: + { + if (ext_amt == i2c_bus.BusRead(ext_slave, ext_addr, ext_amt, data)) { // Passthrough data modifications via wiibrew.org // Data passing through drops the least significant bit of the three accelerometer values @@ -1322,42 +1329,51 @@ void Wiimote::MotionPlusLogic::Update() // Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below. mplus_data.is_mp_data = false; } + else + { + // Read failed (extension unplugged), Send M+ data instead + mplus_data.is_mp_data = true; + } + break; } - break; - } - case PassthroughMode::PASSTHROUGH_CLASSIC: - { - // If we sent mplus data last time now we will try to send ext data. - if (mplus_data.is_mp_data) + case PassthroughMode::PASSTHROUGH_CLASSIC: { - if (6 == i2c_bus.BusRead(ACTIVE_DEVICE_ADDR, 0x00, 6, data)) + if (ext_amt == i2c_bus.BusRead(ext_slave, ext_addr, ext_amt, data)) { // Passthrough data modifications via wiibrew.org // Data passing through drops the least significant bit of the axes of the left (or only) // joystick Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] Bits 0 and 1 // of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before - SetBit(data[0], 0, Common::ExtractBit(data[5], 0)); SetBit(data[1], 0, Common::ExtractBit(data[5], 1)); // Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below. mplus_data.is_mp_data = false; } + else + { + // Read failed (extension unplugged), Send M+ data instead + mplus_data.is_mp_data = true; + } + break; + } + default: + PanicAlert("MotionPlus unknown passthrough-mode %d", GetPassthroughMode()); + break; } - break; - } - default: - PanicAlert("MotionPlus unknown passthrough-mode %d", GetPassthroughMode()); - break; } - // If the above logic determined this should be mp data, update it here + // If the above logic determined this should be M+ data, update it here if (mplus_data.is_mp_data) { // Wiibrew: "While the Wiimote is still, the values will be about 0x1F7F (8,063)" - u16 yaw_value = 0x1F7F; - u16 roll_value = 0x1F7F; - u16 pitch_value = 0x1F7F; + // high-velocity range should be about +/- 1500 or 1600 dps + // low-velocity range should be about +/- 400 dps + // Wiibrew implies it shoould be +/- 595 and 2700 + + u16 yaw_value = 0x2000; + u16 roll_value = 0x2000; + u16 pitch_value = 0x2000; mplus_data.yaw_slow = 1; mplus_data.roll_slow = 1; @@ -1372,14 +1388,6 @@ void Wiimote::MotionPlusLogic::Update() mplus_data.yaw2 = yaw_value >> 8; mplus_data.roll2 = roll_value >> 8; mplus_data.pitch2 = pitch_value >> 8; - - // hax: - //data[0] = 0x6d; - //data[1] = 0xfa; - //data[2] = 0x13; - //data[3] = 0x7f; - //data[4] = 0x83; - //data[5] = 0x7e; } mplus_data.extension_connected = extension_port.IsDeviceConnected(); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 4b335af3de..15a355fe9f 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -9,6 +9,7 @@ #include #include "Common/Logging/Log.h" +#include "Core/HW/WiimoteCommon/WiimoteConstants.h" #include "Core/HW/WiimoteCommon/WiimoteHid.h" #include "Core/HW/WiimoteCommon/WiimoteReport.h" #include "Core/HW/WiimoteEmu/Encryption.h" @@ -597,6 +598,7 @@ private: // address 0xF1 u8 cert_enable; + // Conduit 2 writes 1 byte to 0xf2 on calibration screen u8 unknown_0xf2[5]; // address 0xf7 @@ -696,6 +698,11 @@ private: // 0x1a is final value reg_data.cert_ready = 0x18; } + // TODO: kill magic number + else if (0xf2 == addr) + { + INFO_LOG(WIIMOTE, "M+ calibration ?? : 0x%x", reg_data.unknown_0xf2[0]); + } return result; } @@ -721,6 +728,10 @@ private: // TODO: kill magic number //reg_data.cert_ready = 0x2; + // A real M+ is unresponsive on the bus for some time during activation + // Reads fail to ack and ext data gets filled with 0xff for a frame or two + // I don't think we need to emulate that. + // TODO: activate extension and disable encrption // also do this if an extension is attached after activation. std::array data = {0x55}; @@ -753,7 +764,7 @@ private: } m_motion_plus_logic; void ReportMode(const wm_report_mode* dr); - void SendAck(u8 report_id, u8 error_code = 0x0); + void SendAck(u8 report_id, WiimoteErrorCode error_code = WiimoteErrorCode::SUCCESS); void RequestStatus(const wm_request_status* rs = nullptr); void ReadData(const wm_read_data* rd); void WriteData(const wm_write_data* wd); @@ -812,7 +823,7 @@ private: struct ReadRequest { - u8 space; + WiimoteAddressSpace space; u8 slave_address; u16 address; u16 size; From d3906e548dec89a6ecfdff21f2b608eae71150f7 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Mon, 3 Dec 2018 18:07:32 -0600 Subject: [PATCH 16/22] WiimoteEmu: Cleanup ack handling. Improve accuracy of report handling with unusual values. Eliminated outdated comments. --- .../Core/HW/WiimoteCommon/WiimoteConstants.h | 4 +- .../Core/HW/WiimoteCommon/WiimoteReport.h | 18 +-- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 128 ++++++++---------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 24 ++-- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 28 ++-- .../Core/Core/HW/WiimoteReal/WiimoteReal.cpp | 4 - 6 files changed, 103 insertions(+), 103 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h index 8c732fac3e..833a664bdf 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h @@ -52,9 +52,9 @@ enum class WiimoteAddressSpace : u8 enum class WiimoteErrorCode : u8 { SUCCESS = 0, + INVALID_SPACE = 6, NACK = 7, - // TODO: Can there be a better name for this? - INVALID = 8, + INVALID_ADDRESS = 8, }; constexpr u8 MAX_PAYLOAD = 23; diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h index 282d9ddd29..2d727ac105 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h @@ -16,6 +16,7 @@ typedef std::vector Report; // Report defines +// TODO: enum classes enum ReportType { RT_RUMBLE = 0x10, @@ -184,6 +185,7 @@ union wm_nc }; static_assert(sizeof(wm_nc) == 6, "Wrong size"); +// TODO: kill/move these: union wm_classic_extension_buttons { u16 hex; @@ -375,10 +377,12 @@ struct wm_report u8 data[0]; struct { - u8 rumble : 1; // enable/disable rumble - // only valid for certain reports - u8 ack : 1; // respond with an ack - u8 enable : 1; // enable/disable certain features + // Enable/disable rumble. (Valid for ALL output reports) + u8 rumble : 1; + // Respond with an ack. (Only valid for certain reports) + u8 ack : 1; + // Enable/disable certain features. (Only valid for certain reports) + u8 enable : 1; }; }; }; @@ -387,7 +391,6 @@ static_assert(sizeof(wm_report) == 2, "Wrong size"); struct wm_leds { u8 rumble : 1; - // real Wii also sets bit 0x2 (unknown purpose) u8 : 3; u8 leds : 4; }; @@ -396,8 +399,7 @@ static_assert(sizeof(wm_leds) == 1, "Wrong size"); struct wm_report_mode { u8 rumble : 1; - // unsure what "all_the_time" actually is, the real Wii does set it (bit 0x2) - u8 all_the_time : 1; + u8 : 1; u8 continuous : 1; u8 : 5; u8 mode; @@ -419,7 +421,7 @@ struct wm_status_report u8 speaker : 1; u8 ir : 1; u8 leds : 4; - u8 padding2[2]; // two 00, TODO: this needs more investigation + u8 padding2[2]; u8 battery; }; static_assert(sizeof(wm_status_report) == 6, "Wrong size"); diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index b514ea5767..278858d5f4 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -49,15 +49,13 @@ void Wiimote::ReportMode(const wm_report_mode* const dr) // TODO: A real wiimote sends a report immediately. // even on REPORT_CORE and continuous off when the buttons haven't changed. + // But.. it is sent after the ACK // INFO_LOG(WIIMOTE, "Set data report mode"); // DEBUG_LOG(WIIMOTE, " Rumble: %x", dr->rumble); // DEBUG_LOG(WIIMOTE, " Continuous: %x", dr->continuous); - // DEBUG_LOG(WIIMOTE, " All The Time: %x", dr->all_the_time); // DEBUG_LOG(WIIMOTE, " Mode: 0x%02x", dr->mode); - // TODO: what does the 'all_the_time' bit really do? - // m_reporting_auto = dr->all_the_time; m_reporting_auto = dr->continuous; m_reporting_mode = dr->mode; } @@ -86,52 +84,50 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) switch (sr->wm) { - case RT_RUMBLE: // 0x10 - // this is handled above - return; // no ack + case RT_RUMBLE: + // This is handled above. + // A real wiimote never ACKs a rumble report: + return; break; - case RT_LEDS: // 0x11 + case RT_LEDS: // INFO_LOG(WIIMOTE, "Set LEDs: 0x%02x", sr->data[0]); m_status.leds = sr->data[0] >> 4; break; - case RT_REPORT_MODE: // 0x12 + case RT_REPORT_MODE: ReportMode(reinterpret_cast(sr->data)); break; - case RT_IR_PIXEL_CLOCK: // 0x13 + case RT_IR_PIXEL_CLOCK: // INFO_LOG(WIIMOTE, "WM IR Clock: 0x%02x", sr->data[0]); // Camera data is currently always updated. Ignoring pixel clock status. - // m_ir_clock = sr->enable; - if (false == sr->ack) - return; break; - case RT_SPEAKER_ENABLE: // 0x14 - // ERROR_LOG(WIIMOTE, "WM Speaker Enable: %02x", sr->enable); - // PanicAlert( "WM Speaker Enable: %d", sr->data[0] ); + case RT_SPEAKER_ENABLE: + // INFO_LOG(WIIMOTE, "WM Speaker Enable: %02x", sr->enable); m_status.speaker = sr->enable; - if (false == sr->ack) - return; break; - case RT_REQUEST_STATUS: // 0x15 + case RT_REQUEST_STATUS: RequestStatus(reinterpret_cast(sr->data)); - return; // sends its own ack + // No ACK: + return; break; - case RT_WRITE_DATA: // 0x16 + case RT_WRITE_DATA: WriteData(reinterpret_cast(sr->data)); - return; // sends its own ack + // Sends it's own ACK conditionally: + return; break; - case RT_READ_DATA: // 0x17 + case RT_READ_DATA: ReadData(reinterpret_cast(sr->data)); - return; // sends its own ack/reply + // No ACK: + return; break; - case RT_WRITE_SPEAKER_DATA: // 0x18 + case RT_WRITE_SPEAKER_DATA: // Not sure if speaker mute stops the bus write on real hardware, but it's not that important if (!m_speaker_mute) { @@ -140,29 +136,27 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) PanicAlert("EmuWiimote: bad speaker data length!"); SpeakerData(sd->data, sd->length); } - return; // no ack + // No ACK: + return; break; - case RT_SPEAKER_MUTE: // 0x19 + case RT_SPEAKER_MUTE: m_speaker_mute = sr->enable; - if (false == sr->ack) - return; break; - case RT_IR_LOGIC: // 0x1a + case RT_IR_LOGIC: // Camera data is currently always updated. Just saving this for status requests. m_status.ir = sr->enable; - if (false == sr->ack) - return; break; default: PanicAlert("HidOutputReport: Unknown channel 0x%02x", sr->wm); - return; // no ack + return; break; } - SendAck(sr->wm); + if (sr->ack) + SendAck(sr->wm); } /* This will generate the 0x22 acknowledgement for most Input reports. @@ -219,8 +213,6 @@ void Wiimote::HandleExtensionSwap() void Wiimote::RequestStatus(const wm_request_status* const rs) { - // INFO_LOG(WIIMOTE, "Wiimote::RequestStatus"); - // update status struct m_status.extension = m_extension_port.IsDeviceConnected(); // Battery levels in voltage @@ -229,8 +221,7 @@ void Wiimote::RequestStatus(const wm_request_status* const rs) // 0x33 - 0x54: level 3 // 0x55 - 0xff: level 4 m_status.battery = (u8)(m_battery_setting->GetValue() * 0xff); - // TODO: this right? - m_status.battery_low = m_status.battery < 0x33; + m_status.battery_low = m_status.battery < 0x20; // set up report TypedHidPacket rpt; @@ -251,18 +242,13 @@ void Wiimote::WriteData(const wm_write_data* const wd) { u16 address = Common::swap16(wd->address); - if (0 == wd->size) - { - // Ignore requests of zero size - return; - } - INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x @ 0x%02x (%d)", wd->space, wd->slave_address, address, wd->size); - if (wd->size > 16) + if (0 == wd->size || wd->size > 16) { - PanicAlert("WriteData: size is > 16 bytes"); + WARN_LOG(WIIMOTE, "WriteData: invalid size: %d", wd->size); + // A real wiimote silently ignores such a request: return; } @@ -276,21 +262,23 @@ void Wiimote::WriteData(const wm_write_data* const wd) if (address + wd->size > WIIMOTE_EEPROM_SIZE) { - ERROR_LOG(WIIMOTE, "WriteData: address + size out of bounds!"); - PanicAlert("WriteData: address + size out of bounds!"); - return; + WARN_LOG(WIIMOTE, "WriteData: address + size out of bounds!"); + error_code = WiimoteErrorCode::INVALID_ADDRESS; } - memcpy(m_eeprom + address, wd->data, wd->size); - - // write mii data to file - if (address >= 0x0FCA && address < 0x12C0) + else { - // TODO Only write parts of the Mii block - std::ofstream file; - File::OpenFStream(file, File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/mii.bin", - std::ios::binary | std::ios::out); - file.write((char*)m_eeprom + 0x0FCA, 0x02f0); - file.close(); + std::copy_n(wd->data, wd->size, m_eeprom + address); + + // Write mii data to file + if (address >= 0x0FCA && address < 0x12C0) + { + // TODO: Only write parts of the Mii block + std::ofstream file; + File::OpenFStream(file, File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/mii.bin", + std::ios::binary | std::ios::out); + file.write((char*)m_eeprom + 0x0FCA, 0x02f0); + file.close(); + } } } break; @@ -312,7 +300,9 @@ void Wiimote::WriteData(const wm_write_data* const wd) break; default: - PanicAlert("WriteData: unimplemented parameters!"); + WARN_LOG(WIIMOTE, "WriteData: invalid address space: 0x%x", wd->space); + // A real wiimote gives error 6: + error_code = WiimoteErrorCode::INVALID_SPACE; break; } @@ -325,7 +315,7 @@ void Wiimote::ReadData(const wm_read_data* const rd) if (m_read_request.size) { // There is already an active read request. - // a real wiimote ignores the new one. + // A real wiimote ignores the new one. return; } @@ -375,14 +365,14 @@ bool Wiimote::ProcessReadDataRequest() case WiimoteAddressSpace::EEPROM: { // Read from EEPROM - if (m_read_request.address + m_read_request.size >= WIIMOTE_EEPROM_FREE_SIZE) + if (m_read_request.address + m_read_request.size > WIIMOTE_EEPROM_FREE_SIZE) { if (m_read_request.address + m_read_request.size > WIIMOTE_EEPROM_SIZE) { - ERROR_LOG(WIIMOTE, "ReadData: address + size out of bounds"); + WARN_LOG(WIIMOTE, "ReadData: address + size out of bounds!"); } - // generate a read error, even if the start of the block is readable a real wiimote just sends + // Generate a read error. Even if the start of the block is readable a real wiimote just sends // error code 8 // The real Wiimote generate an error for the first @@ -390,8 +380,7 @@ bool Wiimote::ProcessReadDataRequest() // read the calibration data at the beginning of Eeprom. I think this // error is supposed to occur when we try to read above the freely // usable space that ends at 0x16ff. - //INFO_LOG(WIIMOTE, "Responding with read error 8."); - error_code = WiimoteErrorCode::INVALID; + error_code = WiimoteErrorCode::INVALID_ADDRESS; } else { @@ -417,8 +406,7 @@ bool Wiimote::ProcessReadDataRequest() case WiimoteAddressSpace::I2C_BUS: case WiimoteAddressSpace::I2C_BUS_ALT: { - // Read from Control Register - + // Read from I2C bus // Top byte of address is ignored on the bus, but it IS maintained in the read-reply. auto const bytes_read = m_i2c_bus.BusRead( m_read_request.slave_address, (u8)m_read_request.address, bytes_to_read, reply->data); @@ -436,7 +424,9 @@ bool Wiimote::ProcessReadDataRequest() break; default: - PanicAlert("Wiimote::ReadData: invalid address space (space: 0x%x)!", m_read_request.space); + WARN_LOG(WIIMOTE, "ReadData: invalid address space: 0x%x", m_read_request.space); + // A real wiimote gives error 6: + error_code = WiimoteErrorCode::INVALID_SPACE; break; } @@ -449,7 +439,7 @@ bool Wiimote::ProcessReadDataRequest() } else { - // Modify the read request, zero size == complete + // Modify the active read request, zero size == complete m_read_request.address += bytes_to_read; m_read_request.size -= bytes_to_read; } diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 88ad56871e..021d34c0a9 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -314,14 +314,15 @@ static const char* const named_buttons[] = { void Wiimote::Reset() { - // TODO: Is a wiimote in CORE or DISABLED reporting mode on sync? - m_reporting_mode = RT_REPORT_DISABLED; + // Wiimote starts in non-continuous CORE mode: + m_reporting_mode = RT_REPORT_CORE; m_reporting_channel = 0; m_reporting_auto = false; m_rumble_on = false; m_speaker_mute = false; + // TODO: Can probably just set this to the desired extension right away? m_extension->active_extension = 0; // eeprom @@ -889,8 +890,7 @@ void Wiimote::Update() return; } - u8 data[MAX_PAYLOAD]; - memset(data, 0, sizeof(data)); + u8 data[MAX_PAYLOAD] = {}; Movie::SetPolledDevice(); @@ -939,7 +939,6 @@ void Wiimote::Update() // acceleration if (rptf.accel_size) { - // TODO: GetAccelData has hardcoded payload offsets.. s16 x, y, z; GetAccelData(&x, &y, &z); @@ -983,8 +982,12 @@ void Wiimote::Update() if (rptf.ir_size) { if (!m_status.ir) + { + // TODO: Does a real wiimote send 0xFFs in this case? WARN_LOG(WIIMOTE, "Game is reading IR data without enabling IR logic first."); + } + // The real wiimote reads camera data from the i2c bus at offset 0x37: u8 camera_data_offset = offsetof(IRCameraLogic::RegData, camera_data); if (RT_REPORT_INTERLEAVE2 == m_reporting_mode) @@ -1218,7 +1221,10 @@ void Wiimote::ExtensionLogic::Update() template void SetBit(T& value, u32 bit_number, bool bit_value) { - bit_value ? value |= (1 << bit_number) : value &= ~(1 << bit_number); + if (bit_value) + value |= (1 << bit_number); + else + value &= ~(1 << bit_number); } void Wiimote::MotionPlusLogic::Update() @@ -1303,13 +1309,13 @@ void Wiimote::MotionPlusLogic::Update() { switch (GetPassthroughMode()) { - case PassthroughMode::PASSTHROUGH_DISABLED: + case PassthroughMode::DISABLED: { // Passthrough disabled, always send M+ data: mplus_data.is_mp_data = true; break; } - case PassthroughMode::PASSTHROUGH_NUNCHUK: + case PassthroughMode::NUNCHUK: { if (ext_amt == i2c_bus.BusRead(ext_slave, ext_addr, ext_amt, data)) { @@ -1336,7 +1342,7 @@ void Wiimote::MotionPlusLogic::Update() } break; } - case PassthroughMode::PASSTHROUGH_CLASSIC: + case PassthroughMode::CLASSIC: { if (ext_amt == i2c_bus.BusRead(ext_slave, ext_addr, ext_amt, data)) { diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 15a355fe9f..bee711cb87 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -413,6 +413,9 @@ private: struct RegData { // Contains sensitivity and other unknown data + // TODO: Do the IR and Camera enabling reports write to the i2c bus? + // TODO: does disabling the camera peripheral reset the mode or sensitivity? + // TODO: break out this "data" array into some known members u8 data[0x33]; u8 mode; u8 unk[3]; @@ -475,12 +478,12 @@ private: // Check if encrypted reads is on if (0xaa == reg_data.encryption) { - //INFO_LOG(WIIMOTE, "Encrypted read."); + // INFO_LOG(WIIMOTE, "Encrypted read."); WiimoteEncrypt(&ext_key, data_out, addr, (u8)count); } else { - //INFO_LOG(WIIMOTE, "Unencrypted read."); + // INFO_LOG(WIIMOTE, "Unencrypted read."); } return result; @@ -509,11 +512,12 @@ private: struct SpeakerLogic : public I2CSlave { + // 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 { // Speaker reports result in a write of samples to addr 0x00 (which also plays sound) @@ -522,14 +526,17 @@ private: 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 play; - u8 unk_9; - u8 unknown[0xf4]; + u8 unk_8; + u8 unknown[0xf6]; } reg_data; +#pragma pack(pop) static_assert(0x100 == sizeof(reg_data)); @@ -557,7 +564,6 @@ private: } else { - // TODO: should address wrap around after 0xff result in processing speaker data? return RawWrite(®_data, addr, count, data_in); } } @@ -626,9 +632,9 @@ private: enum class PassthroughMode : u8 { - PASSTHROUGH_DISABLED = 0x04, - PASSTHROUGH_NUNCHUK = 0x05, - PASSTHROUGH_CLASSIC = 0x07, + DISABLED = 0x04, + NUNCHUK = 0x05, + CLASSIC = 0x07, }; bool IsActive() const { return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; } @@ -726,7 +732,7 @@ private: // Activate motion plus: reg_data.ext_identifier[2] = ACTIVE_DEVICE_ADDR << 1; // TODO: kill magic number - //reg_data.cert_ready = 0x2; + // reg_data.cert_ready = 0x2; // A real M+ is unresponsive on the bus for some time during activation // Reads fail to ack and ext data gets filled with 0xff for a frame or two diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp index 2a33321a53..51f4969fb6 100644 --- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp +++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp @@ -98,7 +98,6 @@ void Wiimote::DisableDataReporting() // This probably accomplishes nothing. wm_report_mode rpt = {}; rpt.mode = RT_REPORT_CORE; - rpt.all_the_time = 0; rpt.continuous = 0; rpt.rumble = 0; QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt)); @@ -110,7 +109,6 @@ void Wiimote::EnableDataReporting(u8 mode) wm_report_mode rpt = {}; rpt.mode = mode; - rpt.all_the_time = 1; rpt.continuous = 1; QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt)); } @@ -445,7 +443,6 @@ void Wiimote::EmuResume() wm_report_mode rpt = {}; rpt.mode = wm->m_reporting_mode; - rpt.all_the_time = 1; rpt.continuous = 1; QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt)); @@ -460,7 +457,6 @@ void Wiimote::EmuPause() wm_report_mode rpt = {}; rpt.mode = RT_REPORT_CORE; - rpt.all_the_time = 0; rpt.continuous = 0; QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt)); From 6e5847a7902b08e9aaf4e3984e9d20db23a2b93a Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Tue, 4 Dec 2018 17:45:18 -0600 Subject: [PATCH 17/22] WiimoteEmu: Fix wiimote pan setting from keeping center at half volume. --- .../Core/HW/WiimoteCommon/WiimoteConstants.h | 2 + .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 14 +++++-- Source/Core/Core/HW/WiimoteEmu/Speaker.cpp | 39 +++++++++++-------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 4 +- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 3 ++ 5 files changed, 39 insertions(+), 23 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h index 833a664bdf..a72776dbcd 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h @@ -4,6 +4,8 @@ #pragma once +#include "Common/CommonTypes.h" + // Wiimote internal codes // Communication channels diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 278858d5f4..25f0726ded 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -128,13 +128,19 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) break; case RT_WRITE_SPEAKER_DATA: - // Not sure if speaker mute stops the bus write on real hardware, but it's not that important + // TODO: Does speaker mute stop speaker data processing? + // (important to keep decoder in proper state) if (!m_speaker_mute) { auto sd = reinterpret_cast(sr->data); - if (sd->length > 20) - PanicAlert("EmuWiimote: bad speaker data length!"); - SpeakerData(sd->data, sd->length); + if (sd->length > ArraySize(sd->data)) + { + ERROR_LOG(WIIMOTE, "Bad speaker data length: %d", sd->length); + } + else + { + SpeakerData(sd->data, sd->length); + } } // No ACK: return; diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp index 2f81af1a0f..4f99a92ee7 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp @@ -7,6 +7,7 @@ #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" @@ -70,12 +71,15 @@ void stopdamnwav() void Wiimote::SpeakerData(const u8* data, int length) { + // TODO: should we still process samples for the decoder state? if (!SConfig::GetInstance().m_WiimoteEnableSpeaker) return; - if (m_speaker_logic.reg_data.volume == 0 || m_speaker_logic.reg_data.sample_rate == 0 || - length == 0) + + if (m_speaker_logic.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 samples(new s16[length * 2]); @@ -87,7 +91,7 @@ void Wiimote::SpeakerData(const u8* data, int length) // 8 bit PCM for (int i = 0; i < length; ++i) { - samples[i] = ((s16)(s8)data[i]) << 8; + samples[i] = ((s16)(s8)data[i]) * 0x100; } // Following details from http://wiibrew.org/wiki/Wiimote#Speaker @@ -107,9 +111,6 @@ void Wiimote::SpeakerData(const u8* data, int length) // 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)length * 2; } @@ -119,19 +120,23 @@ void Wiimote::SpeakerData(const u8* data, int length) return; } + if (m_speaker_logic.reg_data.volume > volume_divisor) + { + DEBUG_LOG(IOS_WIIMOTE, "Wiimote volume is higher than suspected maximum!"); + volume_divisor = m_speaker_logic.reg_data.volume; + } + // Speaker Pan - // TODO: fix - unsigned int vol = (unsigned int)(m_options->numeric_settings[0]->GetValue() * 100); + // GUI clamps pan setting from -127 to 127. Why? + const auto pan = (unsigned int)(m_options->numeric_settings[0]->GetValue() * 100); - unsigned int sample_rate = sample_rate_dividend / m_speaker_logic.reg_data.sample_rate; + const unsigned int sample_rate = sample_rate_dividend / m_speaker_logic.reg_data.sample_rate; float speaker_volume_ratio = (float)m_speaker_logic.reg_data.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); - - if (left_volume > 255) - left_volume = 255; - if (right_volume > 255) - right_volume = 255; + // Sloppy math: + unsigned int left_volume = + MathUtil::Clamp((0xff + (2 * pan)) * speaker_volume_ratio, 0, 0xff); + unsigned int right_volume = + MathUtil::Clamp((0xff - (2 * pan)) * speaker_volume_ratio, 0, 0xff); g_sound_stream->GetMixer()->SetWiimoteSpeakerVolume(left_volume, right_volume); @@ -161,4 +166,4 @@ void Wiimote::SpeakerData(const u8* data, int length) num++; #endif } -} +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 021d34c0a9..603f3cb7ba 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -1276,7 +1276,7 @@ void Wiimote::MotionPlusLogic::Update() // A real wiimote takes about 2 seconds to reach this state: reg_data.cert_ready = 0x1a; - INFO_LOG(WIIMOTE, "M+ cert 2 ready!", reg_data.cert_ready); + INFO_LOG(WIIMOTE, "M+ cert 2 ready!"); } // TODO: make sure a motion plus report is sent first after init @@ -1364,7 +1364,7 @@ void Wiimote::MotionPlusLogic::Update() break; } default: - PanicAlert("MotionPlus unknown passthrough-mode %d", GetPassthroughMode()); + PanicAlert("MotionPlus unknown passthrough-mode %d", (int)GetPassthroughMode()); break; } } diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index bee711cb87..ebb1c97397 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -540,6 +540,7 @@ private: static_assert(0x100 == sizeof(reg_data)); + // TODO: What actions should reset this state? ADPCMState adpcm_state; static const u8 DEVICE_ADDR = 0x51; @@ -564,6 +565,8 @@ private: } 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(®_data, addr, count, data_in); } } From b1f350ab1c225996bf5eee169acd1f53d8936234 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Wed, 12 Dec 2018 18:10:55 -0600 Subject: [PATCH 18/22] WiimoteEmu: Cleanups, Kill some now-redundant constants. --- Source/Core/Core/Core.vcxproj | 1 - Source/Core/Core/Core.vcxproj.filters | 17 ++------ Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 39 ++++++++++++------- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 52d8aa400f..b3054e54bc 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -442,7 +442,6 @@ - diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index be4aca8f97..5c5242c852 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -642,12 +642,6 @@ PowerPC\JitCommon - - PowerPC\Jit64 - - - PowerPC\Jit64 - PowerPC\Jit64 @@ -675,9 +669,6 @@ PowerPC\Jit64 - - PowerPC\Jit64 - HW %28Flipper/Hollywood%29\GCKeyboard @@ -907,6 +898,9 @@ Config + + + @@ -1233,9 +1227,6 @@ HW %28Flipper/Hollywood%29\Wiimote\Emu - - HW %28Flipper/Hollywood%29\Wiimote\Emu - HW %28Flipper/Hollywood%29\Wiimote\Real @@ -1611,4 +1602,4 @@ - + \ No newline at end of file diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 603f3cb7ba..39c22d3271 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -49,13 +49,6 @@ #include "InputCommon/ControllerEmu/Setting/BooleanSetting.h" #include "InputCommon/ControllerEmu/Setting/NumericSetting.h" -namespace -{ -// :) -auto const TAU = 6.28318530717958647692; -auto const PI = TAU / 2.0; -} // namespace - namespace WiimoteEmu { constexpr int SHAKE_FREQ = 6; @@ -67,8 +60,8 @@ constexpr int SHAKE_STEP_MAX = ::Wiimote::UPDATE_FREQ / SHAKE_FREQ; static const u8 eeprom_data_0[] = { // IR, maybe more // assuming last 2 bytes are checksum - 0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, - 0x00, // messing up the checksum on purpose + // messing up the checksum on purpose + 0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, 0x00, 0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, 0x00, // Accelerometer // Important: checksum is required for tilt games @@ -137,7 +130,7 @@ void EmulateShake(AccelData* const accel, ControllerEmu::Buttons* const buttons_ { if (shake & (1 << i)) { - (&(accel->x))[i] += std::sin(TAU * shake_step[i] / SHAKE_STEP_MAX) * intensity; + (&(accel->x))[i] += std::sin(MathUtil::TAU * shake_step[i] / SHAKE_STEP_MAX) * intensity; shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX; } else @@ -163,7 +156,7 @@ void EmulateDynamicShake(AccelData* const accel, DynamicData& dynamic_data, else if (dynamic_data.executing_frames_left[i] > 0) { (&(accel->x))[i] += - std::sin(TAU * shake_step[i] / SHAKE_STEP_MAX) * dynamic_data.intensity[i]; + std::sin(MathUtil::TAU * shake_step[i] / SHAKE_STEP_MAX) * dynamic_data.intensity[i]; shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX; dynamic_data.executing_frames_left[i]--; } @@ -196,8 +189,8 @@ void EmulateTilt(AccelData* const accel, ControllerEmu::Tilt* const tilt_group, { // 180 degrees const ControllerEmu::Tilt::StateData state = tilt_group->GetState(); - const ControlState roll = state.x * PI; - const ControlState pitch = state.y * PI; + const ControlState roll = state.x * MathUtil::PI; + const ControlState pitch = state.y * MathUtil::PI; // Some notes that no one will understand but me :p // left, forward, up @@ -217,7 +210,7 @@ void EmulateTilt(AccelData* const accel, ControllerEmu::Tilt* const tilt_group, if (!sideways && upright) sgn[ud] *= -1; - (&accel->x)[ud] = (sin((PI / 2) - std::max(fabs(roll), fabs(pitch)))) * sgn[ud]; + (&accel->x)[ud] = (sin((MathUtil::PI / 2) - std::max(fabs(roll), fabs(pitch)))) * sgn[ud]; (&accel->x)[lr] = -sin(roll) * sgn[lr]; (&accel->x)[fb] = sin(pitch) * sgn[fb]; } @@ -1243,13 +1236,22 @@ void Wiimote::MotionPlusLogic::Update() // Device still operates when changing the data slightly so its not any sort of encrpytion // It even works when removing the is_mp_data bit in the last byte // My M+ non-inside gives: 61,46,45,aa,0,2 or b6,46,45,9a,0,2 - static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc | 0x01, 0x02}; + // static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc | 0x01, 0x02}; + static const u8 init_data[6] = {0x81, 0x46, 0x46, 0xb6, 0x01, 0x02}; std::copy(std::begin(init_data), std::end(init_data), data); reg_data.cert_ready = 0x2; return; } if (0x2 == reg_data.cert_ready) + { + static const u8 init_data[6] = {0x7f, 0xcf, 0xdf, 0x8b, 0x4f, 0x82}; + std::copy(std::begin(init_data), std::end(init_data), data); + reg_data.cert_ready = 0x8; + return; + } + + if (0x8 == reg_data.cert_ready) { // A real wiimote takes about 2 seconds to reach this state: reg_data.cert_ready = 0xe; @@ -1304,6 +1306,13 @@ void Wiimote::MotionPlusLogic::Update() // Try to alternate between M+ and EXT data: mplus_data.is_mp_data ^= true; + // hax!!! + static const u8 hacky_mp_data[6] = {0x1d, 0x91, 0x49, 0x87, 0x73, 0x7a}; + static const u8 hacky_nc_data[6] = {0x79, 0x7f, 0x4b, 0x83, 0x8b, 0xec}; + auto& hacky_ptr = mplus_data.is_mp_data ? hacky_mp_data : hacky_nc_data; + std::copy(std::begin(hacky_ptr), std::end(hacky_ptr), data); + return; + // If the last frame had M+ data try to send some non-M+ data: if (!mplus_data.is_mp_data) { From 0d1fbe7bbca8e42449ee37600519509cb47516b9 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Tue, 1 Jan 2019 08:32:39 -0600 Subject: [PATCH 19/22] WiimoteEmu: Major renaming and cleanup. --- Source/Core/Common/BitUtils.h | 10 + Source/Core/Core/CMakeLists.txt | 18 +- Source/Core/Core/Core.vcxproj | 37 +- Source/Core/Core/Core.vcxproj.filters | 131 +- .../Core/Core/HW/WiimoteCommon/DataReport.cpp | 425 ++++++ .../Core/Core/HW/WiimoteCommon/DataReport.h | 115 ++ .../Core/HW/WiimoteCommon/WiimoteConstants.h | 74 +- .../Core/Core/HW/WiimoteCommon/WiimoteHid.h | 86 +- .../Core/HW/WiimoteCommon/WiimoteReport.h | 597 ++------ .../HW/WiimoteEmu/Attachment/Attachment.cpp | 79 -- .../HW/WiimoteEmu/Attachment/Attachment.h | 41 - Source/Core/Core/HW/WiimoteEmu/Camera.cpp | 202 +++ Source/Core/Core/HW/WiimoteEmu/Camera.h | 104 ++ Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp | 262 ++++ Source/Core/Core/HW/WiimoteEmu/Dynamics.h | 68 + .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 542 +++---- Source/Core/Core/HW/WiimoteEmu/Encryption.cpp | 24 +- Source/Core/Core/HW/WiimoteEmu/Encryption.h | 21 +- .../{Attachment => Extension}/Classic.cpp | 85 +- .../{Attachment => Extension}/Classic.h | 72 +- .../{Attachment => Extension}/Drums.cpp | 33 +- .../{Attachment => Extension}/Drums.h | 44 +- .../HW/WiimoteEmu/Extension/Extension.cpp | 129 ++ .../Core/HW/WiimoteEmu/Extension/Extension.h | 112 ++ .../{Attachment => Extension}/Guitar.cpp | 24 +- .../{Attachment => Extension}/Guitar.h | 44 +- .../{Attachment => Extension}/Nunchuk.cpp | 157 +- .../{Attachment => Extension}/Nunchuk.h | 59 +- .../{Attachment => Extension}/Turntable.cpp | 22 +- .../{Attachment => Extension}/Turntable.h | 50 +- .../Core/Core/HW/WiimoteEmu/ExtensionPort.cpp | 29 + .../Core/Core/HW/WiimoteEmu/ExtensionPort.h | 43 + Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp | 56 + Source/Core/Core/HW/WiimoteEmu/I2CBus.h | 76 + Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp | 376 +++++ Source/Core/Core/HW/WiimoteEmu/MotionPlus.h | 131 ++ Source/Core/Core/HW/WiimoteEmu/Speaker.cpp | 75 +- Source/Core/Core/HW/WiimoteEmu/Speaker.h | 67 + Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 1260 ++++------------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 852 ++--------- Source/Core/Core/HW/WiimoteReal/IOWin.cpp | 51 +- Source/Core/Core/HW/WiimoteReal/IOdarwin.mm | 2 +- Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp | 7 +- .../Core/Core/HW/WiimoteReal/WiimoteReal.cpp | 137 +- Source/Core/Core/HW/WiimoteReal/WiimoteReal.h | 33 +- Source/Core/Core/Movie.cpp | 102 +- Source/Core/Core/Movie.h | 24 +- Source/Core/Core/NetPlayClient.cpp | 3 + .../Config/Mapping/WiimoteEmuExtension.cpp | 5 + .../Config/Mapping/WiimoteEmuGeneral.cpp | 21 +- Source/Core/DolphinQt/MainWindow.cpp | 6 +- .../Core/DolphinQt/TAS/WiiTASInputWindow.cpp | 111 +- Source/Core/DolphinQt/TAS/WiiTASInputWindow.h | 12 +- Source/Core/InputCommon/CMakeLists.txt | 2 +- .../ControlGroup/Attachments.cpp | 33 + .../ControllerEmu/ControlGroup/Attachments.h | 37 + .../ControlGroup/ControlGroup.cpp | 21 +- .../ControllerEmu/ControlGroup/ControlGroup.h | 2 +- .../ControllerEmu/ControlGroup/Extension.cpp | 16 - .../ControllerEmu/ControlGroup/Extension.h | 31 - .../ControllerEmu/ControlGroup/Force.h | 3 - .../ControllerEmu/ControllerEmu.cpp | 14 +- Source/Core/InputCommon/InputCommon.vcxproj | 6 +- .../InputCommon/InputCommon.vcxproj.filters | 14 +- 64 files changed, 4140 insertions(+), 3185 deletions(-) create mode 100644 Source/Core/Core/HW/WiimoteCommon/DataReport.cpp create mode 100644 Source/Core/Core/HW/WiimoteCommon/DataReport.h delete mode 100644 Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.cpp delete mode 100644 Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.h create mode 100644 Source/Core/Core/HW/WiimoteEmu/Camera.cpp create mode 100644 Source/Core/Core/HW/WiimoteEmu/Camera.h create mode 100644 Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp create mode 100644 Source/Core/Core/HW/WiimoteEmu/Dynamics.h rename Source/Core/Core/HW/WiimoteEmu/{Attachment => Extension}/Classic.cpp (89%) rename Source/Core/Core/HW/WiimoteEmu/{Attachment => Extension}/Classic.h (60%) rename Source/Core/Core/HW/WiimoteEmu/{Attachment => Extension}/Drums.cpp (85%) rename Source/Core/Core/HW/WiimoteEmu/{Attachment => Extension}/Drums.h (55%) create mode 100644 Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp create mode 100644 Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h rename Source/Core/Core/HW/WiimoteEmu/{Attachment => Extension}/Guitar.cpp (93%) rename Source/Core/Core/HW/WiimoteEmu/{Attachment => Extension}/Guitar.h (59%) rename Source/Core/Core/HW/WiimoteEmu/{Attachment => Extension}/Nunchuk.cpp (86%) rename Source/Core/Core/HW/WiimoteEmu/{Attachment => Extension}/Nunchuk.h (59%) rename Source/Core/Core/HW/WiimoteEmu/{Attachment => Extension}/Turntable.cpp (93%) rename Source/Core/Core/HW/WiimoteEmu/{Attachment => Extension}/Turntable.h (59%) create mode 100644 Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp create mode 100644 Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h create mode 100644 Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp create mode 100644 Source/Core/Core/HW/WiimoteEmu/I2CBus.h create mode 100644 Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp create mode 100644 Source/Core/Core/HW/WiimoteEmu/MotionPlus.h create mode 100644 Source/Core/Core/HW/WiimoteEmu/Speaker.h create mode 100644 Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp create mode 100644 Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h delete mode 100644 Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.cpp delete mode 100644 Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.h diff --git a/Source/Core/Common/BitUtils.h b/Source/Core/Common/BitUtils.h index c975b5c475..62b9604fab 100644 --- a/Source/Core/Common/BitUtils.h +++ b/Source/Core/Common/BitUtils.h @@ -199,4 +199,14 @@ inline To BitCast(const From& source) noexcept std::memcpy(&storage, &source, sizeof(storage)); return reinterpret_cast(storage); } + +template +void SetBit(T& value, size_t bit_number, bool bit_value) +{ + if (bit_value) + value |= (1 << bit_number); + else + value &= ~(1 << bit_number); +} + } // namespace Common diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index ccf1f69e64..433aeb9d15 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -133,16 +133,22 @@ add_library(core HW/VideoInterface.cpp HW/WII_IPC.cpp HW/Wiimote.cpp + HW/WiimoteCommon/DataReport.cpp HW/WiimoteEmu/WiimoteEmu.cpp - HW/WiimoteEmu/Attachment/Classic.cpp - HW/WiimoteEmu/Attachment/Attachment.cpp - HW/WiimoteEmu/Attachment/Nunchuk.cpp - HW/WiimoteEmu/Attachment/Drums.cpp - HW/WiimoteEmu/Attachment/Guitar.cpp - HW/WiimoteEmu/Attachment/Turntable.cpp + HW/WiimoteEmu/Camera.cpp + HW/WiimoteEmu/Dynamics.cpp HW/WiimoteEmu/EmuSubroutines.cpp HW/WiimoteEmu/Encryption.cpp + HW/WiimoteEmu/ExtensionPort.cpp + HW/WiimoteEmu/I2CBus.cpp + HW/WiimoteEmu/MotionPlus.cpp HW/WiimoteEmu/Speaker.cpp + HW/WiimoteEmu/Extension/Classic.cpp + HW/WiimoteEmu/Extension/Extension.cpp + HW/WiimoteEmu/Extension/Nunchuk.cpp + HW/WiimoteEmu/Extension/Drums.cpp + HW/WiimoteEmu/Extension/Guitar.cpp + HW/WiimoteEmu/Extension/Turntable.cpp HW/WiimoteReal/WiimoteReal.cpp HW/WiiSave.cpp IOS/Device.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index b3054e54bc..837fbfdd15 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -171,14 +171,20 @@ - - - - - - + + + + + + + + + + + + @@ -430,17 +436,24 @@ + - - - - - - + + + + + + + + + + + + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 5c5242c852..114b9a61ac 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -79,9 +79,6 @@ {bc3e845a-3d01-4713-aa32-f27110838d0c} - - {cdbd65da-541f-47d2-8fdc-e99e73e98e69} - {d19f1218-0e28-4f24-a4b3-33fac750a899} @@ -163,9 +160,12 @@ {f0b52c84-49f4-470a-b037-edeea5634b9e} - + {ee6645da-3ad9-4fe7-809f-e4646d0b0ca5} + + {68c09d7e-4f5a-435d-a0d2-7eb7a74d7054} + @@ -516,33 +516,12 @@ HW %28Flipper/Hollywood%29\VI - Video Interface - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - HW %28Flipper/Hollywood%29\Wiimote\Emu HW %28Flipper/Hollywood%29\Wiimote\Emu - - HW %28Flipper/Hollywood%29\Wiimote\Emu - HW %28Flipper/Hollywood%29\Wiimote\Emu @@ -901,6 +880,45 @@ + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Common + @@ -1200,24 +1218,6 @@ HW %28Flipper/Hollywood%29\VI - Video Interface - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - - - HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment - HW %28Flipper/Hollywood%29\Wiimote\Emu @@ -1584,13 +1584,13 @@ IOS\Network\NCD - HW %28Flipper/Hollywood%29\WiimoteCommon + HW %28Flipper/Hollywood%29\Wiimote\Common - HW %28Flipper/Hollywood%29\WiimoteCommon + HW %28Flipper/Hollywood%29\Wiimote\Common - HW %28Flipper/Hollywood%29\WiimoteCommon + HW %28Flipper/Hollywood%29\Wiimote\Common Config @@ -1598,6 +1598,45 @@ Config + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension + + + HW %28Flipper/Hollywood%29\Wiimote\Emu + + + HW %28Flipper/Hollywood%29\Wiimote\Common + diff --git a/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp b/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp new file mode 100644 index 0000000000..47b01d2a65 --- /dev/null +++ b/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp @@ -0,0 +1,425 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "Core/HW/WiimoteCommon/DataReport.h" + +namespace WiimoteCommon +{ +bool DataReportManipulator::HasIR() const +{ + return 0 != GetIRDataSize(); +} + +bool DataReportManipulator::HasExt() const +{ + return 0 != GetExtDataSize(); +} + +u8* DataReportManipulator::GetDataPtr() +{ + return data_ptr; +} + +const u8* DataReportManipulator::GetDataPtr() const +{ + return data_ptr; +} + +struct IncludeCore : virtual DataReportManipulator +{ + bool HasCore() const override { return true; } + + void GetCoreData(CoreData* result) const override + { + *result = *reinterpret_cast(data_ptr); + + // Remove accel LSBs. + result->hex &= CoreData::BUTTON_MASK; + } + + void SetCoreData(const CoreData& new_core) override + { + auto& core = *reinterpret_cast(data_ptr); + + // Don't overwrite accel LSBs. + core.hex &= ~CoreData::BUTTON_MASK; + core.hex |= new_core.hex & CoreData::BUTTON_MASK; + } +}; + +struct NoCore : virtual DataReportManipulator +{ + bool HasCore() const override { return false; } + + void GetCoreData(CoreData*) const override {} + + void SetCoreData(const CoreData&) override {} +}; + +struct NoAccel : virtual DataReportManipulator +{ + bool HasAccel() const override { return false; } + void GetAccelData(AccelData* accel) const override {} + void SetAccelData(const AccelData& accel) override {} +}; + +// Handles typical non-interleaved accel data: +struct IncludeAccel : virtual DataReportManipulator +{ + void GetAccelData(AccelData* result) const override + { + const auto& accel = *reinterpret_cast(data_ptr + 2); + result->x = accel.x << 2; + result->y = accel.y << 2; + result->z = accel.z << 2; + + // LSBs + const auto& core = *reinterpret_cast(data_ptr); + result->x |= core.acc_bits & 0b11; + result->y |= (core.acc_bits2 & 0b1) << 1; + result->z |= core.acc_bits2 & 0b10; + } + + void SetAccelData(const AccelData& new_accel) override + { + auto& accel = *reinterpret_cast(data_ptr + 2); + accel.x = new_accel.x >> 2; + accel.y = new_accel.y >> 2; + accel.z = new_accel.z >> 2; + + // LSBs + auto& core = *reinterpret_cast(data_ptr); + core.acc_bits = (new_accel.x >> 0) & 0b11; + core.acc_bits2 = (new_accel.y >> 1) & 0x1; + core.acc_bits2 |= (new_accel.z & 0xb10); + } + + bool HasAccel() const override { return true; } + +private: + struct AccelMSB + { + u8 x, y, z; + }; + static_assert(sizeof(AccelMSB) == 3, "Wrong size"); +}; + +template +struct IncludeExt : virtual DataReportManipulator +{ + u32 GetExtDataSize() const override { return Length; } + const u8* GetExtDataPtr() const override { return data_ptr + Offset; } + u8* GetExtDataPtr() override { return data_ptr + Offset; } + + // Any report that has Extension data has it last. + u32 GetDataSize() const override { return Offset + Length; } +}; + +struct NoExt : virtual DataReportManipulator +{ + u32 GetExtDataSize() const override { return 0; } + const u8* GetExtDataPtr() const override { return nullptr; } + u8* GetExtDataPtr() override { return nullptr; } +}; + +template +struct IncludeIR : virtual DataReportManipulator +{ + u32 GetIRDataSize() const override { return Length; } + const u8* GetIRDataPtr() const override { return data_ptr + Offset; } + u8* GetIRDataPtr() override { return data_ptr + Offset; } + u32 GetIRDataFormatOffset() const override { return DataOffset; } +}; + +struct NoIR : virtual DataReportManipulator +{ + u32 GetIRDataSize() const override { return 0; } + const u8* GetIRDataPtr() const override { return nullptr; } + u8* GetIRDataPtr() override { return nullptr; } + u32 GetIRDataFormatOffset() const override { return 0; } +}; + +#ifdef _MSC_VER +#pragma warning(push) +// Disable warning for inheritance via dominance +#pragma warning(disable : 4250) +#endif + +struct ReportCore : IncludeCore, NoAccel, NoIR, NoExt +{ + u32 GetDataSize() const override { return 2; } +}; + +struct ReportCoreAccel : IncludeCore, IncludeAccel, NoIR, NoExt +{ + u32 GetDataSize() const override { return 5; } +}; + +struct ReportCoreExt8 : IncludeCore, NoAccel, NoIR, IncludeExt<5, 8> +{ +}; + +struct ReportCoreAccelIR12 : IncludeCore, IncludeAccel, IncludeIR<5, 12>, NoExt +{ + u32 GetDataSize() const override { return 17; } +}; + +struct ReportCoreExt19 : IncludeCore, NoAccel, NoIR, IncludeExt<2, 19> +{ +}; + +struct ReportCoreAccelExt16 : IncludeCore, IncludeAccel, NoIR, IncludeExt<5, 16> +{ +}; + +struct ReportCoreIR10Ext9 : IncludeCore, NoAccel, IncludeIR<2, 10>, IncludeExt<12, 9> +{ +}; + +struct ReportCoreAccelIR10Ext6 : IncludeCore, IncludeAccel, IncludeIR<5, 10>, IncludeExt<15, 6> +{ +}; + +struct ReportExt21 : NoCore, NoAccel, NoIR, IncludeExt<0, 21> +{ +}; + +struct ReportInterleave1 : IncludeCore, IncludeIR<3, 18, 0>, NoExt +{ + // FYI: Only 8-bits of precision in this report, and no Y axis. + // Only contains 4 MSB of Z axis. + + void GetAccelData(AccelData* accel) const override + { + accel->x = data_ptr[2] << 2; + + // Retain lower 6LSBs. + accel->z &= 0b111111; + + const auto& core = *reinterpret_cast(data_ptr); + accel->z |= (core.acc_bits << 6) | (core.acc_bits2 << 8); + } + + void SetAccelData(const AccelData& accel) override + { + data_ptr[2] = accel.x >> 2; + + auto& core = *reinterpret_cast(data_ptr); + core.acc_bits = (accel.z >> 6) & 0b11; + core.acc_bits2 = (accel.z >> 8) & 0b11; + } + + bool HasAccel() const override { return true; } + + u32 GetDataSize() const override { return 21; } +}; + +struct ReportInterleave2 : IncludeCore, IncludeIR<3, 18, 18>, NoExt +{ + // FYI: Only 8-bits of precision in this report, and no X axis. + // Only contains 4 LSB of Z axis. + + void GetAccelData(AccelData* accel) const override + { + accel->y = data_ptr[2] << 2; + + // Retain upper 4MSBs. + accel->z &= ~0b111111; + + const auto& core = *reinterpret_cast(data_ptr); + accel->z |= (core.acc_bits << 2) | (core.acc_bits2 << 4); + } + + void SetAccelData(const AccelData& accel) override + { + data_ptr[2] = accel.y >> 2; + + auto& core = *reinterpret_cast(data_ptr); + core.acc_bits = (accel.z >> 2) & 0b11; + core.acc_bits2 = (accel.z >> 4) & 0b11; + } + + bool HasAccel() const override { return true; } + + u32 GetDataSize() const override { return 21; } +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +std::unique_ptr MakeDataReportManipulator(InputReportID rpt_id, u8* data_ptr) +{ + std::unique_ptr ptr; + + switch (rpt_id) + { + case InputReportID::REPORT_CORE: + // 0x30: Core Buttons + ptr = std::make_unique(); + break; + case InputReportID::REPORT_CORE_ACCEL: + // 0x31: Core Buttons and Accelerometer + ptr = std::make_unique(); + break; + case InputReportID::REPORT_CORE_EXT8: + // 0x32: Core Buttons with 8 Extension bytes + ptr = std::make_unique(); + break; + case InputReportID::REPORT_CORE_ACCEL_IR12: + // 0x33: Core Buttons and Accelerometer with 12 IR bytes + ptr = std::make_unique(); + break; + case InputReportID::REPORT_CORE_EXT19: + // 0x34: Core Buttons with 19 Extension bytes + ptr = std::make_unique(); + break; + case InputReportID::REPORT_CORE_ACCEL_EXT16: + // 0x35: Core Buttons and Accelerometer with 16 Extension Bytes + ptr = std::make_unique(); + break; + case InputReportID::REPORT_CORE_IR10_EXT9: + // 0x36: Core Buttons with 10 IR bytes and 9 Extension Bytes + ptr = std::make_unique(); + break; + case InputReportID::REPORT_CORE_ACCEL_IR10_EXT6: + // 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes + ptr = std::make_unique(); + break; + case InputReportID::REPORT_EXT21: + // 0x3d: 21 Extension Bytes + ptr = std::make_unique(); + break; + case InputReportID::REPORT_INTERLEAVE1: + // 0x3e - 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes + ptr = std::make_unique(); + break; + case InputReportID::REPORT_INTERLEAVE2: + ptr = std::make_unique(); + break; + default: + assert(false); + break; + } + + ptr->data_ptr = data_ptr; + return ptr; +} + +DataReportBuilder::DataReportBuilder(InputReportID rpt_id) : m_data(rpt_id) +{ + SetMode(rpt_id); +} + +void DataReportBuilder::SetMode(InputReportID rpt_id) +{ + m_data.report_id = rpt_id; + m_manip = MakeDataReportManipulator(rpt_id, GetDataPtr() + HEADER_SIZE); +} + +InputReportID DataReportBuilder::GetMode() const +{ + return m_data.report_id; +} + +bool DataReportBuilder::IsValidMode(InputReportID mode) +{ + return (mode >= InputReportID::REPORT_CORE && + mode <= InputReportID::REPORT_CORE_ACCEL_IR10_EXT6) || + (mode >= InputReportID::REPORT_EXT21 && InputReportID::REPORT_INTERLEAVE2 <= mode); +} + +bool DataReportBuilder::HasCore() const +{ + return m_manip->HasCore(); +} + +bool DataReportBuilder::HasAccel() const +{ + return m_manip->HasAccel(); +} + +bool DataReportBuilder::HasIR() const +{ + return m_manip->HasIR(); +} + +bool DataReportBuilder::HasExt() const +{ + return m_manip->HasExt(); +} + +u32 DataReportBuilder::GetIRDataSize() const +{ + return m_manip->GetIRDataSize(); +} + +u32 DataReportBuilder::GetExtDataSize() const +{ + return m_manip->GetExtDataSize(); +} + +u32 DataReportBuilder::GetIRDataFormatOffset() const +{ + return m_manip->GetIRDataFormatOffset(); +} + +void DataReportBuilder::GetCoreData(CoreData* core) const +{ + m_manip->GetCoreData(core); +} + +void DataReportBuilder::SetCoreData(const CoreData& core) +{ + m_manip->SetCoreData(core); +} + +void DataReportBuilder::GetAccelData(AccelData* accel) const +{ + m_manip->GetAccelData(accel); +} + +void DataReportBuilder::SetAccelData(const AccelData& accel) +{ + m_manip->SetAccelData(accel); +} + +const u8* DataReportBuilder::GetDataPtr() const +{ + return m_data.GetData(); +} + +u8* DataReportBuilder::GetDataPtr() +{ + return m_data.GetData(); +} + +u32 DataReportBuilder::GetDataSize() const +{ + return m_manip->GetDataSize() + HEADER_SIZE; +} + +u8* DataReportBuilder::GetIRDataPtr() +{ + return m_manip->GetIRDataPtr(); +} + +const u8* DataReportBuilder::GetIRDataPtr() const +{ + return m_manip->GetIRDataPtr(); +} + +u8* DataReportBuilder::GetExtDataPtr() +{ + return m_manip->GetExtDataPtr(); +} + +const u8* DataReportBuilder::GetExtDataPtr() const +{ + return m_manip->GetExtDataPtr(); +} + +} // namespace WiimoteCommon diff --git a/Source/Core/Core/HW/WiimoteCommon/DataReport.h b/Source/Core/Core/HW/WiimoteCommon/DataReport.h new file mode 100644 index 0000000000..f48981677b --- /dev/null +++ b/Source/Core/Core/HW/WiimoteCommon/DataReport.h @@ -0,0 +1,115 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" +#include "Core/HW/WiimoteCommon/WiimoteConstants.h" +#include "Core/HW/WiimoteCommon/WiimoteHid.h" +#include "Core/HW/WiimoteCommon/WiimoteReport.h" + +namespace WiimoteCommon +{ +// Interface for manipulating Wiimote "Data" reports +// If a report does not contain a particular feature the Get/Set is a no-op. +class DataReportManipulator +{ +public: + virtual ~DataReportManipulator() = default; + + // Accel data handled as if there were always 10 bits of precision. + struct AccelData + { + u16 x, y, z; + }; + + typedef ButtonData CoreData; + + virtual bool HasCore() const = 0; + virtual bool HasAccel() const = 0; + bool HasIR() const; + bool HasExt() const; + + virtual void GetCoreData(CoreData*) const = 0; + virtual void GetAccelData(AccelData*) const = 0; + + virtual void SetCoreData(const CoreData&) = 0; + virtual void SetAccelData(const AccelData&) = 0; + + virtual u8* GetIRDataPtr() = 0; + virtual const u8* GetIRDataPtr() const = 0; + virtual u32 GetIRDataSize() const = 0; + virtual u32 GetIRDataFormatOffset() const = 0; + + virtual u8* GetExtDataPtr() = 0; + virtual const u8* GetExtDataPtr() const = 0; + virtual u32 GetExtDataSize() const = 0; + + u8* GetDataPtr(); + const u8* GetDataPtr() const; + + virtual u32 GetDataSize() const = 0; + + u8* data_ptr; +}; + +std::unique_ptr MakeDataReportManipulator(InputReportID rpt_id, + u8* data_ptr); + +class DataReportBuilder +{ +public: + DataReportBuilder(InputReportID rpt_id); + + typedef ButtonData CoreData; + typedef DataReportManipulator::AccelData AccelData; + + typedef std::vector IRData; + typedef std::vector ExtData; + + void SetMode(InputReportID rpt_id); + InputReportID GetMode() const; + + static bool IsValidMode(InputReportID rpt_id); + + bool HasCore() const; + bool HasAccel() const; + bool HasIR() const; + bool HasExt() const; + + u32 GetIRDataSize() const; + u32 GetExtDataSize() const; + + u32 GetIRDataFormatOffset() const; + + void GetCoreData(CoreData*) const; + void GetAccelData(AccelData*) const; + + void SetCoreData(const CoreData&); + void SetAccelData(const AccelData&); + + u8* GetIRDataPtr(); + const u8* GetIRDataPtr() const; + u8* GetExtDataPtr(); + const u8* GetExtDataPtr() const; + + u8* GetDataPtr(); + const u8* GetDataPtr() const; + + u32 GetDataSize() const; + +private: + static constexpr int HEADER_SIZE = 2; + + static constexpr int MAX_DATA_SIZE = MAX_PAYLOAD - 2; + + TypedHIDInputData> m_data; + + std::unique_ptr m_manip; +}; + +} // namespace WiimoteCommon diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h index a72776dbcd..7b0e5dc30d 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h @@ -6,58 +6,76 @@ #include "Common/CommonTypes.h" -// Wiimote internal codes - -// Communication channels -enum WiimoteChannel +namespace WiimoteCommon { - WC_OUTPUT = 0x11, - WC_INPUT = 0x13 +constexpr u8 MAX_PAYLOAD = 23; + +enum class InputReportID : u8 +{ + STATUS = 0x20, + READ_DATA_REPLY = 0x21, + ACK = 0x22, + + // Not a real value on the wiimote, just a state to disable reports: + REPORT_DISABLED = 0x00, + + REPORT_CORE = 0x30, + REPORT_CORE_ACCEL = 0x31, + REPORT_CORE_EXT8 = 0x32, + REPORT_CORE_ACCEL_IR12 = 0x33, + REPORT_CORE_EXT19 = 0x34, + REPORT_CORE_ACCEL_EXT16 = 0x35, + REPORT_CORE_IR10_EXT9 = 0x36, + REPORT_CORE_ACCEL_IR10_EXT6 = 0x37, + + REPORT_EXT21 = 0x3d, + REPORT_INTERLEAVE1 = 0x3e, + REPORT_INTERLEAVE2 = 0x3f, }; -// 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 +enum class OutputReportID : u8 { -#ifdef _WIN32 - WR_SET_REPORT = 0x50 -#else - WR_SET_REPORT = 0xA0 -#endif + RUMBLE = 0x10, + LEDS = 0x11, + REPORT_MODE = 0x12, + IR_PIXEL_CLOCK = 0x13, + SPEAKER_ENABLE = 0x14, + REQUEST_STATUS = 0x15, + WRITE_DATA = 0x16, + READ_DATA = 0x17, + SPEAKER_DATA = 0x18, + SPEAKER_MUTE = 0x19, + IR_LOGIC = 0x1A, }; -enum WiimoteBT +enum class LED : u8 { - BT_INPUT = 0x01, - BT_OUTPUT = 0x02 -}; - -// LED bit masks -enum WiimoteLED -{ - LED_NONE = 0x00, + NONE = 0x00, LED_1 = 0x10, LED_2 = 0x20, LED_3 = 0x40, LED_4 = 0x80 }; -enum class WiimoteAddressSpace : u8 +enum class AddressSpace : u8 { + // FYI: The EEPROM address space is offset 0x0070 on i2c slave 0x50. + // However attempting to access this device directly results in an error. EEPROM = 0x00, // 0x01 is never used but it does function on a real wiimote: I2C_BUS_ALT = 0x01, I2C_BUS = 0x02, }; -enum class WiimoteErrorCode : u8 +enum class ErrorCode : u8 { SUCCESS = 0, INVALID_SPACE = 6, NACK = 7, INVALID_ADDRESS = 8, + + // Not a real value: + DO_NOT_SEND_ACK = 0xff, }; -constexpr u8 MAX_PAYLOAD = 23; -constexpr u32 WIIMOTE_DEFAULT_TIMEOUT = 1000; +} // namespace WiimoteCommon diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h index a467d9017b..ccd05a9303 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h @@ -5,44 +5,13 @@ #pragma once #include "Common/CommonTypes.h" +#include "Core/HW/WiimoteCommon/WiimoteConstants.h" +#include "Core/HW/WiimoteCommon/WiimoteReport.h" -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4200) -#endif - -#pragma pack(push, 1) - +namespace WiimoteCommon +{ // Source: HID_010_SPC_PFL/1.0 (official HID specification) -struct hid_packet -{ - u8 param : 4; - u8 type : 4; - u8 data[0]; -}; - -template -struct TypedHidPacket -{ - u8 param : 4; - u8 type : 4; - - // TODO: auto set this from the T-type: - u8 report_id; - - T data; - - static_assert(std::is_pod::value); - - const u8* GetData() const { return reinterpret_cast(this); } - u32 GetSize() const - { - static_assert(sizeof(*this) == sizeof(T) + 2); - return sizeof(*this); - } -}; - constexpr u8 HID_TYPE_HANDSHAKE = 0; constexpr u8 HID_TYPE_SET_REPORT = 5; constexpr u8 HID_TYPE_DATA = 0xA; @@ -52,8 +21,55 @@ constexpr u8 HID_HANDSHAKE_SUCCESS = 0; constexpr u8 HID_PARAM_INPUT = 1; constexpr u8 HID_PARAM_OUTPUT = 2; +#ifdef _MSC_VER +#pragma warning(push) +// Disable warning for zero-sized array: +#pragma warning(disable : 4200) +#endif + +#pragma pack(push, 1) + +struct HIDPacket +{ + static constexpr int HEADER_SIZE = 1; + + u8 param : 4; + u8 type : 4; + + u8 data[0]; +}; + +template +struct TypedHIDInputData +{ + TypedHIDInputData(InputReportID _rpt_id) + : param(HID_PARAM_INPUT), type(HID_TYPE_DATA), report_id(_rpt_id) + { + } + + u8 param : 4; + u8 type : 4; + + InputReportID report_id; + + T data; + + static_assert(std::is_pod::value); + + u8* GetData() { return reinterpret_cast(this); } + const u8* GetData() const { return reinterpret_cast(this); } + + constexpr u32 GetSize() const + { + static_assert(sizeof(*this) == sizeof(T) + 2); + return sizeof(*this); + } +}; + #pragma pack(pop) #ifdef _MSC_VER #pragma warning(pop) #endif + +} // namespace WiimoteCommon diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h index 2d727ac105..18b0293804 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h @@ -7,54 +7,124 @@ #include #include "Common/CommonTypes.h" +#include "Core/HW/WiimoteCommon/WiimoteConstants.h" #ifdef _MSC_VER #pragma warning(push) +// Disable warning for zero-sized array: #pragma warning(disable : 4200) #endif -typedef std::vector Report; - -// Report defines -// TODO: enum classes -enum ReportType +namespace WiimoteCommon { - RT_RUMBLE = 0x10, - RT_LEDS = 0x11, - RT_REPORT_MODE = 0x12, - RT_IR_PIXEL_CLOCK = 0x13, - RT_SPEAKER_ENABLE = 0x14, - RT_REQUEST_STATUS = 0x15, - RT_WRITE_DATA = 0x16, - RT_READ_DATA = 0x17, - RT_WRITE_SPEAKER_DATA = 0x18, - RT_SPEAKER_MUTE = 0x19, - RT_IR_LOGIC = 0x1A, - RT_STATUS_REPORT = 0x20, - RT_READ_DATA_REPLY = 0x21, - RT_ACK_DATA = 0x22, - - // Not a real value on the wiimote, just a state to disable reports: - RT_REPORT_DISABLED = 0x00, - - RT_REPORT_CORE = 0x30, - RT_REPORT_CORE_ACCEL = 0x31, - RT_REPORT_CORE_EXT8 = 0x32, - RT_REPORT_CORE_ACCEL_IR12 = 0x33, - RT_REPORT_CORE_EXT19 = 0x34, - RT_REPORT_CORE_ACCEL_EXT16 = 0x35, - RT_REPORT_CORE_IR10_EXT9 = 0x36, - RT_REPORT_CORE_ACCEL_IR10_EXT6 = 0x37, - RT_REPORT_EXT21 = 0x3d, - RT_REPORT_INTERLEAVE1 = 0x3e, - RT_REPORT_INTERLEAVE2 = 0x3f -}; - -// Source: http://wiibrew.org/wiki/Wiimote -// Custom structs #pragma pack(push, 1) -union wm_buttons // also just called "core data" +struct OutputReportGeneric { + OutputReportID rpt_id; + + static constexpr int HEADER_SIZE = sizeof(OutputReportID); + + union + { + u8 data[0]; + struct + { + // Enable/disable rumble. (Valid for ALL output reports) + u8 rumble : 1; + }; + }; +}; +static_assert(sizeof(OutputReportGeneric) == 2, "Wrong size"); + +// TODO: The following structs don't have the report_id header byte. +// This is fine but the naming conventions are poor. + +struct OutputReportRumble +{ + u8 rumble : 1; +}; +static_assert(sizeof(OutputReportRumble) == 1, "Wrong size"); + +struct OutputReportEnableFeature +{ + u8 rumble : 1; + // Respond with an ack. + u8 ack : 1; + // Enable/disable certain feature. + u8 enable : 1; +}; +static_assert(sizeof(OutputReportEnableFeature) == 1, "Wrong size"); + +struct OutputReportLeds +{ + u8 rumble : 1; + u8 ack : 1; + u8 : 2; + u8 leds : 4; +}; +static_assert(sizeof(OutputReportLeds) == 1, "Wrong size"); + +struct OutputReportMode +{ + u8 rumble : 1; + u8 ack : 1; + u8 continuous : 1; + u8 : 5; + InputReportID mode; +}; +static_assert(sizeof(OutputReportMode) == 2, "Wrong size"); + +struct OutputReportRequestStatus +{ + u8 rumble : 1; + u8 : 7; +}; +static_assert(sizeof(OutputReportRequestStatus) == 1, "Wrong size"); + +struct OutputReportWriteData +{ + u8 rumble : 1; + u8 space : 2; + u8 : 5; + // A real wiimote ignores the i2c read/write bit. + u8 i2c_rw_ignored : 1; + // Used only for register space (i2c bus) (7-bits): + u8 slave_address : 7; + // big endian: + u8 address[2]; + u8 size; + u8 data[16]; +}; +static_assert(sizeof(OutputReportWriteData) == 21, "Wrong size"); + +struct OutputReportReadData +{ + u8 rumble : 1; + u8 space : 2; + u8 : 5; + // A real wiimote ignores the i2c read/write bit. + u8 i2c_rw_ignored : 1; + // Used only for register space (i2c bus) (7-bits): + u8 slave_address : 7; + // big endian: + u8 address[2]; + u8 size[2]; +}; +static_assert(sizeof(OutputReportReadData) == 6, "Wrong size"); + +struct OutputReportSpeakerData +{ + u8 unknown : 3; + u8 length : 5; + u8 data[20]; +}; +static_assert(sizeof(OutputReportSpeakerData) == 21, "Wrong size"); + +// FYI: Also contains LSB of accel data: +union ButtonData +{ + static constexpr u16 BUTTON_MASK = ~0x6060; + u16 hex; struct @@ -80,342 +150,11 @@ union wm_buttons // also just called "core data" u8 home : 1; }; }; -static_assert(sizeof(wm_buttons) == 2, "Wrong size"); +static_assert(sizeof(ButtonData) == 2, "Wrong size"); -struct wm_accel +struct InputReportStatus { - u8 x, y, z; -}; -static_assert(sizeof(wm_accel) == 3, "Wrong size"); - -// Four bytes for two objects. Filled with 0xFF if empty -struct wm_ir_basic -{ - u8 x1; - u8 y1; - u8 x2hi : 2; - u8 y2hi : 2; - u8 x1hi : 2; - u8 y1hi : 2; - u8 x2; - u8 y2; -}; -static_assert(sizeof(wm_ir_basic) == 5, "Wrong size"); - -// Three bytes for one object -struct wm_ir_extended -{ - u8 x; - u8 y; - u8 size : 4; - u8 xhi : 2; - u8 yhi : 2; -}; -static_assert(sizeof(wm_ir_extended) == 3, "Wrong size"); - -// Nine bytes for one object -// first 3 bytes are the same as extended -struct wm_ir_full : wm_ir_extended -{ - u8 xmin : 7; - u8 : 1; - u8 ymin : 7; - u8 : 1; - u8 xmax : 7; - u8 : 1; - u8 ymax : 7; - u8 : 1; - u8 zero; - u8 intensity; -}; -static_assert(sizeof(wm_ir_full) == 9, "Wrong size"); - -// Nunchuk -union wm_nc_core -{ - u8 hex; - - struct - { - u8 z : 1; - u8 c : 1; - - // LSBs of accelerometer - u8 acc_x_lsb : 2; - u8 acc_y_lsb : 2; - u8 acc_z_lsb : 2; - }; -}; -static_assert(sizeof(wm_nc_core) == 1, "Wrong size"); - -union wm_nc -{ - struct - { - // joystick x, y - u8 jx; - u8 jy; - - // accelerometer - u8 ax; - u8 ay; - u8 az; - - wm_nc_core bt; // buttons + accelerometer LSBs - }; // regular data - - struct - { - u8 reserved[4]; // jx, jy, ax and ay as in regular case - - u8 extension_connected : 1; - u8 acc_z : 7; // MSBs of accelerometer data - - u8 unknown : 1; // always 0? - u8 report_type : 1; // 1: report contains M+ data, 0: report contains extension data - - u8 z : 1; - u8 c : 1; - - // LSBs of accelerometer - starting from bit 1! - u8 acc_x_lsb : 1; - u8 acc_y_lsb : 1; - u8 acc_z_lsb : 2; - } passthrough_data; -}; -static_assert(sizeof(wm_nc) == 6, "Wrong size"); - -// TODO: kill/move these: -union wm_classic_extension_buttons -{ - u16 hex; - - struct - { - u8 extension_connected : 1; - u8 rt : 1; // right trigger - u8 plus : 1; - u8 home : 1; - u8 minus : 1; - u8 lt : 1; // left trigger - u8 dpad_down : 1; - u8 dpad_right : 1; - - u8 : 2; // cf. extension_data and passthrough_data - u8 zr : 1; // right z button - u8 x : 1; - u8 a : 1; - u8 y : 1; - u8 b : 1; - u8 zl : 1; // left z button - }; // common data - - // M+ pass-through mode slightly differs from the regular data. - // Refer to the common data for unnamed fields - struct - { - u8 : 8; - - u8 dpad_up : 1; - u8 dpad_left : 1; - u8 : 6; - } regular_data; - - struct - { - u8 : 8; - - u8 unknown : 1; // always 0? - u8 report_type : 1; // 1: report contains M+ data, 0: report contains extension data - u8 : 6; - } passthrough_data; -}; -static_assert(sizeof(wm_classic_extension_buttons) == 2, "Wrong size"); - -union wm_classic_extension -{ - // lx/ly/lz; left joystick - // rx/ry/rz; right joystick - // lt; left trigger - // rt; left trigger - - struct - { - u8 : 6; - u8 rx3 : 2; - - u8 : 6; - u8 rx2 : 2; - - u8 ry : 5; - u8 lt2 : 2; - u8 rx1 : 1; - - u8 rt : 5; - u8 lt1 : 3; - - wm_classic_extension_buttons bt; // byte 4, 5 - }; - - struct - { - u8 lx : 6; // byte 0 - u8 : 2; - - u8 ly : 6; // byte 1 - u8 : 2; - - unsigned : 32; - } regular_data; - - struct - { - u8 dpad_up : 1; - u8 lx : 5; // Bits 1-5 - u8 : 2; - - u8 dpad_left : 1; - u8 ly : 5; // Bits 1-5 - u8 : 2; - - unsigned : 32; - } passthrough_data; -}; -static_assert(sizeof(wm_classic_extension) == 6, "Wrong size"); - -struct wm_guitar_extension -{ - u8 sx : 6; - u8 pad1 : 2; // 1 on gh3, 0 on ghwt - - u8 sy : 6; - u8 pad2 : 2; // 1 on gh3, 0 on ghwt - - u8 sb : 5; // not used in gh3 - u8 pad3 : 3; // always 0 - - u8 whammy : 5; - u8 pad4 : 3; // always 0 - - u16 bt; // buttons -}; -static_assert(sizeof(wm_guitar_extension) == 6, "Wrong size"); - -struct wm_drums_extension -{ - u8 sx : 6; - u8 pad1 : 2; // always 0 - - u8 sy : 6; - u8 pad2 : 2; // always 0 - - u8 pad3 : 1; // unknown - u8 which : 5; - u8 none : 1; - u8 hhp : 1; - - u8 pad4 : 1; // unknown - u8 velocity : 4; // unknown - u8 softness : 3; - - u16 bt; // buttons -}; -static_assert(sizeof(wm_drums_extension) == 6, "Wrong size"); - -struct wm_turntable_extension -{ - u8 sx : 6; - u8 rtable3 : 2; - - u8 sy : 6; - u8 rtable2 : 2; - - u8 rtable4 : 1; - u8 slider : 4; - u8 dial2 : 2; - u8 rtable1 : 1; - - u8 ltable1 : 5; - u8 dial1 : 3; - - union - { - u16 ltable2 : 1; - u16 bt; // buttons - }; -}; -static_assert(sizeof(wm_turntable_extension) == 6, "Wrong size"); - -struct wm_motionplus_data -{ - // 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; -}; -static_assert(sizeof(wm_motionplus_data) == 6, "Wrong size"); - -struct wm_report -{ - u8 wm; - union - { - u8 data[0]; - struct - { - // Enable/disable rumble. (Valid for ALL output reports) - u8 rumble : 1; - // Respond with an ack. (Only valid for certain reports) - u8 ack : 1; - // Enable/disable certain features. (Only valid for certain reports) - u8 enable : 1; - }; - }; -}; -static_assert(sizeof(wm_report) == 2, "Wrong size"); - -struct wm_leds -{ - u8 rumble : 1; - u8 : 3; - u8 leds : 4; -}; -static_assert(sizeof(wm_leds) == 1, "Wrong size"); - -struct wm_report_mode -{ - u8 rumble : 1; - u8 : 1; - u8 continuous : 1; - u8 : 5; - u8 mode; -}; -static_assert(sizeof(wm_report_mode) == 2, "Wrong size"); - -struct wm_request_status -{ - u8 rumble : 1; - u8 : 7; -}; -static_assert(sizeof(wm_request_status) == 1, "Wrong size"); - -struct wm_status_report -{ - wm_buttons buttons; + ButtonData buttons; u8 battery_low : 1; u8 extension : 1; u8 speaker : 1; @@ -424,121 +163,29 @@ struct wm_status_report u8 padding2[2]; u8 battery; }; -static_assert(sizeof(wm_status_report) == 6, "Wrong size"); +static_assert(sizeof(InputReportStatus) == 6, "Wrong size"); -struct wm_write_data +struct InputReportAck { - u8 rumble : 1; - u8 space : 2; - u8 : 5; - // A real wiimote ignores the i2c read/write bit. - u8 i2c_rw_ignored : 1; - // Used only for register space (i2c bus) (7-bits): - u8 slave_address : 7; - // big endian: - u8 address[2]; - u8 size; - u8 data[16]; + ButtonData buttons; + OutputReportID rpt_id; + ErrorCode error_code; }; -static_assert(sizeof(wm_write_data) == 21, "Wrong size"); +static_assert(sizeof(InputReportAck) == 4, "Wrong size"); -struct wm_acknowledge +struct InputReportReadDataReply { - wm_buttons buttons; - u8 reportID; - u8 errorID; -}; -static_assert(sizeof(wm_acknowledge) == 4, "Wrong size"); - -struct wm_read_data -{ - u8 rumble : 1; - u8 space : 2; - u8 : 5; - // A real wiimote ignores the i2c read/write bit. - u8 i2c_rw_ignored : 1; - // Used only for register space (i2c bus) (7-bits): - u8 slave_address : 7; - // big endian: - u8 address[2]; - u8 size[2]; -}; -static_assert(sizeof(wm_read_data) == 6, "Wrong size"); - -struct wm_read_data_reply -{ - wm_buttons buttons; + ButtonData buttons; u8 error : 4; u8 size_minus_one : 4; // big endian: u16 address; u8 data[16]; }; -static_assert(sizeof(wm_read_data_reply) == 21, "Wrong size"); +static_assert(sizeof(InputReportReadDataReply) == 21, "Wrong size"); -// Data reports +} // namespace WiimoteCommon -struct wm_report_core -{ - wm_buttons c; -}; -static_assert(sizeof(wm_report_core) == 2, "Wrong size"); - -struct wm_report_core_accel -{ - wm_buttons c; - wm_accel a; -}; -static_assert(sizeof(wm_report_core_accel) == 5, "Wrong size"); - -struct wm_report_core_ext8 -{ - wm_buttons c; - u8 ext[8]; -}; -static_assert(sizeof(wm_report_core_ext8) == 10, "Wrong size"); - -struct wm_report_core_accel_ir12 -{ - wm_buttons c; - wm_accel a; - wm_ir_extended ir[4]; -}; -static_assert(sizeof(wm_report_core_accel_ir12) == 17, "Wrong size"); - -struct wm_report_core_accel_ext16 -{ - wm_buttons c; - wm_accel a; - wm_nc ext; // TODO: Does this make any sense? Shouldn't it be just a general "extension" field? - // wm_ir_basic ir[2]; - u8 pad[10]; -}; -static_assert(sizeof(wm_report_core_accel_ext16) == 21, "Wrong size"); - -struct wm_report_core_accel_ir10_ext6 -{ - wm_buttons c; - wm_accel a; - wm_ir_basic ir[2]; - // u8 ext[6]; - wm_nc ext; // TODO: Does this make any sense? Shouldn't it be just a general "extension" field? -}; -static_assert(sizeof(wm_report_core_accel_ir10_ext6) == 21, "Wrong size"); - -struct wm_report_ext21 -{ - u8 ext[21]; -}; -static_assert(sizeof(wm_report_ext21) == 21, "Wrong size"); - -struct wm_speaker_data -{ - u8 unknown : 3; - u8 length : 5; - u8 data[20]; -}; -static_assert(sizeof(wm_speaker_data) == 21, "Wrong size"); #pragma pack(pop) #ifdef _MSC_VER diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.cpp b/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.cpp deleted file mode 100644 index a9185125b1..0000000000 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2010 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "Core/HW/WiimoteEmu/Attachment/Attachment.h" - -#include -#include -#include - -#include "Common/CommonTypes.h" -#include "Common/Compiler.h" -#include "Core/HW/WiimoteEmu/WiimoteEmu.h" -#include "InputCommon/ControllerEmu/ControlGroup/Extension.h" - -namespace WiimoteEmu -{ -// Extension device IDs to be written to the last bytes of the extension reg -// The id for nothing inserted -constexpr std::array nothing_id{{0x00, 0x00, 0x00, 0x00, 0x2e, 0x2e}}; -// The id for a partially inserted extension (currently unused) -DOLPHIN_UNUSED constexpr std::array partially_id{{0x00, 0x00, 0x00, 0x00, 0xff, 0xff}}; - -Attachment::Attachment(const char* const name, ExtensionReg& reg) : m_name(name), m_reg(reg) -{ -} - -None::None(ExtensionReg& reg) : Attachment("None", reg) -{ - // set up register - m_id = nothing_id; -} - -void Attachment::GetState(u8* const data) -{ -} - -bool Attachment::IsButtonPressed() const -{ - return false; -} - -std::string Attachment::GetName() const -{ - return m_name; -} - -void Attachment::Reset() -{ - // set up register - m_reg = {}; - std::copy(m_id.cbegin(), m_id.cend(), m_reg.constant_id); - std::copy(m_calibration.cbegin(), m_calibration.cend(), m_reg.calibration); -} -} // namespace WiimoteEmu - -namespace ControllerEmu -{ -void Extension::GetState(u8* const data) -{ - static_cast(attachments[active_extension].get())->GetState(data); -} - -bool Extension::IsButtonPressed() const -{ - // Extension == 0 means no Extension, > 0 means one is connected - // Since we want to use this to know if disconnected Wiimotes want to be connected, and - // disconnected - // Wiimotes (can? always?) have their active_extension set to -1, we also have to check the - // switch_extension - if (active_extension > 0) - return static_cast(attachments[active_extension].get()) - ->IsButtonPressed(); - if (switch_extension > 0) - return static_cast(attachments[switch_extension].get()) - ->IsButtonPressed(); - return false; -} -} // namespace ControllerEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.h b/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.h deleted file mode 100644 index b1bae12086..0000000000 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2010 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include "Common/CommonTypes.h" -#include "InputCommon/ControllerEmu/ControllerEmu.h" - -namespace WiimoteEmu -{ -struct ExtensionReg; - -class Attachment : public ControllerEmu::EmulatedController -{ -public: - Attachment(const char* const name, ExtensionReg& reg); - - virtual void GetState(u8* const data); - virtual bool IsButtonPressed() const; - - void Reset(); - std::string GetName() const override; - -protected: - std::array m_id{}; - std::array m_calibration{}; - -private: - const char* const m_name; - ExtensionReg& m_reg; -}; - -class None : public Attachment -{ -public: - explicit None(ExtensionReg& reg); -}; -} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Camera.cpp b/Source/Core/Core/HW/WiimoteEmu/Camera.cpp new file mode 100644 index 0000000000..0a8d20c67e --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/Camera.cpp @@ -0,0 +1,202 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/HW/WiimoteEmu/Camera.h" + +#include "Common/ChunkFile.h" +#include "Core/HW/WiimoteCommon/WiimoteReport.h" +#include "Core/HW/WiimoteEmu/MatrixMath.h" + +namespace WiimoteEmu +{ +void CameraLogic::Reset() +{ + reg_data = {}; +} + +void CameraLogic::DoState(PointerWrap& p) +{ + p.Do(reg_data); +} + +int CameraLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) +{ + if (I2C_ADDR != slave_addr) + return 0; + + return RawRead(®_data, addr, count, data_out); +} + +int CameraLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) +{ + if (I2C_ADDR != slave_addr) + return 0; + + return RawWrite(®_data, addr, count, data_in); +} + +void CameraLogic::Update(const ControllerEmu::Cursor::StateData& cursor, + const NormalizedAccelData& accel, bool sensor_bar_on_top) +{ + u16 x[4], y[4]; + memset(x, 0xFF, sizeof(x)); + + double nsin, ncos; + + // Ugly code to figure out the wiimote's current angle. + // TODO: Kill this. + double ax = accel.x; + double az = accel.z; + const double len = sqrt(ax * ax + az * az); + + if (len) + { + ax /= len; + az /= len; // normalizing the vector + nsin = ax; + ncos = az; + } + else + { + nsin = 0; + ncos = 1; + } + + const double ir_sin = nsin; + const double ir_cos = ncos; + + static constexpr int camWidth = 1024; + static constexpr int camHeight = 768; + static constexpr double bndleft = 0.78820266; + static constexpr double bndright = -0.78820266; + static constexpr double dist1 = 100.0 / camWidth; // this seems the optimal distance for zelda + static constexpr double dist2 = 1.2 * dist1; + + std::array v; + for (auto& vtx : v) + { + vtx.x = cursor.x * (bndright - bndleft) / 2 + (bndleft + bndright) / 2; + + static constexpr double bndup = -0.315447; + static constexpr double bnddown = 0.85; + + if (sensor_bar_on_top) + vtx.y = cursor.y * (bndup - bnddown) / 2 + (bndup + bnddown) / 2; + else + vtx.y = cursor.y * (bndup - bnddown) / 2 - (bndup + bnddown) / 2; + + vtx.z = 0; + } + + v[0].x -= (cursor.z * 0.5 + 1) * dist1; + v[1].x += (cursor.z * 0.5 + 1) * dist1; + v[2].x -= (cursor.z * 0.5 + 1) * dist2; + v[3].x += (cursor.z * 0.5 + 1) * dist2; + +#define printmatrix(m) \ + PanicAlert("%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", m[0][0], m[0][1], m[0][2], \ + m[0][3], m[1][0], m[1][1], m[1][2], m[1][3], m[2][0], m[2][1], m[2][2], m[2][3], \ + m[3][0], m[3][1], m[3][2], m[3][3]) + Matrix rot, tot; + static Matrix scale; + MatrixScale(scale, 1, camWidth / camHeight, 1); + MatrixRotationByZ(rot, ir_sin, ir_cos); + MatrixMultiply(tot, scale, rot); + + for (std::size_t i = 0; i < v.size(); i++) + { + MatrixTransformVertex(tot, v[i]); + + if ((v[i].x < -1) || (v[i].x > 1) || (v[i].y < -1) || (v[i].y > 1)) + continue; + + x[i] = static_cast(lround((v[i].x + 1) / 2 * (camWidth - 1))); + y[i] = static_cast(lround((v[i].y + 1) / 2 * (camHeight - 1))); + } + + // IR data is read from offset 0x37 on real hardware + auto& data = reg_data.camera_data; + // A maximum of 36 bytes: + std::fill(std::begin(data), std::end(data), 0xff); + + // Fill report with valid data when full handshake was done + // TODO: kill magic number: + if (reg_data.data[0x30]) + { + switch (reg_data.mode) + { + case IR_MODE_BASIC: + { + auto* const irdata = reinterpret_cast(data); + for (unsigned int i = 0; i < 2; ++i) + { + if (x[i * 2] < 1024 && y[i * 2] < 768) + { + irdata[i].x1 = static_cast(x[i * 2]); + irdata[i].x1hi = x[i * 2] >> 8; + + irdata[i].y1 = static_cast(y[i * 2]); + irdata[i].y1hi = y[i * 2] >> 8; + } + if (x[i * 2 + 1] < 1024 && y[i * 2 + 1] < 768) + { + irdata[i].x2 = static_cast(x[i * 2 + 1]); + irdata[i].x2hi = x[i * 2 + 1] >> 8; + + irdata[i].y2 = static_cast(y[i * 2 + 1]); + irdata[i].y2hi = y[i * 2 + 1] >> 8; + } + } + break; + } + case IR_MODE_EXTENDED: + { + auto* const irdata = reinterpret_cast(data); + for (unsigned int i = 0; i < 4; ++i) + if (x[i] < 1024 && y[i] < 768) + { + irdata[i].x = static_cast(x[i]); + irdata[i].xhi = x[i] >> 8; + + irdata[i].y = static_cast(y[i]); + irdata[i].yhi = y[i] >> 8; + + irdata[i].size = 10; + } + break; + } + case IR_MODE_FULL: + { + auto* const irdata = reinterpret_cast(data); + for (unsigned int i = 0; i < 4; ++i) + if (x[i] < 1024 && y[i] < 768) + { + irdata[i].x = static_cast(x[i]); + irdata[i].xhi = x[i] >> 8; + + irdata[i].y = static_cast(y[i]); + irdata[i].yhi = y[i] >> 8; + + irdata[i].size = 10; + + // TODO: implement these sensibly: + // TODO: do high bits of x/y min/max need to be set to zero? + irdata[i].xmin = 0; + irdata[i].ymin = 0; + irdata[i].xmax = 0; + irdata[i].ymax = 0; + irdata[i].zero = 0; + irdata[i].intensity = 0; + } + break; + } + default: + // This seems to be fairly common, 0xff data is sent in this case: + // WARN_LOG(WIIMOTE, "Game is requesting IR data before setting IR mode."); + break; + } + } +} + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Camera.h b/Source/Core/Core/HW/WiimoteEmu/Camera.h new file mode 100644 index 0000000000..fdcc48858b --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/Camera.h @@ -0,0 +1,104 @@ +// 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/Dynamics.h" +#include "Core/HW/WiimoteEmu/I2CBus.h" +#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h" + +namespace WiimoteEmu +{ +// Four bytes for two objects. Filled with 0xFF if empty +struct IRBasic +{ + u8 x1; + u8 y1; + u8 x2hi : 2; + u8 y2hi : 2; + u8 x1hi : 2; + u8 y1hi : 2; + u8 x2; + u8 y2; +}; +static_assert(sizeof(IRBasic) == 5, "Wrong size"); + +// Three bytes for one object +struct IRExtended +{ + u8 x; + u8 y; + u8 size : 4; + u8 xhi : 2; + u8 yhi : 2; +}; +static_assert(sizeof(IRExtended) == 3, "Wrong size"); + +// Nine bytes for one object +// first 3 bytes are the same as extended +struct IRFull : IRExtended +{ + u8 xmin : 7; + u8 : 1; + u8 ymin : 7; + u8 : 1; + u8 xmax : 7; + u8 : 1; + u8 ymax : 7; + u8 : 1; + u8 zero; + u8 intensity; +}; +static_assert(sizeof(IRFull) == 9, "Wrong size"); + +class CameraLogic : public I2CSlave +{ +public: + enum : u8 + { + IR_MODE_BASIC = 1, + IR_MODE_EXTENDED = 3, + IR_MODE_FULL = 5, + }; + +private: + // TODO: some of this memory is write-only and should return error 7. +#pragma pack(push, 1) + struct Register + { + // Contains sensitivity and other unknown data + // TODO: Do the IR and Camera enabling reports write to the i2c bus? + // TODO: Does disabling the camera peripheral reset the mode or sensitivity? + // TODO: Break out this "data" array into some known members + u8 data[0x33]; + u8 mode; + u8 unk[3]; + // addr: 0x37 + u8 camera_data[36]; + u8 unk2[165]; + }; +#pragma pack(pop) + + static_assert(0x100 == sizeof(Register)); + +public: + static constexpr u8 I2C_ADDR = 0x58; + + // The real wiimote reads camera data from the i2c bus at offset 0x37: + static const u8 REPORT_DATA_OFFSET = offsetof(Register, camera_data); + + void Reset(); + void DoState(PointerWrap& p); + void Update(const ControllerEmu::Cursor::StateData& cursor, const NormalizedAccelData& accel, + bool sensor_bar_on_top); + +private: + Register reg_data; + + 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; +}; +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp b/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp new file mode 100644 index 0000000000..47214ea46b --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp @@ -0,0 +1,262 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/HW/WiimoteEmu/Dynamics.h" + +#include + +#include "Common/MathUtil.h" +#include "Core/Config/WiimoteInputSettings.h" +#include "Core/HW/Wiimote.h" +#include "Core/HW/WiimoteEmu/WiimoteEmu.h" +#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h" +#include "InputCommon/ControllerEmu/ControlGroup/Force.h" +#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h" + +namespace WiimoteEmu +{ +constexpr int SHAKE_FREQ = 6; +// Frame count of one up/down shake +// < 9 no shake detection in "Wario Land: Shake It" +constexpr int SHAKE_STEP_MAX = ::Wiimote::UPDATE_FREQ / SHAKE_FREQ; + +void EmulateShake(NormalizedAccelData* const accel, ControllerEmu::Buttons* const buttons_group, + const double intensity, u8* const shake_step) +{ + // shake is a bitfield of X,Y,Z shake button states + static const unsigned int btns[] = {0x01, 0x02, 0x04}; + unsigned int shake = 0; + buttons_group->GetState(&shake, btns); + + for (int i = 0; i != 3; ++i) + { + if (shake & (1 << i)) + { + (&(accel->x))[i] += std::sin(MathUtil::TAU * shake_step[i] / SHAKE_STEP_MAX) * intensity; + shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX; + } + else + shake_step[i] = 0; + } +} + +void EmulateDynamicShake(NormalizedAccelData* const accel, DynamicData& dynamic_data, + ControllerEmu::Buttons* const buttons_group, + const DynamicConfiguration& config, u8* const shake_step) +{ + // shake is a bitfield of X,Y,Z shake button states + static const unsigned int btns[] = {0x01, 0x02, 0x04}; + unsigned int shake = 0; + buttons_group->GetState(&shake, btns); + + for (int i = 0; i != 3; ++i) + { + if ((shake & (1 << i)) && dynamic_data.executing_frames_left[i] == 0) + { + dynamic_data.timing[i]++; + } + else if (dynamic_data.executing_frames_left[i] > 0) + { + (&(accel->x))[i] += + std::sin(MathUtil::TAU * shake_step[i] / SHAKE_STEP_MAX) * dynamic_data.intensity[i]; + shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX; + dynamic_data.executing_frames_left[i]--; + } + else if (shake == 0 && dynamic_data.timing[i] > 0) + { + if (dynamic_data.timing[i] > config.frames_needed_for_high_intensity) + { + dynamic_data.intensity[i] = config.high_intensity; + } + else if (dynamic_data.timing[i] < config.frames_needed_for_low_intensity) + { + dynamic_data.intensity[i] = config.low_intensity; + } + else + { + dynamic_data.intensity[i] = config.med_intensity; + } + dynamic_data.timing[i] = 0; + dynamic_data.executing_frames_left[i] = config.frames_to_execute; + } + else + { + shake_step[i] = 0; + } + } +} + +void EmulateTilt(NormalizedAccelData* const accel, ControllerEmu::Tilt* const tilt_group, + const bool sideways, const bool upright) +{ + // 180 degrees + const ControllerEmu::Tilt::StateData state = tilt_group->GetState(); + const ControlState roll = state.x * MathUtil::PI; + const ControlState pitch = state.y * MathUtil::PI; + + // Some notes that no one will understand but me :p + // left, forward, up + // lr/ left == negative for all orientations + // ud/ up == negative for upright longways + // fb/ forward == positive for (sideways flat) + + // Determine which axis is which direction + const u32 ud = upright ? (sideways ? 0 : 1) : 2; + const u32 lr = sideways; + const u32 fb = upright ? 2 : (sideways ? 0 : 1); + + // Sign fix + std::array sgn{{-1, 1, 1}}; + if (sideways && !upright) + sgn[fb] *= -1; + if (!sideways && upright) + sgn[ud] *= -1; + + (&accel->x)[ud] = (sin((MathUtil::PI / 2) - std::max(fabs(roll), fabs(pitch)))) * sgn[ud]; + (&accel->x)[lr] = -sin(roll) * sgn[lr]; + (&accel->x)[fb] = sin(pitch) * sgn[fb]; +} + +void EmulateSwing(NormalizedAccelData* const accel, ControllerEmu::Force* const swing_group, + const double intensity, const bool sideways, const bool upright) +{ + const ControllerEmu::Force::StateData swing = swing_group->GetState(); + + // Determine which axis is which direction + const std::array axis_map{{ + upright ? (sideways ? 0 : 1) : 2, // up/down + sideways, // left/right + upright ? 2 : (sideways ? 0 : 1), // forward/backward + }}; + + // Some orientations have up as positive, some as negative + // same with forward + std::array g_dir{{-1, -1, -1}}; + if (sideways && !upright) + g_dir[axis_map[2]] *= -1; + if (!sideways && upright) + g_dir[axis_map[0]] *= -1; + + for (std::size_t i = 0; i < swing.size(); ++i) + (&accel->x)[axis_map[i]] += swing[i] * g_dir[i] * intensity; +} + +void EmulateDynamicSwing(NormalizedAccelData* const accel, DynamicData& dynamic_data, + ControllerEmu::Force* const swing_group, + const DynamicConfiguration& config, const bool sideways, + const bool upright) +{ + const ControllerEmu::Force::StateData swing = swing_group->GetState(); + + // Determine which axis is which direction + const std::array axis_map{{ + upright ? (sideways ? 0 : 1) : 2, // up/down + sideways, // left/right + upright ? 2 : (sideways ? 0 : 1), // forward/backward + }}; + + // Some orientations have up as positive, some as negative + // same with forward + std::array g_dir{{-1, -1, -1}}; + if (sideways && !upright) + g_dir[axis_map[2]] *= -1; + if (!sideways && upright) + g_dir[axis_map[0]] *= -1; + + for (std::size_t i = 0; i < swing.size(); ++i) + { + if (swing[i] > 0 && dynamic_data.executing_frames_left[i] == 0) + { + dynamic_data.timing[i]++; + } + else if (dynamic_data.executing_frames_left[i] > 0) + { + (&accel->x)[axis_map[i]] += g_dir[i] * dynamic_data.intensity[i]; + dynamic_data.executing_frames_left[i]--; + } + else if (swing[i] == 0 && dynamic_data.timing[i] > 0) + { + if (dynamic_data.timing[i] > config.frames_needed_for_high_intensity) + { + dynamic_data.intensity[i] = config.high_intensity; + } + else if (dynamic_data.timing[i] < config.frames_needed_for_low_intensity) + { + dynamic_data.intensity[i] = config.low_intensity; + } + else + { + dynamic_data.intensity[i] = config.med_intensity; + } + dynamic_data.timing[i] = 0; + dynamic_data.executing_frames_left[i] = config.frames_to_execute; + } + } +} + +WiimoteCommon::DataReportBuilder::AccelData DenormalizeAccelData(const NormalizedAccelData& accel, + u16 zero_g, u16 one_g) +{ + const u8 accel_range = one_g - zero_g; + + const s32 unclamped_x = (s32)(accel.x * accel_range + zero_g); + const s32 unclamped_y = (s32)(accel.y * accel_range + zero_g); + const s32 unclamped_z = (s32)(accel.z * accel_range + zero_g); + + WiimoteCommon::DataReportBuilder::AccelData result; + + result.x = MathUtil::Clamp(unclamped_x, 0, 0x3ff); + result.y = MathUtil::Clamp(unclamped_y, 0, 0x3ff); + result.z = MathUtil::Clamp(unclamped_z, 0, 0x3ff); + + return result; +} + +void Wiimote::GetAccelData(NormalizedAccelData* accel) +{ + const bool is_sideways = IsSideways(); + const bool is_upright = IsUpright(); + + EmulateTilt(accel, m_tilt, is_sideways, is_upright); + + DynamicConfiguration swing_config; + swing_config.low_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_SLOW); + swing_config.med_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_MEDIUM); + swing_config.high_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_FAST); + swing_config.frames_needed_for_high_intensity = + Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_FAST); + swing_config.frames_needed_for_low_intensity = + Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_SLOW); + swing_config.frames_to_execute = Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_LENGTH); + + EmulateSwing(accel, m_swing, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_MEDIUM), + is_sideways, is_upright); + EmulateSwing(accel, m_swing_slow, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_SLOW), + is_sideways, is_upright); + EmulateSwing(accel, m_swing_fast, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_FAST), + is_sideways, is_upright); + EmulateDynamicSwing(accel, m_swing_dynamic_data, m_swing_dynamic, swing_config, is_sideways, + is_upright); + + DynamicConfiguration shake_config; + shake_config.low_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT); + shake_config.med_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM); + shake_config.high_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD); + shake_config.frames_needed_for_high_intensity = + Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_HARD); + shake_config.frames_needed_for_low_intensity = + Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_SOFT); + shake_config.frames_to_execute = Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_LENGTH); + + EmulateShake(accel, m_shake, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM), + m_shake_step.data()); + EmulateShake(accel, m_shake_soft, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT), + m_shake_soft_step.data()); + EmulateShake(accel, m_shake_hard, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD), + m_shake_hard_step.data()); + EmulateDynamicShake(accel, m_shake_dynamic_data, m_shake_dynamic, shake_config, + m_shake_dynamic_step.data()); +} + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Dynamics.h b/Source/Core/Core/HW/WiimoteEmu/Dynamics.h new file mode 100644 index 0000000000..116c03d912 --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/Dynamics.h @@ -0,0 +1,68 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Core/HW/WiimoteCommon/DataReport.h" +#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h" +#include "InputCommon/ControllerEmu/ControlGroup/Force.h" +#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h" + +namespace WiimoteEmu +{ +struct NormalizedAccelData +{ + double x, y, z; +}; + +// Used for a dynamic swing or +// shake +struct DynamicData +{ + std::array timing; // Hold length in frames for each axis + std::array intensity; // Swing or shake intensity + std::array executing_frames_left; // Number of frames to execute the intensity operation +}; + +// Used for a dynamic swing or +// shake. This is used to pass +// in data that defines the dynamic +// action +struct DynamicConfiguration +{ + double low_intensity; + int frames_needed_for_low_intensity; + + double med_intensity; + // Frames needed for med intensity can be calculated between high & low + + double high_intensity; + int frames_needed_for_high_intensity; + + int frames_to_execute; // How many frames should we execute the action for? +}; + +void EmulateShake(NormalizedAccelData* accel, ControllerEmu::Buttons* buttons_group, + double intensity, u8* shake_step); + +void EmulateDynamicShake(NormalizedAccelData* accel, DynamicData& dynamic_data, + ControllerEmu::Buttons* buttons_group, const DynamicConfiguration& config, + u8* shake_step); + +void EmulateTilt(NormalizedAccelData* accel, ControllerEmu::Tilt* tilt_group, bool sideways = false, + bool upright = false); + +void EmulateSwing(NormalizedAccelData* accel, ControllerEmu::Force* swing_group, double intensity, + bool sideways = false, bool upright = false); + +void EmulateDynamicSwing(NormalizedAccelData* accel, DynamicData& dynamic_data, + ControllerEmu::Force* swing_group, const DynamicConfiguration& config, + bool sideways = false, bool upright = false); + +WiimoteCommon::DataReportBuilder::AccelData DenormalizeAccelData(const NormalizedAccelData& accel, + u16 zero_g, u16 one_g); + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 25f0726ded..cf71ba7b50 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -2,24 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -/* HID reports access guide. */ - -/* 0x10 - 0x1a Output EmuMain.cpp: HidOutputReport() - 0x10 - 0x14: General - 0x15: Status report request from the Wii - 0x16 and 0x17: Write and read memory or registers - 0x19 and 0x1a: General - 0x20 - 0x22 Input EmuMain.cpp: HidOutputReport() to the destination - 0x15 leads to a 0x20 Input report - 0x17 leads to a 0x21 Input report - 0x10 - 0x1a leads to a 0x22 Input report - 0x30 - 0x3f Input This file: Update() */ - #include -#include -#include -#include -#include #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" @@ -29,21 +12,21 @@ #include "Common/Swap.h" #include "Core/Core.h" #include "Core/HW/WiimoteCommon/WiimoteHid.h" -#include "Core/HW/WiimoteEmu/Attachment/Attachment.h" #include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include "Core/HW/WiimoteReal/WiimoteReal.h" -#include "InputCommon/ControllerEmu/ControlGroup/Extension.h" +#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h" #include "InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.h" namespace WiimoteEmu { -void Wiimote::ReportMode(const wm_report_mode* const dr) +using namespace WiimoteCommon; + +void Wiimote::HandleReportMode(const OutputReportMode& dr) { - if (dr->mode < RT_REPORT_CORE || dr->mode > RT_REPORT_INTERLEAVE2 || - (dr->mode > RT_REPORT_CORE_ACCEL_IR10_EXT6 && dr->mode < RT_REPORT_EXT21)) + if (!DataReportBuilder::IsValidMode(dr.mode)) { // A real wiimote ignores the entire message if the mode is invalid. - WARN_LOG(WIIMOTE, "Game requested invalid report mode: 0x%02x", dr->mode); + WARN_LOG(WIIMOTE, "Game requested invalid report mode: 0x%02x", int(dr.mode)); return; } @@ -51,229 +34,192 @@ void Wiimote::ReportMode(const wm_report_mode* const dr) // even on REPORT_CORE and continuous off when the buttons haven't changed. // But.. it is sent after the ACK - // INFO_LOG(WIIMOTE, "Set data report mode"); + // DEBUG_LOG(WIIMOTE, "Set data report mode"); // DEBUG_LOG(WIIMOTE, " Rumble: %x", dr->rumble); // DEBUG_LOG(WIIMOTE, " Continuous: %x", dr->continuous); // DEBUG_LOG(WIIMOTE, " Mode: 0x%02x", dr->mode); - m_reporting_auto = dr->continuous; - m_reporting_mode = dr->mode; + m_reporting_continuous = dr.continuous; + m_reporting_mode = dr.mode; + + SendAck(OutputReportID::REPORT_MODE, ErrorCode::SUCCESS); } -/* Here we process the Output Reports that the Wii sends. Our response will be - an Input Report back to the Wii. Input and Output is from the Wii's - perspective, Output means data to the Wiimote (from the Wii), Input means - data from the Wiimote. - - The call browser: - - 1. Wiimote_InterruptChannel > InterruptChannel > HidOutputReport - 2. Wiimote_ControlChannel > ControlChannel > HidOutputReport - - The IR enable/disable and speaker enable/disable and mute/unmute values are - bit2: 0 = Disable (0x02), 1 = Enable (0x06) -*/ -void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack) +// Tests that we have enough bytes for the report before we run the handler. +template +void Wiimote::InvokeHandler(H&& handler, const WiimoteCommon::OutputReportGeneric& rpt, u32 size) { - DEBUG_LOG(WIIMOTE, "HidOutputReport (page: %i, cid: 0x%02x, wm: 0x%02x)", m_index, - m_reporting_channel, sr->wm); + if (size < sizeof(T)) + { + ERROR_LOG(WIIMOTE, "InvokeHandler: report: 0x%02x invalid size: %d", int(rpt.rpt_id), size); + } + + (this->*handler)(*reinterpret_cast(rpt.data)); +} + +// Here we process the Output Reports that the Wii sends. Our response will be +// an Input Report back to the Wii. Input and Output is from the Wii's +// perspective, Output means data to the Wiimote (from the Wii), Input means +// data from the Wiimote. +// +// The call browser: +// +// 1. Wiimote_InterruptChannel > InterruptChannel > HIDOutputReport +// 2. Wiimote_ControlChannel > ControlChannel > HIDOutputReport + +void Wiimote::HIDOutputReport(const void* data, u32 size) +{ + if (!size) + { + ERROR_LOG(WIIMOTE, "HIDOutputReport: zero sized data"); + return; + } + + auto& rpt = *static_cast(data); + const int rpt_size = size - rpt.HEADER_SIZE; + + DEBUG_LOG(WIIMOTE, "HIDOutputReport (page: %i, cid: 0x%02x, wm: 0x%02x)", m_index, + m_reporting_channel, int(rpt.rpt_id)); // WiiBrew: // In every single Output Report, bit 0 (0x01) of the first byte controls the Rumble feature. - m_rumble_on = sr->rumble; + InvokeHandler(&Wiimote::HandleReportRumble, rpt, rpt_size); - switch (sr->wm) + switch (rpt.rpt_id) { - case RT_RUMBLE: + case OutputReportID::RUMBLE: // This is handled above. - // A real wiimote never ACKs a rumble report: - return; break; - - case RT_LEDS: - // INFO_LOG(WIIMOTE, "Set LEDs: 0x%02x", sr->data[0]); - m_status.leds = sr->data[0] >> 4; + case OutputReportID::LEDS: + InvokeHandler(&Wiimote::HandleReportLeds, rpt, rpt_size); break; - - case RT_REPORT_MODE: - ReportMode(reinterpret_cast(sr->data)); + case OutputReportID::REPORT_MODE: + InvokeHandler(&Wiimote::HandleReportMode, rpt, rpt_size); break; - - case RT_IR_PIXEL_CLOCK: - // INFO_LOG(WIIMOTE, "WM IR Clock: 0x%02x", sr->data[0]); - // Camera data is currently always updated. Ignoring pixel clock status. + case OutputReportID::IR_PIXEL_CLOCK: + InvokeHandler(&Wiimote::HandleIRPixelClock, rpt, rpt_size); break; - - case RT_SPEAKER_ENABLE: - // INFO_LOG(WIIMOTE, "WM Speaker Enable: %02x", sr->enable); - m_status.speaker = sr->enable; + case OutputReportID::SPEAKER_ENABLE: + InvokeHandler(&Wiimote::HandleSpeakerEnable, rpt, rpt_size); break; - - case RT_REQUEST_STATUS: - RequestStatus(reinterpret_cast(sr->data)); - // No ACK: - return; + case OutputReportID::REQUEST_STATUS: + InvokeHandler(&Wiimote::HandleRequestStatus, rpt, rpt_size); break; - - case RT_WRITE_DATA: - WriteData(reinterpret_cast(sr->data)); - // Sends it's own ACK conditionally: - return; + case OutputReportID::WRITE_DATA: + InvokeHandler(&Wiimote::HandleWriteData, rpt, rpt_size); break; - - case RT_READ_DATA: - ReadData(reinterpret_cast(sr->data)); - // No ACK: - return; + case OutputReportID::READ_DATA: + InvokeHandler(&Wiimote::HandleReadData, rpt, rpt_size); break; - - case RT_WRITE_SPEAKER_DATA: - // TODO: Does speaker mute stop speaker data processing? - // (important to keep decoder in proper state) - if (!m_speaker_mute) - { - auto sd = reinterpret_cast(sr->data); - if (sd->length > ArraySize(sd->data)) - { - ERROR_LOG(WIIMOTE, "Bad speaker data length: %d", sd->length); - } - else - { - SpeakerData(sd->data, sd->length); - } - } - // No ACK: - return; + case OutputReportID::SPEAKER_DATA: + InvokeHandler(&Wiimote::HandleSpeakerData, rpt, rpt_size); break; - - case RT_SPEAKER_MUTE: - m_speaker_mute = sr->enable; + case OutputReportID::SPEAKER_MUTE: + InvokeHandler(&Wiimote::HandleSpeakerMute, rpt, rpt_size); break; - - case RT_IR_LOGIC: - // Camera data is currently always updated. Just saving this for status requests. - m_status.ir = sr->enable; + case OutputReportID::IR_LOGIC: + InvokeHandler(&Wiimote::HandleIRLogic, rpt, rpt_size); break; - default: - PanicAlert("HidOutputReport: Unknown channel 0x%02x", sr->wm); - return; + PanicAlert("HidOutputReport: Unknown report ID 0x%02x", int(rpt.rpt_id)); break; } - - if (sr->ack) - SendAck(sr->wm); } -/* This will generate the 0x22 acknowledgement for most Input reports. - It has the form of "a1 22 00 00 _reportID 00". - The first two bytes are the core buttons data, - 00 00 means nothing is pressed. - The last byte is the success code 00. */ -void Wiimote::SendAck(u8 report_id, WiimoteErrorCode error_code) +void Wiimote::CallbackInterruptChannel(const u8* data, u32 size) { - TypedHidPacket rpt; - rpt.type = HID_TYPE_DATA; - rpt.param = HID_PARAM_INPUT; - rpt.report_id = RT_ACK_DATA; + Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, size); +} +void Wiimote::SendAck(OutputReportID rpt_id, ErrorCode error_code) +{ + TypedHIDInputData rpt(InputReportID::ACK); auto& ack = rpt.data; ack.buttons = m_status.buttons; - ack.reportID = report_id; - ack.errorID = static_cast(error_code); + ack.rpt_id = rpt_id; + ack.error_code = error_code; - Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), - rpt.GetSize()); + CallbackInterruptChannel(rpt.GetData(), rpt.GetSize()); } void Wiimote::HandleExtensionSwap() { - // handle switch extension - if (m_extension->active_extension != m_extension->switch_extension) + const ExtensionNumber desired_extension = + static_cast(m_attachments->GetSelectedAttachment()); + + if (GetActiveExtensionNumber() != desired_extension) { - // if an extension is currently connected and we want to switch to a different extension - if ((m_extension->active_extension > 0) && m_extension->switch_extension) + if (GetActiveExtensionNumber()) { - // detach extension first, wait til next Update() or RequestStatus() call to change to the new - // extension - m_extension->active_extension = 0; + // First we must detach the current extension. + // The next call will change to the new extension if needed. + m_active_extension = ExtensionNumber::NONE; } else { - // set the wanted extension - m_extension->active_extension = m_extension->switch_extension; + m_active_extension = desired_extension; } - // TODO: this is a bit ugly: - if (m_extension->active_extension != 0) - m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic); - else - m_motion_plus_logic.extension_port.SetAttachment(nullptr); - - // reset register - ((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension].get()) - ->Reset(); + // TODO: Attach directly when not using M+. + m_motion_plus.AttachExtension(GetActiveExtension()); + GetActiveExtension()->Reset(); } } -void Wiimote::RequestStatus(const wm_request_status* const rs) +void Wiimote::HandleRequestStatus(const OutputReportRequestStatus&) { - // update status struct + // FYI: buttons are updated in Update() for determinism + + // Update status struct m_status.extension = m_extension_port.IsDeviceConnected(); + + // TODO: Battery level will break determinism in TAS/Netplay + // Battery levels in voltage // 0x00 - 0x32: level 1 // 0x33 - 0x43: level 2 // 0x33 - 0x54: level 3 // 0x55 - 0xff: level 4 m_status.battery = (u8)(m_battery_setting->GetValue() * 0xff); + // Less than 0x20 triggers the low-battery flag: m_status.battery_low = m_status.battery < 0x20; - // set up report - TypedHidPacket rpt; - rpt.type = HID_TYPE_DATA; - rpt.param = HID_PARAM_INPUT; - rpt.report_id = RT_STATUS_REPORT; - - // status values + TypedHIDInputData rpt(InputReportID::STATUS); rpt.data = m_status; - - // send report - Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), - rpt.GetSize()); + CallbackInterruptChannel(rpt.GetData(), rpt.GetSize()); } -/* Write data to Wiimote and Extensions registers. */ -void Wiimote::WriteData(const wm_write_data* const wd) +void Wiimote::HandleWriteData(const OutputReportWriteData& wd) { - u16 address = Common::swap16(wd->address); + // TODO: Are writes ignored during an active read request? - INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x @ 0x%02x (%d)", wd->space, - wd->slave_address, address, wd->size); + u16 address = Common::swap16(wd.address); - if (0 == wd->size || wd->size > 16) + DEBUG_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x @ 0x%02x (%d)", wd.space, + wd.slave_address, address, wd.size); + + if (0 == wd.size || wd.size > 16) { - WARN_LOG(WIIMOTE, "WriteData: invalid size: %d", wd->size); + WARN_LOG(WIIMOTE, "WriteData: invalid size: %d", wd.size); // A real wiimote silently ignores such a request: return; } - WiimoteErrorCode error_code = WiimoteErrorCode::SUCCESS; + ErrorCode error_code = ErrorCode::SUCCESS; - switch (static_cast(wd->space)) + switch (static_cast(wd.space)) { - case WiimoteAddressSpace::EEPROM: + case AddressSpace::EEPROM: { - // Write to EEPROM - - if (address + wd->size > WIIMOTE_EEPROM_SIZE) + if (address + wd.size > EEPROM_FREE_SIZE) { WARN_LOG(WIIMOTE, "WriteData: address + size out of bounds!"); - error_code = WiimoteErrorCode::INVALID_ADDRESS; + error_code = ErrorCode::INVALID_ADDRESS; } else { - std::copy_n(wd->data, wd->size, m_eeprom + address); + std::copy_n(wd.data, wd.size, m_eeprom.data.data() + address); // Write mii data to file if (address >= 0x0FCA && address < 0x12C0) @@ -282,61 +228,145 @@ void Wiimote::WriteData(const wm_write_data* const wd) std::ofstream file; File::OpenFStream(file, File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/mii.bin", std::ios::binary | std::ios::out); - file.write((char*)m_eeprom + 0x0FCA, 0x02f0); + file.write((char*)m_eeprom.data.data() + 0x0FCA, 0x02f0); file.close(); } } } break; - case WiimoteAddressSpace::I2C_BUS: - case WiimoteAddressSpace::I2C_BUS_ALT: + case AddressSpace::I2C_BUS: + case AddressSpace::I2C_BUS_ALT: { - // Write to Control Register + // Attempting to access the EEPROM directly over i2c results in error 8. + if (EEPROM_I2C_ADDR == m_read_request.slave_address) + { + WARN_LOG(WIIMOTE, "Attempt to write EEPROM directly."); + error_code = ErrorCode::INVALID_ADDRESS; + break; + } // Top byte of address is ignored on the bus. - auto const bytes_written = - m_i2c_bus.BusWrite(wd->slave_address, (u8)address, wd->size, wd->data); - if (bytes_written != wd->size) + auto const bytes_written = m_i2c_bus.BusWrite(wd.slave_address, (u8)address, wd.size, wd.data); + if (bytes_written != wd.size) { // A real wiimote gives error 7 for failed write to i2c bus (mainly a non-existant slave) - error_code = WiimoteErrorCode::NACK; + error_code = ErrorCode::NACK; } } break; default: - WARN_LOG(WIIMOTE, "WriteData: invalid address space: 0x%x", wd->space); + WARN_LOG(WIIMOTE, "WriteData: invalid address space: 0x%x", wd.space); // A real wiimote gives error 6: - error_code = WiimoteErrorCode::INVALID_SPACE; + error_code = ErrorCode::INVALID_SPACE; break; } - SendAck(RT_WRITE_DATA, error_code); + SendAck(OutputReportID::WRITE_DATA, error_code); } -/* Read data from Wiimote and Extensions registers. */ -void Wiimote::ReadData(const wm_read_data* const rd) +void Wiimote::HandleReportRumble(const WiimoteCommon::OutputReportRumble& rpt) +{ + SetRumble(rpt.rumble); + + // FYI: A real wiimote never seems to ACK a rumble report: +} + +void Wiimote::HandleReportLeds(const WiimoteCommon::OutputReportLeds& rpt) +{ + m_status.leds = rpt.leds; + + if (rpt.ack) + SendAck(OutputReportID::LEDS, ErrorCode::SUCCESS); +} + +void Wiimote::HandleIRPixelClock(const WiimoteCommon::OutputReportEnableFeature& rpt) +{ + // INFO_LOG(WIIMOTE, "WM IR Clock: %02x", erpt.enable); + + // FYI: Camera data is currently always updated. Ignoring pixel clock status. + + if (rpt.ack) + SendAck(OutputReportID::IR_PIXEL_CLOCK, ErrorCode::SUCCESS); +} + +void Wiimote::HandleIRLogic(const WiimoteCommon::OutputReportEnableFeature& rpt) +{ + // FYI: Camera data is currently always updated. We just save this for status reports. + + m_status.ir = rpt.enable; + + if (rpt.ack) + SendAck(OutputReportID::IR_LOGIC, ErrorCode::SUCCESS); +} + +void Wiimote::HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature& rpt) +{ + m_speaker_mute = rpt.enable; + + if (rpt.ack) + SendAck(OutputReportID::SPEAKER_MUTE, ErrorCode::SUCCESS); +} + +void Wiimote::HandleSpeakerEnable(const WiimoteCommon::OutputReportEnableFeature& rpt) +{ + // INFO_LOG(WIIMOTE, "WM Speaker Enable: %02x", erpt.enable); + m_status.speaker = rpt.enable; + + if (rpt.ack) + SendAck(OutputReportID::SPEAKER_ENABLE, ErrorCode::SUCCESS); +} + +void Wiimote::HandleSpeakerData(const WiimoteCommon::OutputReportSpeakerData& rpt) +{ + // TODO: Does speaker_mute stop speaker data processing? + // and what about speaker_enable? + // (important to keep decoder in proper state) + if (!m_speaker_mute) + { + if (rpt.length > ArraySize(rpt.data)) + { + ERROR_LOG(WIIMOTE, "Bad speaker data length: %d", rpt.length); + } + else + { + // Speaker Pan + // GUI clamps pan setting from -127 to 127. Why? + const auto pan = int(m_options->numeric_settings[0]->GetValue() * 100); + + m_speaker_logic.SpeakerData(rpt.data, rpt.length, pan); + } + } + + // FYI: Speaker data reports normally do not ACK but I have seen them ACK with error codes + // It seems some wiimotes do this when receiving data too quickly. + // More investigation is needed. +} + +void Wiimote::HandleReadData(const OutputReportReadData& rd) { if (m_read_request.size) { // There is already an active read request. // A real wiimote ignores the new one. + WARN_LOG(WIIMOTE, "ReadData: ignoring read during active request."); return; } - // Save the request and process it on the next "Update()" calls - m_read_request.space = static_cast(rd->space); - m_read_request.slave_address = rd->slave_address; - m_read_request.address = Common::swap16(rd->address); + // Save the request and process it on the next "Update()" call(s) + m_read_request.space = static_cast(rd.space); + m_read_request.slave_address = rd.slave_address; + m_read_request.address = Common::swap16(rd.address); // A zero size request is just ignored, like on the real wiimote. - m_read_request.size = Common::swap16(rd->size); + m_read_request.size = Common::swap16(rd.size); - INFO_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", m_read_request.space, - m_read_request.slave_address, m_read_request.address, m_read_request.size); + DEBUG_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", int(m_read_request.space), + m_read_request.slave_address, m_read_request.address, m_read_request.size); // Send up to one read-data-reply. // If more data needs to be sent it will happen on the next "Update()" + // TODO: should this be removed and let Update() take care of it? ProcessReadDataRequest(); } @@ -352,41 +382,30 @@ bool Wiimote::ProcessReadDataRequest() return false; } - TypedHidPacket rpt; - rpt.type = HID_TYPE_DATA; - rpt.param = HID_PARAM_INPUT; - rpt.report_id = RT_READ_DATA_REPLY; + TypedHIDInputData rpt(InputReportID::READ_DATA_REPLY); + auto& reply = rpt.data; - auto reply = &rpt.data; - reply->buttons = m_status.buttons; - reply->address = Common::swap16(m_read_request.address); + reply.buttons = m_status.buttons; + reply.address = Common::swap16(m_read_request.address); // Pre-fill with zeros in case of read-error or read < 16-bytes: - std::fill(std::begin(reply->data), std::end(reply->data), 0x00); + std::fill(std::begin(reply.data), std::end(reply.data), 0x00); - WiimoteErrorCode error_code = WiimoteErrorCode::SUCCESS; + ErrorCode error_code = ErrorCode::SUCCESS; switch (m_read_request.space) { - case WiimoteAddressSpace::EEPROM: + case AddressSpace::EEPROM: { - // Read from EEPROM - if (m_read_request.address + m_read_request.size > WIIMOTE_EEPROM_FREE_SIZE) + if (m_read_request.address + m_read_request.size > EEPROM_FREE_SIZE) { - if (m_read_request.address + m_read_request.size > WIIMOTE_EEPROM_SIZE) - { - WARN_LOG(WIIMOTE, "ReadData: address + size out of bounds!"); - } - // Generate a read error. Even if the start of the block is readable a real wiimote just sends // error code 8 // The real Wiimote generate an error for the first // request to 0x1770 if we dont't replicate that the game will never - // read the calibration data at the beginning of Eeprom. I think this - // error is supposed to occur when we try to read above the freely - // usable space that ends at 0x16ff. - error_code = WiimoteErrorCode::INVALID_ADDRESS; + // read the calibration data at the beginning of Eeprom. + error_code = ErrorCode::INVALID_ADDRESS; } else { @@ -398,50 +417,57 @@ bool Wiimote::ProcessReadDataRequest() std::ifstream file; File::OpenFStream(file, (File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/mii.bin").c_str(), std::ios::binary | std::ios::in); - file.read((char*)m_eeprom + 0x0FCA, 0x02f0); + file.read((char*)m_eeprom.data.data() + 0x0FCA, 0x02f0); file.close(); } - // read memory to be sent to Wii - std::copy_n(m_eeprom + m_read_request.address, bytes_to_read, reply->data); - reply->size_minus_one = bytes_to_read - 1; + // Read memory to be sent to Wii + std::copy_n(m_eeprom.data.data() + m_read_request.address, bytes_to_read, reply.data); + reply.size_minus_one = bytes_to_read - 1; } } break; - case WiimoteAddressSpace::I2C_BUS: - case WiimoteAddressSpace::I2C_BUS_ALT: + case AddressSpace::I2C_BUS: + case AddressSpace::I2C_BUS_ALT: { - // Read from I2C bus + // Attempting to access the EEPROM directly over i2c results in error 8. + if (EEPROM_I2C_ADDR == m_read_request.slave_address) + { + WARN_LOG(WIIMOTE, "Attempt to read EEPROM directly."); + error_code = ErrorCode::INVALID_ADDRESS; + break; + } + // Top byte of address is ignored on the bus, but it IS maintained in the read-reply. auto const bytes_read = m_i2c_bus.BusRead( - m_read_request.slave_address, (u8)m_read_request.address, bytes_to_read, reply->data); - - reply->size_minus_one = bytes_read - 1; + m_read_request.slave_address, (u8)m_read_request.address, bytes_to_read, reply.data); if (bytes_read != bytes_to_read) { - // generate read error, 7 == no such slave (no ack) - INFO_LOG(WIIMOTE, "Responding with read error 7 @ 0x%x @ 0x%x (%d)", - m_read_request.slave_address, m_read_request.address, m_read_request.size); - error_code = WiimoteErrorCode::NACK; + DEBUG_LOG(WIIMOTE, "Responding with read error 7 @ 0x%x @ 0x%x (%d)", + m_read_request.slave_address, m_read_request.address, m_read_request.size); + error_code = ErrorCode::NACK; + break; } + + reply.size_minus_one = bytes_read - 1; } break; default: - WARN_LOG(WIIMOTE, "ReadData: invalid address space: 0x%x", m_read_request.space); + WARN_LOG(WIIMOTE, "ReadData: invalid address space: 0x%x", int(m_read_request.space)); // A real wiimote gives error 6: - error_code = WiimoteErrorCode::INVALID_SPACE; + error_code = ErrorCode::INVALID_SPACE; break; } - if (WiimoteErrorCode::SUCCESS != error_code) + if (ErrorCode::SUCCESS != error_code) { // Stop processing request on read error: m_read_request.size = 0; // Real wiimote seems to set size to max value on read errors: - reply->size_minus_one = 0xf; + reply.size_minus_one = 0xf; } else { @@ -450,49 +476,57 @@ bool Wiimote::ProcessReadDataRequest() m_read_request.size -= bytes_to_read; } - reply->error = static_cast(error_code); + reply.error = static_cast(error_code); - // Send the data - Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), - rpt.GetSize()); + CallbackInterruptChannel(rpt.GetData(), rpt.GetSize()); return true; } void Wiimote::DoState(PointerWrap& p) { - p.Do(m_extension->active_extension); - p.Do(m_extension->switch_extension); + // No need to sync. Index will not change. + // p.Do(m_index); + + // No need to sync. This is not wiimote state. + // p.Do(m_sensor_bar_on_top); - p.Do(m_accel); - p.Do(m_index); - p.Do(ir_sin); - p.Do(ir_cos); - p.Do(m_rumble_on); - p.Do(m_speaker_mute); - p.Do(m_reporting_auto); - p.Do(m_reporting_mode); p.Do(m_reporting_channel); - p.Do(m_shake_step); - p.Do(m_sensor_bar_on_top); + p.Do(m_reporting_mode); + p.Do(m_reporting_continuous); + + p.Do(m_speaker_mute); + p.Do(m_status); - p.Do(m_speaker_logic.adpcm_state); - p.Do(m_ext_logic.ext_key); - p.DoArray(m_eeprom); - p.Do(m_motion_plus_logic.reg_data); - p.Do(m_camera_logic.reg_data); - p.Do(m_ext_logic.reg_data); - p.Do(m_speaker_logic.reg_data); + p.Do(m_eeprom); p.Do(m_read_request); + + // Sub-devices: + m_speaker_logic.DoState(p); + m_motion_plus.DoState(p); + m_camera_logic.DoState(p); + + p.Do(m_active_extension); + GetActiveExtension()->DoState(p); + + // TODO: Handle motion plus being disabled. + m_motion_plus.AttachExtension(GetActiveExtension()); + + // Dynamics + // TODO: clean this up: + p.Do(m_shake_step); + p.DoMarker("Wiimote"); if (p.GetMode() == PointerWrap::MODE_READ) RealState(); - - // TODO: rebuild i2c bus state after state-change } -// load real Wiimote state +ExtensionNumber Wiimote::GetActiveExtensionNumber() const +{ + return m_active_extension; +} + void Wiimote::RealState() { using namespace WiimoteReal; @@ -500,7 +534,7 @@ void Wiimote::RealState() if (g_wiimotes[m_index]) { g_wiimotes[m_index]->SetChannel(m_reporting_channel); - g_wiimotes[m_index]->EnableDataReporting(m_reporting_mode); } } + } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Encryption.cpp b/Source/Core/Core/HW/WiimoteEmu/Encryption.cpp index 8e19716108..9d6a68225d 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Encryption.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Encryption.cpp @@ -241,8 +241,10 @@ static void GenerateTables(const u8* const rand, const u8* const key, const u8 i sb[7] = sboxes[idx][rand[2]] ^ sboxes[(idx + 1) % 8][rand[6]]; } -/* Generate key from the 0x40-0x4c data in g_RegExt */ -void WiimoteGenerateKey(wiimote_key* const key, const u8* const keydata) +namespace WiimoteEmu +{ +// Generate key from the 0x40-0x4c data in g_RegExt +void EncryptionKey::Generate(const u8* const keydata) { u8 rand[10]; u8 skey[6]; @@ -262,22 +264,24 @@ void WiimoteGenerateKey(wiimote_key* const key, const u8* const keydata) } // default case is idx = 7 which is valid (homebrew uses it for the 0x17 case) - GenerateTables(rand, skey, idx, key->ft, key->sb); + GenerateTables(rand, skey, idx, ft, sb); // for homebrew, ft and sb are all 0x97 which is equivalent to 0x17 } -// TODO: is there a reason these can only handle a length of 255? -/* Encrypt data */ -void WiimoteEncrypt(const wiimote_key* const key, u8* const data, int addr, const u8 len) +// Question: Is there a reason these can only handle a length of 255? +// Answer: The i2c address space is only 8-bits so it really doesn't need to. +// Also, only 21 bytes are ever encrypted at most (6 in any normal game). +void EncryptionKey::Encrypt(u8* const data, int addr, const u8 len) const { for (int i = 0; i < len; ++i, ++addr) - data[i] = (data[i] - key->ft[addr % 8]) ^ key->sb[addr % 8]; + data[i] = (data[i] - ft[addr % 8]) ^ sb[addr % 8]; } -/* Decrypt data */ -void WiimoteDecrypt(const wiimote_key* const key, u8* const data, int addr, const u8 len) +void EncryptionKey::Decrypt(u8* const data, int addr, const u8 len) const { for (int i = 0; i < len; ++i, ++addr) - data[i] = (data[i] ^ key->sb[addr % 8]) + key->ft[addr % 8]; + data[i] = (data[i] ^ sb[addr % 8]) + ft[addr % 8]; } + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Encryption.h b/Source/Core/Core/HW/WiimoteEmu/Encryption.h index 5fcf6ff4f4..adac34427d 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Encryption.h +++ b/Source/Core/Core/HW/WiimoteEmu/Encryption.h @@ -8,14 +8,19 @@ #include "Common/CommonTypes.h" -// The key structure to use with WiimoteGenerateKey() -struct wiimote_key +namespace WiimoteEmu { - u8 ft[8]; - u8 sb[8]; +class EncryptionKey +{ +public: + void Generate(const u8* keydata); + + void Encrypt(u8* data, int addr, u8 len) const; + void Decrypt(u8* data, int addr, u8 len) const; + +private: + u8 ft[8] = {}; + u8 sb[8] = {}; }; -void WiimoteEncrypt(const wiimote_key* const key, u8* const data, int addr, const u8 len); -void WiimoteDecrypt(const wiimote_key* const key, u8* const data, int addr, const u8 len); - -void WiimoteGenerateKey(wiimote_key* const key, const u8* const keydata); +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp similarity index 89% rename from Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.cpp rename to Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp index 1cabaf60c4..aa6b7e9ccd 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "Core/HW/WiimoteEmu/Attachment/Classic.h" +#include "Core/HW/WiimoteEmu/Extension/Classic.h" #include #include @@ -71,7 +71,7 @@ constexpr std::array classic_dpad_bitmasks{{ Classic::PAD_RIGHT, }}; -Classic::Classic(ExtensionReg& reg) : Attachment(_trans("Classic"), reg) +Classic::Classic() : EncryptedExtension(_trans("Classic")) { // buttons groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); @@ -104,52 +104,21 @@ Classic::Classic(ExtensionReg& reg) : Attachment(_trans("Classic"), reg) m_dpad->controls.emplace_back( new ControllerEmu::Input(ControllerEmu::Translate, named_direction)); } - - m_id = classic_id; - - // Build calibration data: - m_calibration = {{ - // Left Stick X max,min,center: - CAL_STICK_CENTER + CAL_STICK_RANGE, - CAL_STICK_CENTER - CAL_STICK_RANGE, - CAL_STICK_CENTER, - // Left Stick Y max,min,center: - CAL_STICK_CENTER + CAL_STICK_RANGE, - CAL_STICK_CENTER - CAL_STICK_RANGE, - CAL_STICK_CENTER, - // Right Stick X max,min,center: - CAL_STICK_CENTER + CAL_STICK_RANGE, - CAL_STICK_CENTER - CAL_STICK_RANGE, - CAL_STICK_CENTER, - // Right Stick Y max,min,center: - CAL_STICK_CENTER + CAL_STICK_RANGE, - CAL_STICK_CENTER - CAL_STICK_RANGE, - CAL_STICK_CENTER, - // Left/Right trigger range: (assumed based on real calibration data values) - LEFT_TRIGGER_RANGE, - RIGHT_TRIGGER_RANGE, - // 2 checksum bytes calculated below: - 0x00, - 0x00, - }}; - - UpdateCalibrationDataChecksum(m_calibration); } -void Classic::GetState(u8* const data) +void Classic::Update() { - wm_classic_extension classic_data = {}; - - // not using calibration data, o well + auto& classic_data = *reinterpret_cast(&m_reg.controller_data); + classic_data = {}; // left stick { const ControllerEmu::AnalogStick::StateData left_stick_state = m_left_stick->GetState(); - classic_data.regular_data.lx = static_cast( - Classic::LEFT_STICK_CENTER_X + (left_stick_state.x * Classic::LEFT_STICK_RADIUS)); - classic_data.regular_data.ly = static_cast( - Classic::LEFT_STICK_CENTER_Y + (left_stick_state.y * Classic::LEFT_STICK_RADIUS)); + classic_data.lx = static_cast(Classic::LEFT_STICK_CENTER_X + + (left_stick_state.x * Classic::LEFT_STICK_RADIUS)); + classic_data.ly = static_cast(Classic::LEFT_STICK_CENTER_Y + + (left_stick_state.y * Classic::LEFT_STICK_RADIUS)); } // right stick @@ -187,8 +156,6 @@ void Classic::GetState(u8* const data) // flip button bits classic_data.bt.hex ^= 0xFFFF; - - std::memcpy(data, &classic_data, sizeof(wm_classic_extension)); } bool Classic::IsButtonPressed() const @@ -201,6 +168,40 @@ bool Classic::IsButtonPressed() const return buttons != 0; } +void Classic::Reset() +{ + m_reg = {}; + m_reg.identifier = classic_id; + + // Build calibration data: + m_reg.calibration = {{ + // Left Stick X max,min,center: + CAL_STICK_CENTER + CAL_STICK_RANGE, + CAL_STICK_CENTER - CAL_STICK_RANGE, + CAL_STICK_CENTER, + // Left Stick Y max,min,center: + CAL_STICK_CENTER + CAL_STICK_RANGE, + CAL_STICK_CENTER - CAL_STICK_RANGE, + CAL_STICK_CENTER, + // Right Stick X max,min,center: + CAL_STICK_CENTER + CAL_STICK_RANGE, + CAL_STICK_CENTER - CAL_STICK_RANGE, + CAL_STICK_CENTER, + // Right Stick Y max,min,center: + CAL_STICK_CENTER + CAL_STICK_RANGE, + CAL_STICK_CENTER - CAL_STICK_RANGE, + CAL_STICK_CENTER, + // Left/Right trigger range: (assumed based on real calibration data values) + LEFT_TRIGGER_RANGE, + RIGHT_TRIGGER_RANGE, + // 2 checksum bytes calculated below: + 0x00, + 0x00, + }}; + + UpdateCalibrationDataChecksum(m_reg.calibration, CALIBRATION_CHECKSUM_BYTES); +} + ControllerEmu::ControlGroup* Classic::GetGroup(ClassicGroup group) { switch (group) diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.h similarity index 60% rename from Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.h rename to Source/Core/Core/HW/WiimoteEmu/Extension/Classic.h index 357ad9a1cc..ed144d83db 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.h +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.h @@ -4,7 +4,8 @@ #pragma once -#include "Core/HW/WiimoteEmu/Attachment/Attachment.h" +#include "Core/HW/WiimoteCommon/WiimoteReport.h" +#include "Core/HW/WiimoteEmu/Extension/Extension.h" namespace ControllerEmu { @@ -16,15 +17,74 @@ class MixedTriggers; namespace WiimoteEmu { -enum class ClassicGroup; -struct ExtensionReg; +enum class ClassicGroup +{ + Buttons, + Triggers, + DPad, + LeftStick, + RightStick +}; -class Classic : public Attachment +class Classic : public EncryptedExtension { public: - explicit Classic(ExtensionReg& reg); - void GetState(u8* const data) override; + union ButtonFormat + { + u16 hex; + + struct + { + u8 : 1; + u8 rt : 1; // right trigger + u8 plus : 1; + u8 home : 1; + u8 minus : 1; + u8 lt : 1; // left trigger + u8 dpad_down : 1; + u8 dpad_right : 1; + + u8 dpad_up : 1; + u8 dpad_left : 1; + u8 zr : 1; + u8 x : 1; + u8 a : 1; + u8 y : 1; + u8 b : 1; + u8 zl : 1; // left z button + }; + }; + static_assert(sizeof(ButtonFormat) == 2, "Wrong size"); + + struct DataFormat + { + // lx/ly/lz; left joystick + // rx/ry/rz; right joystick + // lt; left trigger + // rt; left trigger + + u8 lx : 6; // byte 0 + u8 rx3 : 2; + + u8 ly : 6; // byte 1 + u8 rx2 : 2; + + u8 ry : 5; + u8 lt2 : 2; + u8 rx1 : 1; + + u8 rt : 5; + u8 lt1 : 3; + + ButtonFormat bt; // byte 4, 5 + }; + static_assert(sizeof(DataFormat) == 6, "Wrong size"); + + Classic(); + + void Update() override; bool IsButtonPressed() const override; + void Reset() override; ControllerEmu::ControlGroup* GetGroup(ClassicGroup group); diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp similarity index 85% rename from Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.cpp rename to Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp index b32b767b01..976038711c 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "Core/HW/WiimoteEmu/Attachment/Drums.h" +#include "Core/HW/WiimoteEmu/Extension/Drums.h" #include #include @@ -43,7 +43,7 @@ constexpr std::array drum_button_bitmasks{{ Drums::BUTTON_PLUS, }}; -Drums::Drums(ExtensionReg& reg) : Attachment(_trans("Drums"), reg) +Drums::Drums() : EncryptedExtension(_trans("Drums")) { // pads groups.emplace_back(m_pads = new ControllerEmu::Buttons(_trans("Pads"))); @@ -62,16 +62,12 @@ Drums::Drums(ExtensionReg& reg) : Attachment(_trans("Drums"), reg) groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); m_buttons->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "-")); m_buttons->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "+")); - - // set up register - m_id = drums_id; } -void Drums::GetState(u8* const data) +void Drums::Update() { - wm_drums_extension drum_data = {}; - - // calibration data not figured out yet? + auto& drum_data = reinterpret_cast(m_reg.controller_data); + drum_data = {}; // stick { @@ -81,9 +77,12 @@ void Drums::GetState(u8* const data) drum_data.sy = static_cast((stick_state.y * STICK_RADIUS) + STICK_CENTER); } - // TODO: softness maybe - data[2] = 0xFF; - data[3] = 0xFF; + // TODO: Implement these: + drum_data.which = 0x1F; + drum_data.none = 1; + drum_data.hhp = 1; + drum_data.velocity = 0xf; + drum_data.softness = 7; // buttons m_buttons->GetState(&drum_data.bt, drum_button_bitmasks.data()); @@ -93,8 +92,6 @@ void Drums::GetState(u8* const data) // flip button bits drum_data.bt ^= 0xFFFF; - - std::memcpy(data, &drum_data, sizeof(wm_drums_extension)); } bool Drums::IsButtonPressed() const @@ -105,6 +102,14 @@ bool Drums::IsButtonPressed() const return buttons != 0; } +void Drums::Reset() +{ + m_reg = {}; + m_reg.identifier = drums_id; + + // TODO: Is there calibration data? +} + ControllerEmu::ControlGroup* Drums::GetGroup(DrumsGroup group) { switch (group) diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.h similarity index 55% rename from Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.h rename to Source/Core/Core/HW/WiimoteEmu/Extension/Drums.h index ea36869cb5..d76a7cc555 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.h +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.h @@ -4,26 +4,54 @@ #pragma once -#include "Core/HW/WiimoteEmu/Attachment/Attachment.h" +#include "Core/HW/WiimoteEmu/Extension/Extension.h" namespace ControllerEmu { class AnalogStick; class Buttons; class ControlGroup; -} +} // namespace ControllerEmu namespace WiimoteEmu { -enum class DrumsGroup; -struct ExtensionReg; +enum class DrumsGroup +{ + Buttons, + Pads, + Stick +}; -class Drums : public Attachment +// TODO: Do the drums ever use encryption? +class Drums : public EncryptedExtension { public: - explicit Drums(ExtensionReg& reg); - void GetState(u8* const data) override; + struct DataFormat + { + u8 sx : 6; + u8 pad1 : 2; // always 0 + + u8 sy : 6; + u8 pad2 : 2; // always 0 + + u8 pad3 : 1; // unknown + u8 which : 5; + u8 none : 1; + u8 hhp : 1; + + u8 pad4 : 1; // unknown + u8 velocity : 4; // unknown + u8 softness : 3; + + u16 bt; // buttons + }; + static_assert(sizeof(DataFormat) == 6, "Wrong size"); + + Drums(); + + void Update() override; bool IsButtonPressed() const override; + void Reset() override; ControllerEmu::ControlGroup* GetGroup(DrumsGroup group); @@ -51,4 +79,4 @@ private: ControllerEmu::Buttons* m_pads; ControllerEmu::AnalogStick* m_stick; }; -} +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp new file mode 100644 index 0000000000..d6208b2935 --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp @@ -0,0 +1,129 @@ +// Copyright 2010 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/HW/WiimoteEmu/Extension/Extension.h" + +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/Compiler.h" +#include "Core/HW/WiimoteEmu/WiimoteEmu.h" + +namespace WiimoteEmu +{ +Extension::Extension(const char* name) : m_name(name) +{ +} + +std::string Extension::GetName() const +{ + return m_name; +} + +None::None() : Extension("None") +{ +} + +bool None::ReadDeviceDetectPin() const +{ + return false; +} + +void None::Update() +{ + // Nothing needed. +} + +bool None::IsButtonPressed() const +{ + return false; +} + +void None::Reset() +{ + // Nothing needed. +} + +void None::DoState(PointerWrap& p) +{ + // Nothing needed. +} + +int None::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) +{ + return 0; +} + +int None::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) +{ + return 0; +} + +EncryptedExtension::EncryptedExtension(const char* name) : Extension(name) +{ +} + +bool EncryptedExtension::ReadDeviceDetectPin() const +{ + return true; +} + +int EncryptedExtension::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) +{ + if (I2C_ADDR != slave_addr) + return 0; + + if (0x00 == addr) + { + // This is where real hardware would update controller data + // We do it in Update() for TAS determinism + // TAS code fails to sync data reads and such.. + } + + auto const result = RawRead(&m_reg, addr, count, data_out); + + // Encrypt data read from extension register + if (ENCRYPTION_ENABLED == m_reg.encryption) + { + // INFO_LOG(WIIMOTE, "Encrypted read."); + ext_key.Encrypt(data_out, addr, (u8)count); + } + else + { + // INFO_LOG(WIIMOTE, "Unencrypted read."); + } + + return result; +} + +int EncryptedExtension::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) +{ + if (I2C_ADDR != slave_addr) + return 0; + + auto const result = RawWrite(&m_reg, addr, count, data_in); + + // TODO: make this check less ugly: + if (addr + count > 0x40 && addr < 0x50) + { + // Run the key generation on all writes in the key area, it doesn't matter + // that we send it parts of a key, only the last full key will have an effect + ext_key.Generate(m_reg.encryption_key_data); + } + + return result; +} + +void EncryptedExtension::DoState(PointerWrap& p) +{ + p.Do(m_reg); + + // No need to sync this when we can regenerate it: + if (p.GetMode() == PointerWrap::MODE_READ) + ext_key.Generate(m_reg.encryption_key_data); +} + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h new file mode 100644 index 0000000000..b32b1367bd --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h @@ -0,0 +1,112 @@ +// Copyright 2010 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Core/HW/WiimoteEmu/Extension/Extension.h" + +#include +#include + +#include "Common/ChunkFile.h" +#include "Common/CommonTypes.h" +#include "Core/HW/WiimoteEmu/Encryption.h" +#include "Core/HW/WiimoteEmu/I2CBus.h" +#include "InputCommon/ControllerEmu/ControllerEmu.h" + +namespace WiimoteEmu +{ +class Extension : public ControllerEmu::EmulatedController, public I2CSlave +{ +public: + Extension(const char* name); + + std::string GetName() const override; + + // Used by the wiimote to detect extension changes. + // The normal extensions short this pin so it's always connected, + // but M+ does some tricks with it during activation. + virtual bool ReadDeviceDetectPin() const = 0; + + virtual bool IsButtonPressed() const = 0; + virtual void Reset() = 0; + virtual void DoState(PointerWrap& p) = 0; + virtual void Update() = 0; + +private: + const char* const m_name; +}; + +class None : public Extension +{ +public: + explicit None(); + +private: + bool ReadDeviceDetectPin() const override; + void Update() override; + bool IsButtonPressed() const override; + void Reset() override; + void DoState(PointerWrap& p) override; + + 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; +}; + +// This class provides the encryption and initialization behavior of most extensions. +class EncryptedExtension : public Extension +{ +public: + static constexpr u8 I2C_ADDR = 0x52; + + EncryptedExtension(const char* name); + + // TODO: This is public for TAS reasons. + // TODO: TAS handles encryption poorly. + WiimoteEmu::EncryptionKey ext_key = {}; + +protected: + static constexpr int CALIBRATION_CHECKSUM_BYTES = 2; + +#pragma pack(push, 1) + struct Register + { + // 21 bytes of possible extension data + u8 controller_data[21]; + + u8 unknown2[11]; + + // address 0x20 + std::array calibration; + u8 unknown3[0x10]; + + // address 0x40 + u8 encryption_key_data[0x10]; + u8 unknown4[0xA0]; + + // address 0xF0 + u8 encryption; + u8 unknown5[0x9]; + + // address 0xFA + std::array identifier; + }; +#pragma pack(pop) + + static_assert(0x100 == sizeof(Register)); + + Register m_reg = {}; + +private: + static constexpr u8 ENCRYPTION_ENABLED = 0xaa; + + bool ReadDeviceDetectPin() const override; + + 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; + + void DoState(PointerWrap& p) override; +}; + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp similarity index 93% rename from Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.cpp rename to Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp index 8b3d447376..b3d5ab286b 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "Core/HW/WiimoteEmu/Attachment/Guitar.h" +#include "Core/HW/WiimoteEmu/Extension/Guitar.h" #include #include @@ -61,7 +61,7 @@ constexpr std::array guitar_strum_bitmasks{{ Guitar::BAR_DOWN, }}; -Guitar::Guitar(ExtensionReg& reg) : Attachment(_trans("Guitar"), reg) +Guitar::Guitar() : EncryptedExtension(_trans("Guitar")) { // frets groups.emplace_back(m_frets = new ControllerEmu::Buttons(_trans("Frets"))); @@ -94,16 +94,12 @@ Guitar::Guitar(ExtensionReg& reg) : Attachment(_trans("Guitar"), reg) // slider bar groups.emplace_back(m_slider_bar = new ControllerEmu::Slider(_trans("Slider Bar"))); - - // set up register - m_id = guitar_id; } -void Guitar::GetState(u8* const data) +void Guitar::Update() { - wm_guitar_extension guitar_data = {}; - - // calibration data not figured out yet? + auto& guitar_data = reinterpret_cast(m_reg.controller_data); + guitar_data = {}; // stick { @@ -141,8 +137,6 @@ void Guitar::GetState(u8* const data) // flip button bits guitar_data.bt ^= 0xFFFF; - - std::memcpy(data, &guitar_data, sizeof(wm_guitar_extension)); } bool Guitar::IsButtonPressed() const @@ -154,6 +148,14 @@ bool Guitar::IsButtonPressed() const return buttons != 0; } +void Guitar::Reset() +{ + m_reg = {}; + m_reg.identifier = guitar_id; + + // TODO: Is there calibration data? +} + ControllerEmu::ControlGroup* Guitar::GetGroup(GuitarGroup group) { switch (group) diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.h similarity index 59% rename from Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.h rename to Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.h index 8964221511..0c34c80955 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.h +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.h @@ -4,7 +4,7 @@ #pragma once -#include "Core/HW/WiimoteEmu/Attachment/Attachment.h" +#include "Core/HW/WiimoteEmu/Extension/Extension.h" namespace ControllerEmu { @@ -13,19 +13,47 @@ class Buttons; class ControlGroup; class Triggers; class Slider; -} +} // namespace ControllerEmu namespace WiimoteEmu { -enum class GuitarGroup; -struct ExtensionReg; +enum class GuitarGroup +{ + Buttons, + Frets, + Strum, + Whammy, + Stick, + SliderBar +}; -class Guitar : public Attachment +// TODO: Does the guitar ever use encryption? +class Guitar : public EncryptedExtension { public: - explicit Guitar(ExtensionReg& reg); - void GetState(u8* const data) override; + struct DataFormat + { + u8 sx : 6; + u8 pad1 : 2; // 1 on gh3, 0 on ghwt + + u8 sy : 6; + u8 pad2 : 2; // 1 on gh3, 0 on ghwt + + u8 sb : 5; // not used in gh3 + u8 pad3 : 3; // always 0 + + u8 whammy : 5; + u8 pad4 : 3; // always 0 + + u16 bt; // buttons + }; + static_assert(sizeof(DataFormat) == 6, "Wrong size"); + + Guitar(); + + void Update() override; bool IsButtonPressed() const override; + void Reset() override; ControllerEmu::ControlGroup* GetGroup(GuitarGroup group); @@ -57,4 +85,4 @@ private: ControllerEmu::AnalogStick* m_stick; ControllerEmu::Slider* m_slider_bar; }; -} +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp similarity index 86% rename from Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.cpp rename to Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp index accc64bc74..91dd8bf176 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "Core/HW/WiimoteEmu/Attachment/Nunchuk.h" +#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h" #include #include @@ -30,7 +30,7 @@ constexpr std::array nunchuk_button_bitmasks{{ Nunchuk::BUTTON_Z, }}; -Nunchuk::Nunchuk(ExtensionReg& reg) : Attachment(_trans("Nunchuk"), reg) +Nunchuk::Nunchuk() : EncryptedExtension(_trans("Nunchuk")) { // buttons groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); @@ -68,11 +68,83 @@ Nunchuk::Nunchuk(ExtensionReg& reg) : Attachment(_trans("Nunchuk"), reg) m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X")); m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y")); m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z")); +} - m_id = nunchuk_id; +void Nunchuk::Update() +{ + auto& nc_data = *reinterpret_cast(&m_reg.controller_data); + nc_data = {}; + + // stick + const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState(); + nc_data.jx = u8(STICK_CENTER + stick_state.x * STICK_RADIUS); + nc_data.jy = u8(STICK_CENTER + stick_state.y * STICK_RADIUS); + + // Some terribly coded games check whether to move with a check like + // + // if (x != 0 && y != 0) + // do_movement(x, y); + // + // With keyboard controls, these games break if you simply hit + // of the axes. Adjust this if you're hitting one of the axes so that + // we slightly tweak the other axis. + if (nc_data.jx != STICK_CENTER || nc_data.jy != STICK_CENTER) + { + if (nc_data.jx == STICK_CENTER) + ++nc_data.jx; + if (nc_data.jy == STICK_CENTER) + ++nc_data.jy; + } + + NormalizedAccelData accel; + + // tilt + EmulateTilt(&accel, m_tilt); + + // swing + EmulateSwing(&accel, m_swing, Config::Get(Config::NUNCHUK_INPUT_SWING_INTENSITY_MEDIUM)); + EmulateSwing(&accel, m_swing_slow, Config::Get(Config::NUNCHUK_INPUT_SWING_INTENSITY_SLOW)); + EmulateSwing(&accel, m_swing_fast, Config::Get(Config::NUNCHUK_INPUT_SWING_INTENSITY_FAST)); + + // shake + EmulateShake(&accel, m_shake, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_MEDIUM), + m_shake_step.data()); + EmulateShake(&accel, m_shake_soft, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_SOFT), + m_shake_soft_step.data()); + EmulateShake(&accel, m_shake_hard, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_HARD), + m_shake_hard_step.data()); + + // buttons + m_buttons->GetState(&nc_data.bt.hex, nunchuk_button_bitmasks.data()); + + // flip the button bits :/ + nc_data.bt.hex ^= 0x03; + + // Calibration values are 8-bit but we want 10-bit precision, so << 2. + auto acc = DenormalizeAccelData(accel, ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2); + + nc_data.ax = (acc.x >> 2) & 0xFF; + nc_data.ay = (acc.y >> 2) & 0xFF; + nc_data.az = (acc.z >> 2) & 0xFF; + nc_data.bt.acc_x_lsb = acc.x & 0x3; + nc_data.bt.acc_y_lsb = acc.y & 0x3; + nc_data.bt.acc_z_lsb = acc.z & 0x3; +} + +bool Nunchuk::IsButtonPressed() const +{ + u8 buttons = 0; + m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data()); + return buttons != 0; +} + +void Nunchuk::Reset() +{ + m_reg = {}; + m_reg.identifier = nunchuk_id; // Build calibration data: - m_calibration = {{ + m_reg.calibration = {{ // Accel Zero X,Y,Z: ACCEL_ZERO_G, ACCEL_ZERO_G, @@ -98,82 +170,7 @@ Nunchuk::Nunchuk(ExtensionReg& reg) : Attachment(_trans("Nunchuk"), reg) 0x00, }}; - UpdateCalibrationDataChecksum(m_calibration); -} - -void Nunchuk::GetState(u8* const data) -{ - wm_nc nc_data = {}; - - // stick - const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState(); - nc_data.jx = u8(STICK_CENTER + stick_state.x * STICK_RADIUS); - nc_data.jy = u8(STICK_CENTER + stick_state.y * STICK_RADIUS); - - // Some terribly coded games check whether to move with a check like - // - // if (x != 0 && y != 0) - // do_movement(x, y); - // - // With keyboard controls, these games break if you simply hit - // of the axes. Adjust this if you're hitting one of the axes so that - // we slightly tweak the other axis. - if (nc_data.jx != STICK_CENTER || nc_data.jy != STICK_CENTER) - { - if (nc_data.jx == STICK_CENTER) - ++nc_data.jx; - if (nc_data.jy == STICK_CENTER) - ++nc_data.jy; - } - - AccelData accel; - - // tilt - EmulateTilt(&accel, m_tilt); - - // swing - EmulateSwing(&accel, m_swing, Config::Get(Config::NUNCHUK_INPUT_SWING_INTENSITY_MEDIUM)); - EmulateSwing(&accel, m_swing_slow, Config::Get(Config::NUNCHUK_INPUT_SWING_INTENSITY_SLOW)); - EmulateSwing(&accel, m_swing_fast, Config::Get(Config::NUNCHUK_INPUT_SWING_INTENSITY_FAST)); - - // shake - EmulateShake(&accel, m_shake, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_MEDIUM), - m_shake_step.data()); - EmulateShake(&accel, m_shake_soft, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_SOFT), - m_shake_soft_step.data()); - EmulateShake(&accel, m_shake_hard, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_HARD), - m_shake_hard_step.data()); - - // buttons - m_buttons->GetState(&nc_data.bt.hex, nunchuk_button_bitmasks.data()); - - // flip the button bits :/ - nc_data.bt.hex ^= 0x03; - - // We now use 2 bits more precision, so multiply by 4 before converting to int - s16 accel_x = (s16)(4 * (accel.x * ACCEL_RANGE + ACCEL_ZERO_G)); - s16 accel_y = (s16)(4 * (accel.y * ACCEL_RANGE + ACCEL_ZERO_G)); - s16 accel_z = (s16)(4 * (accel.z * ACCEL_RANGE + ACCEL_ZERO_G)); - - accel_x = MathUtil::Clamp(accel_x, 0, 0x3ff); - accel_y = MathUtil::Clamp(accel_y, 0, 0x3ff); - accel_z = MathUtil::Clamp(accel_z, 0, 0x3ff); - - nc_data.ax = (accel_x >> 2) & 0xFF; - nc_data.ay = (accel_y >> 2) & 0xFF; - nc_data.az = (accel_z >> 2) & 0xFF; - nc_data.bt.acc_x_lsb = accel_x & 0x3; - nc_data.bt.acc_y_lsb = accel_y & 0x3; - nc_data.bt.acc_z_lsb = accel_z & 0x3; - - std::memcpy(data, &nc_data, sizeof(wm_nc)); -} - -bool Nunchuk::IsButtonPressed() const -{ - u8 buttons = 0; - m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data()); - return buttons != 0; + UpdateCalibrationDataChecksum(m_reg.calibration, CALIBRATION_CHECKSUM_BYTES); } ControllerEmu::ControlGroup* Nunchuk::GetGroup(NunchukGroup group) diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.h similarity index 59% rename from Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.h rename to Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.h index 9b36cac1b2..22c66f5263 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.h +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.h @@ -5,7 +5,9 @@ #pragma once #include -#include "Core/HW/WiimoteEmu/Attachment/Attachment.h" + +#include "Core/HW/WiimoteCommon/WiimoteReport.h" +#include "Core/HW/WiimoteEmu/Extension/Extension.h" namespace ControllerEmu { @@ -18,16 +20,59 @@ class Tilt; namespace WiimoteEmu { -enum class NunchukGroup; -struct ExtensionReg; +enum class NunchukGroup +{ + Buttons, + Stick, + Tilt, + Swing, + Shake +}; -class Nunchuk : public Attachment +class Nunchuk : public EncryptedExtension { public: - explicit Nunchuk(ExtensionReg& reg); + union ButtonFormat + { + u8 hex; - void GetState(u8* const data) override; + struct + { + u8 z : 1; + u8 c : 1; + + // LSBs of accelerometer + u8 acc_x_lsb : 2; + u8 acc_y_lsb : 2; + u8 acc_z_lsb : 2; + }; + }; + static_assert(sizeof(ButtonFormat) == 1, "Wrong size"); + + union DataFormat + { + struct + { + // joystick x, y + u8 jx; + u8 jy; + + // accelerometer + u8 ax; + u8 ay; + u8 az; + + // buttons + accelerometer LSBs + ButtonFormat bt; + }; + }; + static_assert(sizeof(DataFormat) == 6, "Wrong size"); + + Nunchuk(); + + void Update() override; bool IsButtonPressed() const override; + void Reset() override; ControllerEmu::ControlGroup* GetGroup(NunchukGroup group); @@ -41,7 +86,6 @@ public: { ACCEL_ZERO_G = 0x80, ACCEL_ONE_G = 0xB3, - ACCEL_RANGE = (ACCEL_ONE_G - ACCEL_ZERO_G), }; enum @@ -55,6 +99,7 @@ public: private: ControllerEmu::Tilt* m_tilt; + ControllerEmu::Force* m_swing; ControllerEmu::Force* m_swing_slow; ControllerEmu::Force* m_swing_fast; diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp similarity index 93% rename from Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.cpp rename to Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp index 76eef2ab2a..2871a73339 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "Core/HW/WiimoteEmu/Attachment/Turntable.h" +#include "Core/HW/WiimoteEmu/Extension/Turntable.h" #include #include @@ -44,7 +44,7 @@ constexpr std::array turntable_button_names{{ _trans("Blue Right"), }}; -Turntable::Turntable(ExtensionReg& reg) : Attachment(_trans("Turntable"), reg) +Turntable::Turntable() : EncryptedExtension(_trans("Turntable")) { // buttons groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); @@ -80,14 +80,12 @@ Turntable::Turntable(ExtensionReg& reg) : Attachment(_trans("Turntable"), reg) // crossfade groups.emplace_back(m_crossfade = new ControllerEmu::Slider(_trans("Crossfade"))); - - // set up register - m_id = turntable_id; } -void Turntable::GetState(u8* const data) +void Turntable::Update() { - wm_turntable_extension tt_data = {}; + auto& tt_data = reinterpret_cast(m_reg.controller_data); + tt_data = {}; // stick { @@ -139,8 +137,6 @@ void Turntable::GetState(u8* const data) // flip button bits :/ tt_data.bt ^= (BUTTON_L_GREEN | BUTTON_L_RED | BUTTON_L_BLUE | BUTTON_R_GREEN | BUTTON_R_RED | BUTTON_R_BLUE | BUTTON_MINUS | BUTTON_PLUS | BUTTON_EUPHORIA); - - std::memcpy(data, &tt_data, sizeof(wm_turntable_extension)); } bool Turntable::IsButtonPressed() const @@ -150,6 +146,14 @@ bool Turntable::IsButtonPressed() const return buttons != 0; } +void Turntable::Reset() +{ + m_reg = {}; + m_reg.identifier = turntable_id; + + // TODO: Is there calibration data? +} + ControllerEmu::ControlGroup* Turntable::GetGroup(TurntableGroup group) { switch (group) diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.h similarity index 59% rename from Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.h rename to Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.h index 8d2a0b0988..158f3cdd73 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.h +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.h @@ -4,7 +4,7 @@ #pragma once -#include "Core/HW/WiimoteEmu/Attachment/Attachment.h" +#include "Core/HW/WiimoteEmu/Extension/Extension.h" namespace ControllerEmu { @@ -13,19 +13,53 @@ class Buttons; class ControlGroup; class Slider; class Triggers; -} +} // namespace ControllerEmu namespace WiimoteEmu { -enum class TurntableGroup; -struct ExtensionReg; +enum class TurntableGroup +{ + Buttons, + Stick, + EffectDial, + LeftTable, + RightTable, + Crossfade +}; -class Turntable : public Attachment +// TODO: Does the turntable ever use encryption? +class Turntable : public EncryptedExtension { public: - explicit Turntable(ExtensionReg& reg); - void GetState(u8* const data) override; + struct DataFormat + { + u8 sx : 6; + u8 rtable3 : 2; + + u8 sy : 6; + u8 rtable2 : 2; + + u8 rtable4 : 1; + u8 slider : 4; + u8 dial2 : 2; + u8 rtable1 : 1; + + u8 ltable1 : 5; + u8 dial1 : 3; + + union + { + u16 ltable2 : 1; + u16 bt; // buttons + }; + }; + static_assert(sizeof(DataFormat) == 6, "Wrong size"); + + Turntable(); + + void Update() override; bool IsButtonPressed() const override; + void Reset() override; ControllerEmu::ControlGroup* GetGroup(TurntableGroup group); @@ -59,4 +93,4 @@ private: ControllerEmu::Slider* m_right_table; ControllerEmu::Slider* m_crossfade; }; -} +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp new file mode 100644 index 0000000000..8a9c6de766 --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp @@ -0,0 +1,29 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/HW/WiimoteEmu/ExtensionPort.h" + +#include "Common/ChunkFile.h" + +namespace WiimoteEmu +{ +ExtensionPort::ExtensionPort(I2CBus* i2c_bus) : m_i2c_bus(*i2c_bus) +{ +} + +bool ExtensionPort::IsDeviceConnected() const +{ + return m_extension->ReadDeviceDetectPin(); +} + +void ExtensionPort::AttachExtension(Extension* ext) +{ + m_i2c_bus.RemoveSlave(m_extension); + + m_extension = ext; + m_i2c_bus.AddSlave(m_extension); + ; +} + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h new file mode 100644 index 0000000000..13dcc0025d --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h @@ -0,0 +1,43 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/ChunkFile.h" +#include "Core/HW/WiimoteEmu/Extension/Extension.h" +#include "Core/HW/WiimoteEmu/I2CBus.h" + +namespace WiimoteEmu +{ +enum ExtensionNumber : u8 +{ + NONE, + + NUNCHUK, + CLASSIC, + GUITAR, + DRUMS, + TURNTABLE, +}; + +// FYI: An extension must be attached. +// Attach "None" for no extension. +class ExtensionPort +{ +public: + // The real wiimote reads extension data from i2c slave 0x52 addres 0x00: + static constexpr u8 REPORT_I2C_SLAVE = 0x52; + static constexpr u8 REPORT_I2C_ADDR = 0x00; + + ExtensionPort(I2CBus* i2c_bus); + + bool IsDeviceConnected() const; + void AttachExtension(Extension* dev); + +private: + Extension* m_extension = nullptr; + I2CBus& m_i2c_bus; +}; + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp b/Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp new file mode 100644 index 0000000000..ee5c3b4325 --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp @@ -0,0 +1,56 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/HW/WiimoteEmu/I2CBus.h" + +#include + +namespace WiimoteEmu +{ +void I2CBus::AddSlave(I2CSlave* slave) +{ + m_slaves.emplace_back(slave); +} + +void I2CBus::RemoveSlave(I2CSlave* slave) +{ + m_slaves.erase(std::remove(m_slaves.begin(), m_slaves.end(), slave), m_slaves.end()); +} + +void I2CBus::Reset() +{ + m_slaves.clear(); +} + +int I2CBus::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) +{ + // INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); + for (auto& slave : m_slaves) + { + auto const bytes_read = slave->BusRead(slave_addr, addr, count, data_out); + + // A slave responded, we are done. + if (bytes_read) + return bytes_read; + } + + return 0; +} + +int I2CBus::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) +{ + // INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); + for (auto& slave : m_slaves) + { + auto const bytes_written = slave->BusWrite(slave_addr, addr, count, data_in); + + // A slave responded, we are done. + if (bytes_written) + return bytes_written; + } + + return 0; +} + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/I2CBus.h b/Source/Core/Core/HW/WiimoteEmu/I2CBus.h new file mode 100644 index 0000000000..f20719af32 --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/I2CBus.h @@ -0,0 +1,76 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" + +namespace WiimoteEmu +{ +class I2CBus; + +class I2CSlave +{ + friend I2CBus; + +protected: + virtual ~I2CSlave() = default; + + virtual int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) = 0; + virtual int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) = 0; + + template + static int RawRead(T* reg_data, u8 addr, int count, u8* data_out) + { + static_assert(std::is_pod::value); + static_assert(0x100 == sizeof(T)); + + // TODO: addr wraps around after 0xff + + u8* src = reinterpret_cast(reg_data) + addr; + count = std::min(count, int(reinterpret_cast(reg_data + 1) - src)); + + std::copy_n(src, count, data_out); + + return count; + } + + template + static int RawWrite(T* reg_data, u8 addr, int count, const u8* data_in) + { + static_assert(std::is_pod::value); + static_assert(0x100 == sizeof(T)); + + // TODO: addr wraps around after 0xff + + u8* dst = reinterpret_cast(reg_data) + addr; + count = std::min(count, int(reinterpret_cast(reg_data + 1) - dst)); + + std::copy_n(data_in, count, dst); + + return count; + } +}; + +class I2CBus +{ +public: + void AddSlave(I2CSlave* slave); + void RemoveSlave(I2CSlave* slave); + + void Reset(); + + // TODO: change int to u16 or something + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out); + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in); + +private: + // Pointers are unowned: + std::vector m_slaves; +}; + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp new file mode 100644 index 0000000000..6734c32e53 --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp @@ -0,0 +1,376 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/HW/WiimoteEmu/MotionPlus.h" + +#include "Common/BitUtils.h" +#include "Common/ChunkFile.h" +#include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" + +namespace WiimoteEmu +{ +MotionPlus::MotionPlus() : Extension("MotionPlus") +{ +} + +void MotionPlus::Reset() +{ + reg_data = {}; + + constexpr std::array initial_id = {0x00, 0x00, 0xA6, 0x20, 0x00, 0x05}; + + // FYI: This ID changes on activation + std::copy(std::begin(initial_id), std::end(initial_id), reg_data.ext_identifier); + + // TODO: determine meaning of calibration data: + static const u8 cdata[32] = { + 0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, 0xcf, 0xf0, 0x31, + 0xad, 0xc8, 0x0b, 0x5e, 0x39, 0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, + 0x33, 0x60, 0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d, + }; + + std::copy(std::begin(cdata), std::end(cdata), reg_data.calibration_data); + + // TODO: determine the meaning behind this: + static const u8 cert[64] = { + 0x99, 0x1a, 0x07, 0x1b, 0x97, 0xf1, 0x11, 0x78, 0x0c, 0x42, 0x2b, 0x68, 0xdf, + 0x44, 0x38, 0x0d, 0x2b, 0x7e, 0xd6, 0x84, 0x84, 0x58, 0x65, 0xc9, 0xf2, 0x95, + 0xd9, 0xaf, 0xb6, 0xc4, 0x87, 0xd5, 0x18, 0xdb, 0x67, 0x3a, 0xc0, 0x71, 0xec, + 0x3e, 0xf4, 0xe6, 0x7e, 0x35, 0xa3, 0x29, 0xf8, 0x1f, 0xc5, 0x7c, 0x3d, 0xb9, + 0x56, 0x22, 0x95, 0x98, 0x8f, 0xfb, 0x66, 0x3e, 0x9a, 0xdd, 0xeb, 0x7e, + }; + + std::copy(std::begin(cert), std::end(cert), reg_data.cert_data); +} + +void MotionPlus::DoState(PointerWrap& p) +{ + p.Do(reg_data); +} + +void MotionPlus::AttachExtension(Extension* ext) +{ + extension_port.AttachExtension(ext); +} + +bool MotionPlus::IsActive() const +{ + return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; +} + +MotionPlus::PassthroughMode MotionPlus::GetPassthroughMode() const +{ + return static_cast(reg_data.ext_identifier[4]); +} + +int MotionPlus::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) +{ + if (IsActive()) + { + // Motion plus does not respond to 0x53 when activated + if (ACTIVE_DEVICE_ADDR == slave_addr) + return RawRead(®_data, addr, count, data_out); + else + return 0; + } + else + { + if (INACTIVE_DEVICE_ADDR == slave_addr) + return RawRead(®_data, addr, count, data_out); + else + { + // Passthrough to the connected extension (if any) + return i2c_bus.BusRead(slave_addr, addr, count, data_out); + } + } +} + +int MotionPlus::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) +{ + if (IsActive()) + { + // Motion plus does not respond to 0x53 when activated + if (ACTIVE_DEVICE_ADDR == slave_addr) + { + auto const result = RawWrite(®_data, addr, count, data_in); + + // It seems a write of any value triggers deactivation. + // TODO: kill magic number + if (0xf0 == addr) + { + // Deactivate motion plus: + reg_data.ext_identifier[2] = INACTIVE_DEVICE_ADDR << 1; + reg_data.cert_ready = 0x0; + + // Pass through the activation write to the attached extension: + // The M+ deactivation signal is cleverly the same as EXT activation: + i2c_bus.BusWrite(slave_addr, addr, count, data_in); + } + // TODO: kill magic number + else if (0xf1 == addr) + { + INFO_LOG(WIIMOTE, "M+ cert activation: 0x%x", reg_data.cert_enable); + // 0x14,0x18 is also a valid value + // 0x1a is final value + reg_data.cert_ready = 0x18; + } + // TODO: kill magic number + else if (0xf2 == addr) + { + INFO_LOG(WIIMOTE, "M+ calibration ?? : 0x%x", reg_data.unknown_0xf2[0]); + } + + return result; + } + else + { + // No i2c passthrough when activated. + return 0; + } + } + else + { + if (INACTIVE_DEVICE_ADDR == slave_addr) + { + auto const result = RawWrite(®_data, addr, count, data_in); + + // It seems a write of any value triggers activation. + if (0xfe == addr) + { + INFO_LOG(WIIMOTE, "M+ has been activated: %d", data_in[0]); + + // Activate motion plus: + reg_data.ext_identifier[2] = ACTIVE_DEVICE_ADDR << 1; + // TODO: kill magic number + // reg_data.cert_ready = 0x2; + + // A real M+ is unresponsive on the bus for some time during activation + // Reads fail to ack and ext data gets filled with 0xff for a frame or two + // I don't think we need to emulate that. + + // TODO: activate extension and disable encrption + // also do this if an extension is attached after activation. + std::array data = {0x55}; + i2c_bus.BusWrite(ACTIVE_DEVICE_ADDR, 0xf0, (int)data.size(), data.data()); + } + + return result; + } + else + { + // Passthrough to the connected extension (if any) + return i2c_bus.BusWrite(slave_addr, addr, count, data_in); + } + } +} + +bool MotionPlus::ReadDeviceDetectPin() const +{ + if (IsActive()) + { + return true; + } + else + { + // When inactive the device detect pin reads from the ext port: + return extension_port.IsDeviceConnected(); + } +} + +bool MotionPlus::IsButtonPressed() const +{ + return false; +} + +void MotionPlus::Update() +{ + if (!IsActive()) + { + return; + } + + auto& data = reg_data.controller_data; + + if (0x0 == reg_data.cert_ready) + { + // Without sending this nonsense, inputs are unresponsive.. even regular buttons + // Device still operates when changing the data slightly so its not any sort of encrpytion + // It even works when removing the is_mp_data bit in the last byte + // My M+ non-inside gives: 61,46,45,aa,0,2 or b6,46,45,9a,0,2 + // static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc | 0x01, 0x02}; + static const u8 init_data[6] = {0x81, 0x46, 0x46, 0xb6, 0x01, 0x02}; + std::copy(std::begin(init_data), std::end(init_data), data); + reg_data.cert_ready = 0x2; + return; + } + + if (0x2 == reg_data.cert_ready) + { + static const u8 init_data[6] = {0x7f, 0xcf, 0xdf, 0x8b, 0x4f, 0x82}; + std::copy(std::begin(init_data), std::end(init_data), data); + reg_data.cert_ready = 0x8; + return; + } + + if (0x8 == reg_data.cert_ready) + { + // A real wiimote takes about 2 seconds to reach this state: + reg_data.cert_ready = 0xe; + } + + if (0x18 == reg_data.cert_ready) + { + // TODO: determine the meaning of this + const u8 mp_cert2[64] = { + 0xa5, 0x84, 0x1f, 0xd6, 0xbd, 0xdc, 0x7a, 0x4c, 0xf3, 0xc0, 0x24, 0xe0, 0x92, + 0xef, 0x19, 0x28, 0x65, 0xe0, 0x62, 0x7c, 0x9b, 0x41, 0x6f, 0x12, 0xc3, 0xac, + 0x78, 0xe4, 0xfc, 0x6b, 0x7b, 0x0a, 0xb4, 0x50, 0xd6, 0xf2, 0x45, 0xf7, 0x93, + 0x04, 0xaf, 0xf2, 0xb7, 0x26, 0x94, 0xee, 0xad, 0x92, 0x05, 0x6d, 0xe5, 0xc6, + 0xd6, 0x36, 0xdc, 0xa5, 0x69, 0x0f, 0xc8, 0x99, 0xf2, 0x1c, 0x4e, 0x0d, + }; + + std::copy(std::begin(mp_cert2), std::end(mp_cert2), reg_data.cert_data); + + if (0x01 != reg_data.cert_enable) + { + PanicAlert("M+ Failure! Game requested cert2 with value other than 0x01. M+ will disconnect " + "shortly unfortunately. Reconnect wiimote and hope for the best."); + } + + // A real wiimote takes about 2 seconds to reach this state: + reg_data.cert_ready = 0x1a; + INFO_LOG(WIIMOTE, "M+ cert 2 ready!"); + } + + // TODO: make sure a motion plus report is sent first after init + + // On real mplus: + // For some reason the first read seems to have garbage data + // is_mp_data and extension_connected are set, but the data is junk + // it does seem to have some sort of pattern though, byte 5 is always 2 + // something like: d5, b0, 4e, 6e, fc, 2 + // When a passthrough mode is set: + // the second read is valid mplus data, which then triggers a read from the extension + // the third read is finally extension data + // If an extension is not attached the data is always mplus data + // even when passthrough is enabled + + // Real M+ seems to only ever read 6 bytes from the extension. + // Data after 6 bytes seems to be zero-filled. + // After reading, the real M+ uses that data for the next frame. + // But we are going to use it for the current frame instead. + constexpr int EXT_AMT = 6; + // Always read from 0x52 @ 0x00: + constexpr u8 EXT_SLAVE = ExtensionPort::REPORT_I2C_SLAVE; + constexpr u8 EXT_ADDR = ExtensionPort::REPORT_I2C_ADDR; + + // Try to alternate between M+ and EXT data: + auto& mplus_data = *reinterpret_cast(data); + mplus_data.is_mp_data ^= true; + + // hax!!! + // static const u8 hacky_mp_data[6] = {0x1d, 0x91, 0x49, 0x87, 0x73, 0x7a}; + // static const u8 hacky_nc_data[6] = {0x79, 0x7f, 0x4b, 0x83, 0x8b, 0xec}; + // auto& hacky_ptr = mplus_data.is_mp_data ? hacky_mp_data : hacky_nc_data; + // std::copy(std::begin(hacky_ptr), std::end(hacky_ptr), data); + // return; + + // If the last frame had M+ data try to send some non-M+ data: + if (!mplus_data.is_mp_data) + { + switch (GetPassthroughMode()) + { + case PassthroughMode::DISABLED: + { + // Passthrough disabled, always send M+ data: + mplus_data.is_mp_data = true; + break; + } + case PassthroughMode::NUNCHUK: + { + if (EXT_AMT == i2c_bus.BusRead(EXT_SLAVE, EXT_ADDR, EXT_AMT, data)) + { + // Passthrough data modifications via wiibrew.org + // Data passing through drops the least significant bit of the three accelerometer values + // Bit 7 of byte 5 is moved to bit 6 of byte 5, overwriting it + Common::SetBit(data[5], 6, Common::ExtractBit(data[5], 7)); + // Bit 0 of byte 4 is moved to bit 7 of byte 5 + Common::SetBit(data[5], 7, Common::ExtractBit(data[4], 0)); + // Bit 3 of byte 5 is moved to bit 4 of byte 5, overwriting it + Common::SetBit(data[5], 4, Common::ExtractBit(data[5], 3)); + // Bit 1 of byte 5 is moved to bit 3 of byte 5 + Common::SetBit(data[5], 3, Common::ExtractBit(data[5], 1)); + // Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it + Common::SetBit(data[5], 2, Common::ExtractBit(data[5], 0)); + + // Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below. + mplus_data.is_mp_data = false; + } + else + { + // Read failed (extension unplugged), Send M+ data instead + mplus_data.is_mp_data = true; + } + break; + } + case PassthroughMode::CLASSIC: + { + if (EXT_AMT == i2c_bus.BusRead(EXT_SLAVE, EXT_ADDR, EXT_AMT, data)) + { + // Passthrough data modifications via wiibrew.org + // Data passing through drops the least significant bit of the axes of the left (or only) + // joystick Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] Bits 0 and 1 + // of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before + Common::SetBit(data[0], 0, Common::ExtractBit(data[5], 0)); + Common::SetBit(data[1], 0, Common::ExtractBit(data[5], 1)); + + // Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below. + mplus_data.is_mp_data = false; + } + else + { + // Read failed (extension unplugged), Send M+ data instead + mplus_data.is_mp_data = true; + } + break; + } + default: + PanicAlert("MotionPlus unknown passthrough-mode %d", (int)GetPassthroughMode()); + break; + } + } + + // If the above logic determined this should be M+ data, update it here + if (mplus_data.is_mp_data) + { + // Wiibrew: "While the Wiimote is still, the values will be about 0x1F7F (8,063)" + // high-velocity range should be about +/- 1500 or 1600 dps + // low-velocity range should be about +/- 400 dps + // Wiibrew implies it shoould be +/- 595 and 2700 + + u16 yaw_value = 0x2000; + u16 roll_value = 0x2000; + u16 pitch_value = 0x2000; + + mplus_data.yaw_slow = 1; + mplus_data.roll_slow = 1; + mplus_data.pitch_slow = 1; + + // Bits 0-7 + mplus_data.yaw1 = yaw_value & 0xff; + mplus_data.roll1 = roll_value & 0xff; + mplus_data.pitch1 = pitch_value & 0xff; + + // Bits 8-13 + mplus_data.yaw2 = yaw_value >> 8; + mplus_data.roll2 = roll_value >> 8; + mplus_data.pitch2 = pitch_value >> 8; + } + + mplus_data.extension_connected = extension_port.IsDeviceConnected(); + mplus_data.zero = 0; +} + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h new file mode 100644 index 0000000000..cf1b1bfa7e --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h @@ -0,0 +1,131 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#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; + + void AttachExtension(Extension* ext); + +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 extension_port{&i2c_bus}; +}; +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp index 4f99a92ee7..eda359c9d4 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "Core/HW/WiimoteEmu/Speaker.h" + #include #include "AudioCommon/AudioCommon.h" @@ -69,13 +71,13 @@ void stopdamnwav() } #endif -void Wiimote::SpeakerData(const u8* data, int length) +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_speaker_logic.reg_data.sample_rate == 0 || length == 0) + if (reg_data.sample_rate == 0 || length == 0) return; // Even if volume is zero we process samples to maintain proper decoder state. @@ -86,7 +88,7 @@ void Wiimote::SpeakerData(const u8* data, int length) unsigned int sample_rate_dividend, sample_length; u8 volume_divisor; - if (m_speaker_logic.reg_data.format == SpeakerLogic::DATA_FORMAT_PCM) + if (reg_data.format == SpeakerLogic::DATA_FORMAT_PCM) { // 8 bit PCM for (int i = 0; i < length; ++i) @@ -99,14 +101,13 @@ void Wiimote::SpeakerData(const u8* data, int length) volume_divisor = 0xff; sample_length = (unsigned int)length; } - else if (m_speaker_logic.reg_data.format == SpeakerLogic::DATA_FORMAT_ADPCM) + else if (reg_data.format == SpeakerLogic::DATA_FORMAT_ADPCM) { // 4 bit Yamaha ADPCM (same as dreamcast) for (int i = 0; i < length; ++i) { - samples[i * 2] = - adpcm_yamaha_expand_nibble(m_speaker_logic.adpcm_state, (data[i] >> 4) & 0xf); - samples[i * 2 + 1] = adpcm_yamaha_expand_nibble(m_speaker_logic.adpcm_state, 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 @@ -116,27 +117,25 @@ void Wiimote::SpeakerData(const u8* data, int length) } else { - ERROR_LOG(IOS_WIIMOTE, "Unknown speaker format %x", m_speaker_logic.reg_data.format); + ERROR_LOG(IOS_WIIMOTE, "Unknown speaker format %x", reg_data.format); return; } - if (m_speaker_logic.reg_data.volume > volume_divisor) + if (reg_data.volume > volume_divisor) { DEBUG_LOG(IOS_WIIMOTE, "Wiimote volume is higher than suspected maximum!"); - volume_divisor = m_speaker_logic.reg_data.volume; + volume_divisor = reg_data.volume; } - // Speaker Pan - // GUI clamps pan setting from -127 to 127. Why? - const auto pan = (unsigned int)(m_options->numeric_settings[0]->GetValue() * 100); + // TODO: use speaker pan law - const unsigned int sample_rate = sample_rate_dividend / m_speaker_logic.reg_data.sample_rate; - float speaker_volume_ratio = (float)m_speaker_logic.reg_data.volume / volume_divisor; + 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((0xff + (2 * pan)) * speaker_volume_ratio, 0, 0xff); + MathUtil::Clamp((0xff + (2 * speaker_pan)) * speaker_volume_ratio, 0, 0xff); unsigned int right_volume = - MathUtil::Clamp((0xff - (2 * pan)) * speaker_volume_ratio, 0, 0xff); + MathUtil::Clamp((0xff - (2 * speaker_pan)) * speaker_volume_ratio, 0, 0xff); g_sound_stream->GetMixer()->SetWiimoteSpeakerVolume(left_volume, right_volume); @@ -166,4 +165,46 @@ void Wiimote::SpeakerData(const u8* data, int length) 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(®_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(®_data, addr, count, data_in); + } +} + } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.h b/Source/Core/Core/HW/WiimoteEmu/Speaker.h new file mode 100644 index 0000000000..80b5e217d0 --- /dev/null +++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.h @@ -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 diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 39c22d3271..a7ea01b3dd 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -5,18 +5,14 @@ #include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include -#include #include -#include -#include -#include -#include -#include +#include #include "Common/BitUtils.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/Config/Config.h" +#include "Common/Logging/Log.h" #include "Common/MathUtil.h" #include "Common/MsgHandler.h" @@ -24,25 +20,24 @@ #include "Core/Config/WiimoteInputSettings.h" #include "Core/ConfigManager.h" #include "Core/Core.h" -#include "Core/HW/Wiimote.h" -#include "Core/HW/WiimoteCommon/WiimoteConstants.h" -#include "Core/HW/WiimoteCommon/WiimoteHid.h" -#include "Core/HW/WiimoteEmu/Attachment/Classic.h" -#include "Core/HW/WiimoteEmu/Attachment/Drums.h" -#include "Core/HW/WiimoteEmu/Attachment/Guitar.h" -#include "Core/HW/WiimoteEmu/Attachment/Nunchuk.h" -#include "Core/HW/WiimoteEmu/Attachment/Turntable.h" -#include "Core/HW/WiimoteEmu/MatrixMath.h" -#include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/Movie.h" #include "Core/NetPlayClient.h" +#include "Core/HW/WiimoteCommon/WiimoteConstants.h" +#include "Core/HW/WiimoteCommon/WiimoteHid.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/WiimoteReal/WiimoteReal.h" + #include "InputCommon/ControllerEmu/Control/Input.h" #include "InputCommon/ControllerEmu/Control/Output.h" +#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h" #include "InputCommon/ControllerEmu/ControlGroup/Buttons.h" #include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" #include "InputCommon/ControllerEmu/ControlGroup/Cursor.h" -#include "InputCommon/ControllerEmu/ControlGroup/Extension.h" #include "InputCommon/ControllerEmu/ControlGroup/Force.h" #include "InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.h" #include "InputCommon/ControllerEmu/ControlGroup/Tilt.h" @@ -51,246 +46,7 @@ namespace WiimoteEmu { -constexpr int SHAKE_FREQ = 6; -// Frame count of one up/down shake -// < 9 no shake detection in "Wario Land: Shake It" -constexpr int SHAKE_STEP_MAX = ::Wiimote::UPDATE_FREQ / SHAKE_FREQ; - -// clang-format off -static const u8 eeprom_data_0[] = { - // IR, maybe more - // assuming last 2 bytes are checksum - // messing up the checksum on purpose - 0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, 0x00, - 0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, 0x00, - // Accelerometer - // Important: checksum is required for tilt games - ACCEL_ZERO_G, ACCEL_ZERO_G, ACCEL_ZERO_G, 0, ACCEL_ONE_G, ACCEL_ONE_G, ACCEL_ONE_G, 0, 0, 0xA3, - ACCEL_ZERO_G, ACCEL_ZERO_G, ACCEL_ZERO_G, 0, ACCEL_ONE_G, ACCEL_ONE_G, ACCEL_ONE_G, 0, 0, 0xA3, -}; -// clang-format on - -static const u8 motion_plus_id[] = {0x00, 0x00, 0xA6, 0x20, 0x00, 0x05}; - -static const u8 eeprom_data_16D0[] = {0x00, 0x00, 0x00, 0xFF, 0x11, 0xEE, 0x00, 0x00, - 0x33, 0xCC, 0x44, 0xBB, 0x00, 0x00, 0x66, 0x99, - 0x77, 0x88, 0x00, 0x00, 0x2B, 0x01, 0xE8, 0x13}; - -// Counts are how many bytes of each feature are in a particular report -static const ReportFeatures reporting_mode_features[] = { - // 0x30: Core Buttons - {2, 0, 0, 0, 4}, - // 0x31: Core Buttons and Accelerometer - {2, 3, 0, 0, 7}, - // 0x32: Core Buttons with 8 Extension bytes - {2, 0, 0, 8, 12}, - // 0x33: Core Buttons and Accelerometer with 12 IR bytes - {2, 3, 12, 0, 19}, - // 0x34: Core Buttons with 19 Extension bytes - {2, 0, 0, 19, 23}, - // 0x35: Core Buttons and Accelerometer with 16 Extension Bytes - {2, 3, 0, 16, 23}, - // 0x36: Core Buttons with 10 IR bytes and 9 Extension Bytes - {2, 0, 10, 9, 23}, - // 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes - {2, 3, 10, 6, 23}, - // 0x38 - 0x3c: Nothing - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - // 0x3d: 21 Extension Bytes - {0, 0, 0, 21, 23}, - // 0x3e - 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes - {2, 1, 0, 18, 23}, - {2, 1, 0, 18, 23}, -}; - -// Used for extension calibration data: -void UpdateCalibrationDataChecksum(std::array& data) -{ - // Last two bytes are the sum of the previous bytes plus 0x55 and 0xaa (0x55 + 0x55) - const u8 checksum1 = std::accumulate(std::begin(data), std::end(data) - 2, u8(0x55)); - const u8 checksum2 = checksum1 + 0x55; - - data[0xe] = checksum1; - data[0xf] = checksum2; -} - -void EmulateShake(AccelData* const accel, ControllerEmu::Buttons* const buttons_group, - const double intensity, u8* const shake_step) -{ - // shake is a bitfield of X,Y,Z shake button states - static const unsigned int btns[] = {0x01, 0x02, 0x04}; - unsigned int shake = 0; - buttons_group->GetState(&shake, btns); - - for (int i = 0; i != 3; ++i) - { - if (shake & (1 << i)) - { - (&(accel->x))[i] += std::sin(MathUtil::TAU * shake_step[i] / SHAKE_STEP_MAX) * intensity; - shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX; - } - else - shake_step[i] = 0; - } -} - -void EmulateDynamicShake(AccelData* const accel, DynamicData& dynamic_data, - ControllerEmu::Buttons* const buttons_group, - const DynamicConfiguration& config, u8* const shake_step) -{ - // shake is a bitfield of X,Y,Z shake button states - static const unsigned int btns[] = {0x01, 0x02, 0x04}; - unsigned int shake = 0; - buttons_group->GetState(&shake, btns); - - for (int i = 0; i != 3; ++i) - { - if ((shake & (1 << i)) && dynamic_data.executing_frames_left[i] == 0) - { - dynamic_data.timing[i]++; - } - else if (dynamic_data.executing_frames_left[i] > 0) - { - (&(accel->x))[i] += - std::sin(MathUtil::TAU * shake_step[i] / SHAKE_STEP_MAX) * dynamic_data.intensity[i]; - shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX; - dynamic_data.executing_frames_left[i]--; - } - else if (shake == 0 && dynamic_data.timing[i] > 0) - { - if (dynamic_data.timing[i] > config.frames_needed_for_high_intensity) - { - dynamic_data.intensity[i] = config.high_intensity; - } - else if (dynamic_data.timing[i] < config.frames_needed_for_low_intensity) - { - dynamic_data.intensity[i] = config.low_intensity; - } - else - { - dynamic_data.intensity[i] = config.med_intensity; - } - dynamic_data.timing[i] = 0; - dynamic_data.executing_frames_left[i] = config.frames_to_execute; - } - else - { - shake_step[i] = 0; - } - } -} - -void EmulateTilt(AccelData* const accel, ControllerEmu::Tilt* const tilt_group, const bool sideways, - const bool upright) -{ - // 180 degrees - const ControllerEmu::Tilt::StateData state = tilt_group->GetState(); - const ControlState roll = state.x * MathUtil::PI; - const ControlState pitch = state.y * MathUtil::PI; - - // Some notes that no one will understand but me :p - // left, forward, up - // lr/ left == negative for all orientations - // ud/ up == negative for upright longways - // fb/ forward == positive for (sideways flat) - - // Determine which axis is which direction - const u32 ud = upright ? (sideways ? 0 : 1) : 2; - const u32 lr = sideways; - const u32 fb = upright ? 2 : (sideways ? 0 : 1); - - // Sign fix - std::array sgn{{-1, 1, 1}}; - if (sideways && !upright) - sgn[fb] *= -1; - if (!sideways && upright) - sgn[ud] *= -1; - - (&accel->x)[ud] = (sin((MathUtil::PI / 2) - std::max(fabs(roll), fabs(pitch)))) * sgn[ud]; - (&accel->x)[lr] = -sin(roll) * sgn[lr]; - (&accel->x)[fb] = sin(pitch) * sgn[fb]; -} - -void EmulateSwing(AccelData* const accel, ControllerEmu::Force* const swing_group, - const double intensity, const bool sideways, const bool upright) -{ - const ControllerEmu::Force::StateData swing = swing_group->GetState(); - - // Determine which axis is which direction - const std::array axis_map{{ - upright ? (sideways ? 0 : 1) : 2, // up/down - sideways, // left/right - upright ? 2 : (sideways ? 0 : 1), // forward/backward - }}; - - // Some orientations have up as positive, some as negative - // same with forward - std::array g_dir{{-1, -1, -1}}; - if (sideways && !upright) - g_dir[axis_map[2]] *= -1; - if (!sideways && upright) - g_dir[axis_map[0]] *= -1; - - for (std::size_t i = 0; i < swing.size(); ++i) - (&accel->x)[axis_map[i]] += swing[i] * g_dir[i] * intensity; -} - -void EmulateDynamicSwing(AccelData* const accel, DynamicData& dynamic_data, - ControllerEmu::Force* const swing_group, - const DynamicConfiguration& config, const bool sideways, - const bool upright) -{ - const ControllerEmu::Force::StateData swing = swing_group->GetState(); - - // Determine which axis is which direction - const std::array axis_map{{ - upright ? (sideways ? 0 : 1) : 2, // up/down - sideways, // left/right - upright ? 2 : (sideways ? 0 : 1), // forward/backward - }}; - - // Some orientations have up as positive, some as negative - // same with forward - std::array g_dir{{-1, -1, -1}}; - if (sideways && !upright) - g_dir[axis_map[2]] *= -1; - if (!sideways && upright) - g_dir[axis_map[0]] *= -1; - - for (std::size_t i = 0; i < swing.size(); ++i) - { - if (swing[i] > 0 && dynamic_data.executing_frames_left[i] == 0) - { - dynamic_data.timing[i]++; - } - else if (dynamic_data.executing_frames_left[i] > 0) - { - (&accel->x)[axis_map[i]] += g_dir[i] * dynamic_data.intensity[i]; - dynamic_data.executing_frames_left[i]--; - } - else if (swing[i] == 0 && dynamic_data.timing[i] > 0) - { - if (dynamic_data.timing[i] > config.frames_needed_for_high_intensity) - { - dynamic_data.intensity[i] = config.high_intensity; - } - else if (dynamic_data.timing[i] < config.frames_needed_for_low_intensity) - { - dynamic_data.intensity[i] = config.low_intensity; - } - else - { - dynamic_data.intensity[i] = config.med_intensity; - } - dynamic_data.timing[i] = 0; - dynamic_data.executing_frames_left[i] = config.frames_to_execute; - } - } -} +using namespace WiimoteCommon; static const u16 button_bitmasks[] = { Wiimote::BUTTON_A, Wiimote::BUTTON_B, Wiimote::BUTTON_ONE, Wiimote::BUTTON_TWO, @@ -298,6 +54,7 @@ static const u16 button_bitmasks[] = { static const u16 dpad_bitmasks[] = {Wiimote::PAD_UP, Wiimote::PAD_DOWN, Wiimote::PAD_LEFT, Wiimote::PAD_RIGHT}; + static const u16 dpad_sideways_bitmasks[] = {Wiimote::PAD_RIGHT, Wiimote::PAD_LEFT, Wiimote::PAD_UP, Wiimote::PAD_DOWN}; @@ -307,93 +64,84 @@ static const char* const named_buttons[] = { void Wiimote::Reset() { - // Wiimote starts in non-continuous CORE mode: - m_reporting_mode = RT_REPORT_CORE; - m_reporting_channel = 0; - m_reporting_auto = false; + // TODO: This value should be re-read if SYSCONF gets changed. + m_sensor_bar_on_top = Config::Get(Config::SYSCONF_SENSOR_BAR_POSITION) != 0; + + SetRumble(false); + + // Wiimote starts in non-continuous CORE mode: + m_reporting_channel = 0; + m_reporting_mode = InputReportID::REPORT_CORE; + m_reporting_continuous = false; - m_rumble_on = false; m_speaker_mute = false; - // TODO: Can probably just set this to the desired extension right away? - m_extension->active_extension = 0; + // EEPROM + m_eeprom = {}; - // eeprom - memset(m_eeprom, 0, sizeof(m_eeprom)); - // calibration data - memcpy(m_eeprom, eeprom_data_0, sizeof(eeprom_data_0)); - // dunno what this is for, copied from old plugin - memcpy(m_eeprom + 0x16D0, eeprom_data_16D0, sizeof(eeprom_data_16D0)); - - // set up the register - - // TODO: kill/move these - memset(&m_speaker_logic.reg_data, 0, sizeof(m_speaker_logic.reg_data)); - memset(&m_camera_logic.reg_data, 0, sizeof(m_camera_logic.reg_data)); - memset(&m_ext_logic.reg_data, 0, sizeof(m_ext_logic.reg_data)); - - memset(&m_motion_plus_logic.reg_data, 0x00, sizeof(m_motion_plus_logic.reg_data)); - memcpy(&m_motion_plus_logic.reg_data.ext_identifier, motion_plus_id, sizeof(motion_plus_id)); - - // TODO: determine meaning of calibration data: - static const u8 mplus_cdata[32] = { - 0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, 0xcf, 0xf0, 0x31, - 0xad, 0xc8, 0x0b, 0x5e, 0x39, 0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, - 0x33, 0x60, 0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d, + // IR calibration and maybe more unknown purposes: + // The meaning of this data needs more investigation. + // Last byte is a checksum. + std::array ir_calibration = { + 0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, 0x74, 0x00, }; + // Purposely not updating checksum because using this data results in a weird IR offset.. + // UpdateCalibrationDataChecksum(ir_calibration, 1); + m_eeprom.ir_calibration_1 = ir_calibration; + m_eeprom.ir_calibration_2 = ir_calibration; - std::copy(std::begin(mplus_cdata), std::end(mplus_cdata), - m_motion_plus_logic.reg_data.calibration_data); - - // TODO: determine the meaning behind this: - static const u8 mp_cert[64] = { - 0x99, 0x1a, 0x07, 0x1b, 0x97, 0xf1, 0x11, 0x78, 0x0c, 0x42, 0x2b, 0x68, 0xdf, - 0x44, 0x38, 0x0d, 0x2b, 0x7e, 0xd6, 0x84, 0x84, 0x58, 0x65, 0xc9, 0xf2, 0x95, - 0xd9, 0xaf, 0xb6, 0xc4, 0x87, 0xd5, 0x18, 0xdb, 0x67, 0x3a, 0xc0, 0x71, 0xec, - 0x3e, 0xf4, 0xe6, 0x7e, 0x35, 0xa3, 0x29, 0xf8, 0x1f, 0xc5, 0x7c, 0x3d, 0xb9, - 0x56, 0x22, 0x95, 0x98, 0x8f, 0xfb, 0x66, 0x3e, 0x9a, 0xdd, 0xeb, 0x7e, + // Accel calibration: + // Last byte is a checksum. + std::array accel_calibration = { + ACCEL_ZERO_G, ACCEL_ZERO_G, ACCEL_ZERO_G, 0, ACCEL_ONE_G, ACCEL_ONE_G, ACCEL_ONE_G, 0, 0, 0, }; + UpdateCalibrationDataChecksum(accel_calibration, 1); + m_eeprom.accel_calibration_1 = accel_calibration; + m_eeprom.accel_calibration_2 = accel_calibration; - std::copy(std::begin(mp_cert), std::end(mp_cert), m_motion_plus_logic.reg_data.cert_data); + // Data of unknown purpose: + constexpr std::array EEPROM_DATA_16D0 = {0x00, 0x00, 0x00, 0xFF, 0x11, 0xEE, 0x00, 0x00, + 0x33, 0xCC, 0x44, 0xBB, 0x00, 0x00, 0x66, 0x99, + 0x77, 0x88, 0x00, 0x00, 0x2B, 0x01, 0xE8, 0x13}; + m_eeprom.unk_2 = EEPROM_DATA_16D0; - // status - memset(&m_status, 0, sizeof(m_status)); + m_read_request = {}; + // Initialize i2c bus: + m_i2c_bus.Reset(); + m_i2c_bus.AddSlave(&m_speaker_logic); + m_i2c_bus.AddSlave(&m_camera_logic); + + // FYI: AttachExtension also connects devices to the i2c bus + // TODO: Only attach M+ when enabled in settings. + m_extension_port.AttachExtension(&m_motion_plus); + + m_active_extension = ExtensionNumber::NONE; + m_motion_plus.AttachExtension(GetActiveExtension()); + // Switch to the desired extension (if any). + HandleExtensionSwap(); + + // Reset sub-devices: + m_speaker_logic.Reset(); + m_camera_logic.Reset(); + m_motion_plus.Reset(); + GetActiveExtension()->Reset(); + + m_status = {}; + // TODO: This will suppress a status report on connect when an extension is already attached. + // I am not 100% sure if this is proper. + m_status.extension = m_extension_port.IsDeviceConnected(); + + // Dynamics: m_shake_step = {}; m_shake_soft_step = {}; m_shake_hard_step = {}; m_swing_dynamic_data = {}; m_shake_dynamic_data = {}; - - m_read_request.size = 0; - - // Yamaha ADPCM state initialize - m_speaker_logic.adpcm_state.predictor = 0; - m_speaker_logic.adpcm_state.step = 127; - - // Initialize i2c bus - m_i2c_bus.Reset(); - // Address 0x51 - m_i2c_bus.AddSlave(&m_speaker_logic); - // Address 0x58 - m_i2c_bus.AddSlave(&m_camera_logic); - - // TODO: only add to bus when enabled - // This also adds the motion plus to the i2c bus - // Address 0x53 (or 0x52 when activated) - m_extension_port.SetAttachment(&m_motion_plus_logic); - - // TODO: add directly to wiimote bus when mplus is disabled - // TODO: only add to bus when connected: - // Address 0x52 (when motion plus is not activated) - // Connected to motion plus i2c_bus (with passthrough by default) - // m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic); } -Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1) +Wiimote::Wiimote(const unsigned int index) : m_index(index) { - // ---- set up all the controls ---- - // buttons groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); for (const char* named_button : named_buttons) @@ -444,14 +192,13 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1 new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z")); // extension - groups.emplace_back(m_extension = new ControllerEmu::Extension(_trans("Extension"))); - m_ext_logic.extension = m_extension; - m_extension->attachments.emplace_back(new WiimoteEmu::None(m_ext_logic.reg_data)); - m_extension->attachments.emplace_back(new WiimoteEmu::Nunchuk(m_ext_logic.reg_data)); - m_extension->attachments.emplace_back(new WiimoteEmu::Classic(m_ext_logic.reg_data)); - m_extension->attachments.emplace_back(new WiimoteEmu::Guitar(m_ext_logic.reg_data)); - m_extension->attachments.emplace_back(new WiimoteEmu::Drums(m_ext_logic.reg_data)); - m_extension->attachments.emplace_back(new WiimoteEmu::Turntable(m_ext_logic.reg_data)); + groups.emplace_back(m_attachments = new ControllerEmu::Attachments(_trans("Extension"))); + m_attachments->AddAttachment(std::make_unique()); + m_attachments->AddAttachment(std::make_unique()); + m_attachments->AddAttachment(std::make_unique()); + m_attachments->AddAttachment(std::make_unique()); + m_attachments->AddAttachment(std::make_unique()); + m_attachments->AddAttachment(std::make_unique()); // rumble groups.emplace_back(m_rumble = new ControllerEmu::ControlGroup(_trans("Rumble"))); @@ -493,16 +240,12 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1 m_hotkeys->AddInput(_trans("Sideways Hold"), false); m_hotkeys->AddInput(_trans("Upright Hold"), false); - // TODO: This value should probably be re-read if SYSCONF gets changed - m_sensor_bar_on_top = Config::Get(Config::SYSCONF_SENSOR_BAR_POSITION) != 0; - - // --- reset eeprom/register/values to default --- Reset(); } std::string Wiimote::GetName() const { - return std::string("Wiimote") + char('1' + m_index); + return StringFromFormat("Wiimote%d", 1 + m_index); } ControllerEmu::ControlGroup* Wiimote::GetWiimoteGroup(WiimoteGroup group) @@ -523,8 +266,8 @@ ControllerEmu::ControlGroup* Wiimote::GetWiimoteGroup(WiimoteGroup group) return m_swing; case WiimoteGroup::Rumble: return m_rumble; - case WiimoteGroup::Extension: - return m_extension; + case WiimoteGroup::Attachments: + return m_attachments; case WiimoteGroup::Options: return m_options; case WiimoteGroup::Hotkeys: @@ -537,510 +280,220 @@ ControllerEmu::ControlGroup* Wiimote::GetWiimoteGroup(WiimoteGroup group) ControllerEmu::ControlGroup* Wiimote::GetNunchukGroup(NunchukGroup group) { - return static_cast(m_extension->attachments[EXT_NUNCHUK].get())->GetGroup(group); + return static_cast(m_attachments->GetAttachmentList()[ExtensionNumber::NUNCHUK].get()) + ->GetGroup(group); } ControllerEmu::ControlGroup* Wiimote::GetClassicGroup(ClassicGroup group) { - return static_cast(m_extension->attachments[EXT_CLASSIC].get())->GetGroup(group); + return static_cast(m_attachments->GetAttachmentList()[ExtensionNumber::CLASSIC].get()) + ->GetGroup(group); } ControllerEmu::ControlGroup* Wiimote::GetGuitarGroup(GuitarGroup group) { - return static_cast(m_extension->attachments[EXT_GUITAR].get())->GetGroup(group); + return static_cast(m_attachments->GetAttachmentList()[ExtensionNumber::GUITAR].get()) + ->GetGroup(group); } ControllerEmu::ControlGroup* Wiimote::GetDrumsGroup(DrumsGroup group) { - return static_cast(m_extension->attachments[EXT_DRUMS].get())->GetGroup(group); + return static_cast(m_attachments->GetAttachmentList()[ExtensionNumber::DRUMS].get()) + ->GetGroup(group); } ControllerEmu::ControlGroup* Wiimote::GetTurntableGroup(TurntableGroup group) { - return static_cast(m_extension->attachments[EXT_TURNTABLE].get())->GetGroup(group); + return static_cast( + m_attachments->GetAttachmentList()[ExtensionNumber::TURNTABLE].get()) + ->GetGroup(group); } -bool Wiimote::Step() +bool Wiimote::ProcessExtensionPortEvent() { - m_motor->control_ref->State(m_rumble_on); + // WiiBrew: Following a connection or disconnection event on the Extension Port, + // data reporting is disabled and the Data Reporting Mode must be reset before new data can + // arrive. + if (m_extension_port.IsDeviceConnected() == m_status.extension) + return false; - // when a movie is active, this button status update is disabled (moved), because movies only - // record data reports. + // FYI: This happens even during a read request which continues after the status report is sent. + m_reporting_mode = InputReportID::REPORT_DISABLED; + + DEBUG_LOG(WIIMOTE, "Sending status report due to extension status change."); + + HandleRequestStatus(OutputReportRequestStatus{}); + + return true; +} + +// Update buttons in status struct from user input. +void Wiimote::UpdateButtonsStatus() +{ + m_status.buttons.hex = 0; + + m_buttons->GetState(&m_status.buttons.hex, button_bitmasks); + m_dpad->GetState(&m_status.buttons.hex, IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks); +} + +void Wiimote::Update() +{ + // Check if connected. + if (0 == m_reporting_channel) + return; + + // Update buttons in the status struct which is sent in 99% of input reports. + // FYI: Movies only sync button updates in data reports. if (!Core::WantsDeterminism()) { + const auto lock = GetStateLock(); UpdateButtonsStatus(); } - // If an extension change is requested in the GUI it will first be disconnected here. - // causing IsDeviceConnected() to return false below: + // If a new extension is requested in the GUI the change will happen here. HandleExtensionSwap(); - // Check if a status report needs to be sent. This happens when extensions are switched. - // ..even during read requests which continue after the status report is sent. - if (m_status.extension != m_extension_port.IsDeviceConnected()) + // Returns true if a report was sent. + if (ProcessExtensionPortEvent()) { - // WiiBrew: Following a connection or disconnection event on the Extension Port, - // data reporting is disabled and the Data Reporting Mode must be reset before new data can - // arrive. - m_reporting_mode = RT_REPORT_DISABLED; - - INFO_LOG(WIIMOTE, "Sending status report due to extension status change."); - - RequestStatus(); - - return true; + // Extension port event occured. + // Don't send any other reports. + return; } if (ProcessReadDataRequest()) { // Read requests suppress normal input reports // Don't send any other reports - return true; - } - - return false; -} - -void Wiimote::UpdateButtonsStatus() -{ - // update buttons in status struct - m_status.buttons.hex = 0; - const bool sideways_modifier_toggle = m_hotkeys->getSettingsModifier()[0]; - const bool sideways_modifier_switch = m_hotkeys->getSettingsModifier()[2]; - const bool is_sideways = - m_sideways_setting->GetValue() ^ sideways_modifier_toggle ^ sideways_modifier_switch; - m_buttons->GetState(&m_status.buttons.hex, button_bitmasks); - m_dpad->GetState(&m_status.buttons.hex, is_sideways ? dpad_sideways_bitmasks : dpad_bitmasks); -} - -void Wiimote::GetButtonData(u8* const data) -{ - // when a movie is active, the button update happens here instead of Wiimote::Step, to avoid - // potential desync issues. - if (Core::WantsDeterminism()) - { - UpdateButtonsStatus(); - } - - reinterpret_cast(data)->hex |= m_status.buttons.hex; -} - -// Produces 10-bit precision values. -void Wiimote::GetAccelData(s16* x, s16* y, s16* z) -{ - const bool sideways_modifier_toggle = m_hotkeys->getSettingsModifier()[0]; - const bool upright_modifier_toggle = m_hotkeys->getSettingsModifier()[1]; - const bool sideways_modifier_switch = m_hotkeys->getSettingsModifier()[2]; - const bool upright_modifier_switch = m_hotkeys->getSettingsModifier()[3]; - const bool is_sideways = - m_sideways_setting->GetValue() ^ sideways_modifier_toggle ^ sideways_modifier_switch; - const bool is_upright = - m_upright_setting->GetValue() ^ upright_modifier_toggle ^ upright_modifier_switch; - - EmulateTilt(&m_accel, m_tilt, is_sideways, is_upright); - - DynamicConfiguration swing_config; - swing_config.low_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_SLOW); - swing_config.med_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_MEDIUM); - swing_config.high_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_FAST); - swing_config.frames_needed_for_high_intensity = - Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_FAST); - swing_config.frames_needed_for_low_intensity = - Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_SLOW); - swing_config.frames_to_execute = Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_LENGTH); - - EmulateSwing(&m_accel, m_swing, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_MEDIUM), - is_sideways, is_upright); - EmulateSwing(&m_accel, m_swing_slow, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_SLOW), - is_sideways, is_upright); - EmulateSwing(&m_accel, m_swing_fast, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_FAST), - is_sideways, is_upright); - EmulateDynamicSwing(&m_accel, m_swing_dynamic_data, m_swing_dynamic, swing_config, is_sideways, - is_upright); - - DynamicConfiguration shake_config; - shake_config.low_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT); - shake_config.med_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM); - shake_config.high_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD); - shake_config.frames_needed_for_high_intensity = - Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_HARD); - shake_config.frames_needed_for_low_intensity = - Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_SOFT); - shake_config.frames_to_execute = Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_LENGTH); - - EmulateShake(&m_accel, m_shake, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM), - m_shake_step.data()); - EmulateShake(&m_accel, m_shake_soft, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT), - m_shake_soft_step.data()); - EmulateShake(&m_accel, m_shake_hard, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD), - m_shake_hard_step.data()); - EmulateDynamicShake(&m_accel, m_shake_dynamic_data, m_shake_dynamic, shake_config, - m_shake_dynamic_step.data()); - - // We now use 2 bits more precision, so multiply by 4 before converting to int - *x = (s16)(4 * (m_accel.x * ACCEL_RANGE + ACCEL_ZERO_G)); - *y = (s16)(4 * (m_accel.y * ACCEL_RANGE + ACCEL_ZERO_G)); - *z = (s16)(4 * (m_accel.z * ACCEL_RANGE + ACCEL_ZERO_G)); - - *x = MathUtil::Clamp(*x, 0, 0x3ff); - *y = MathUtil::Clamp(*y, 0, 0x3ff); - *z = MathUtil::Clamp(*z, 0, 0x3ff); -} - -inline void LowPassFilter(double& var, double newval, double period) -{ - static const double CUTOFF_FREQUENCY = 5.0; - - double RC = 1.0 / CUTOFF_FREQUENCY; - double alpha = period / (period + RC); - var = newval * alpha + var * (1.0 - alpha); -} - -void Wiimote::UpdateIRData(bool use_accel) -{ - u16 x[4], y[4]; - memset(x, 0xFF, sizeof(x)); - - double nsin, ncos; - - if (use_accel) - { - double ax = m_accel.x; - double az = m_accel.z; - const double len = sqrt(ax * ax + az * az); - - if (len) - { - ax /= len; - az /= len; // normalizing the vector - nsin = ax; - ncos = az; - } - else - { - nsin = 0; - ncos = 1; - } - } - else - { - // TODO m_tilt stuff - nsin = 0; - ncos = 1; - } - - LowPassFilter(ir_sin, nsin, 1.0 / 60); - LowPassFilter(ir_cos, ncos, 1.0 / 60); - - static constexpr int camWidth = 1024; - static constexpr int camHeight = 768; - static constexpr double bndup = -0.315447; - static constexpr double bnddown = 0.85; - static constexpr double bndleft = 0.78820266; - static constexpr double bndright = -0.78820266; - static constexpr double dist1 = 100.0 / camWidth; // this seems the optimal distance for zelda - static constexpr double dist2 = 1.2 * dist1; - - const ControllerEmu::Cursor::StateData cursor_state = m_ir->GetState(true); - - std::array v; - for (auto& vtx : v) - { - vtx.x = cursor_state.x * (bndright - bndleft) / 2 + (bndleft + bndright) / 2; - - if (m_sensor_bar_on_top) - vtx.y = cursor_state.y * (bndup - bnddown) / 2 + (bndup + bnddown) / 2; - else - vtx.y = cursor_state.y * (bndup - bnddown) / 2 - (bndup + bnddown) / 2; - - vtx.z = 0; - } - - v[0].x -= (cursor_state.z * 0.5 + 1) * dist1; - v[1].x += (cursor_state.z * 0.5 + 1) * dist1; - v[2].x -= (cursor_state.z * 0.5 + 1) * dist2; - v[3].x += (cursor_state.z * 0.5 + 1) * dist2; - -#define printmatrix(m) \ - PanicAlert("%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", m[0][0], m[0][1], m[0][2], \ - m[0][3], m[1][0], m[1][1], m[1][2], m[1][3], m[2][0], m[2][1], m[2][2], m[2][3], \ - m[3][0], m[3][1], m[3][2], m[3][3]) - Matrix rot, tot; - static Matrix scale; - MatrixScale(scale, 1, camWidth / camHeight, 1); - MatrixRotationByZ(rot, ir_sin, ir_cos); - MatrixMultiply(tot, scale, rot); - - for (std::size_t i = 0; i < v.size(); i++) - { - MatrixTransformVertex(tot, v[i]); - - if ((v[i].x < -1) || (v[i].x > 1) || (v[i].y < -1) || (v[i].y > 1)) - continue; - - x[i] = static_cast(lround((v[i].x + 1) / 2 * (camWidth - 1))); - y[i] = static_cast(lround((v[i].y + 1) / 2 * (camHeight - 1))); - } - - // IR data is read from offset 0x37 on real hardware - auto& data = m_camera_logic.reg_data.camera_data; - // A maximum of 36 bytes: - std::fill(std::begin(data), std::end(data), 0xff); - - // Fill report with valid data when full handshake was done - // TODO: kill magic number: - if (m_camera_logic.reg_data.data[0x30]) - { - // ir mode - switch (m_camera_logic.reg_data.mode) - { - // basic - case 1: - { - wm_ir_basic* const irdata = reinterpret_cast(data); - for (unsigned int i = 0; i < 2; ++i) - { - if (x[i * 2] < 1024 && y[i * 2] < 768) - { - irdata[i].x1 = static_cast(x[i * 2]); - irdata[i].x1hi = x[i * 2] >> 8; - - irdata[i].y1 = static_cast(y[i * 2]); - irdata[i].y1hi = y[i * 2] >> 8; - } - if (x[i * 2 + 1] < 1024 && y[i * 2 + 1] < 768) - { - irdata[i].x2 = static_cast(x[i * 2 + 1]); - irdata[i].x2hi = x[i * 2 + 1] >> 8; - - irdata[i].y2 = static_cast(y[i * 2 + 1]); - irdata[i].y2hi = y[i * 2 + 1] >> 8; - } - } - break; - } - // extended - case 3: - { - wm_ir_extended* const irdata = reinterpret_cast(data); - for (unsigned int i = 0; i < 4; ++i) - if (x[i] < 1024 && y[i] < 768) - { - irdata[i].x = static_cast(x[i]); - irdata[i].xhi = x[i] >> 8; - - irdata[i].y = static_cast(y[i]); - irdata[i].yhi = y[i] >> 8; - - irdata[i].size = 10; - } - break; - } - // full - case 5: - { - wm_ir_full* const irdata = reinterpret_cast(data); - for (unsigned int i = 0; i < 4; ++i) - if (x[i] < 1024 && y[i] < 768) - { - irdata[i].x = static_cast(x[i]); - irdata[i].xhi = x[i] >> 8; - - irdata[i].y = static_cast(y[i]); - irdata[i].yhi = y[i] >> 8; - - irdata[i].size = 10; - - // TODO: implement these sensibly: - // TODO: do high bits of x/y min/max need to be set to zero? - irdata[i].xmin = 0; - irdata[i].ymin = 0; - irdata[i].xmax = 0; - irdata[i].ymax = 0; - irdata[i].zero = 0; - irdata[i].intensity = 0; - } - break; - } - default: - // This is fairly normal, 0xff data is sent in this case: - // WARN_LOG(WIIMOTE, "Game is requesting IR data before setting IR mode."); - break; - } - } -} - -void Wiimote::Update() -{ - // no channel == not connected i guess - if (0 == m_reporting_channel) return; - - // returns true if a report was sent - { - const auto lock = GetStateLock(); - if (Step()) - return; } - u8 data[MAX_PAYLOAD] = {}; + SendDataReport(); +} +void Wiimote::SendDataReport() +{ Movie::SetPolledDevice(); - if (RT_REPORT_DISABLED == m_reporting_mode) + if (InputReportID::REPORT_DISABLED == m_reporting_mode) { // The wiimote is in this disabled after an extension change. // Input reports are not sent, even on button change. return; } - if (RT_REPORT_CORE == m_reporting_mode && !m_reporting_auto) + if (InputReportID::REPORT_CORE == m_reporting_mode && !m_reporting_continuous) { - // TODO: we only need to send a report if the data changed when reporting_auto is disabled - // probably only sensible to check this with RT_REPORT_CORE - // and just always send a report with the other modes - // return; + // TODO: we only need to send a report if the data changed when m_reporting_continuous is + // disabled. It's probably only sensible to check this with REPORT_CORE } - const ReportFeatures& rptf = reporting_mode_features[m_reporting_mode - RT_REPORT_CORE]; - s8 rptf_size = rptf.total_size; + DataReportBuilder rpt_builder(m_reporting_mode); + if (Movie::IsPlayingInput() && - Movie::PlayWiimote(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key)) + Movie::PlayWiimote(m_index, rpt_builder, m_active_extension, GetExtensionEncryptionKey())) { - if (rptf.core_size) - m_status.buttons = *reinterpret_cast(data + rptf.GetCoreOffset()); + // Update buttons in status struct from movie: + rpt_builder.GetCoreData(&m_status.buttons); } else { - data[0] = HID_TYPE_DATA << 4 | HID_PARAM_INPUT; - data[1] = m_reporting_mode; - const auto lock = GetStateLock(); - // hotkey/settings modifier - m_hotkeys->GetState(); // data is later accessed in UpdateButtonsStatus and GetAccelData + // Hotkey / settings modifier + // Data is later accessed in IsSideways and IsUpright + m_hotkeys->GetState(); - u8* feature_ptr = data + rptf.GetCoreOffset(); + // CORE - // core buttons - if (rptf.core_size) + if (rpt_builder.HasCore()) { - GetButtonData(feature_ptr); - feature_ptr += rptf.core_size; + if (Core::WantsDeterminism()) + { + // When running non-deterministically we've already updated buttons in Update() + UpdateButtonsStatus(); + } + + rpt_builder.SetCoreData(m_status.buttons); } - // acceleration - if (rptf.accel_size) + // ACCEL + + // FYI: This data is also used to tilt the IR dots. + NormalizedAccelData norm_accel = {}; + GetAccelData(&norm_accel); + + if (rpt_builder.HasAccel()) { - s16 x, y, z; - GetAccelData(&x, &y, &z); - - auto& core = *reinterpret_cast(data + rptf.GetCoreOffset()); - - // Special cases for the interleaved reports: - // only 8 bits of precision. - if (RT_REPORT_INTERLEAVE1 == m_reporting_mode) - { - *feature_ptr = (x >> 2) & 0xff; - core.acc_bits = (z >> 6) & 0b11; - core.acc_bits2 = (z >> 8) & 0b11; - } - if (RT_REPORT_INTERLEAVE2 == m_reporting_mode) - { - *feature_ptr = (y >> 2) & 0xff; - core.acc_bits = (z >> 2) & 0b11; - core.acc_bits2 = (z >> 4) & 0b11; - } - else - { - auto& accel = *reinterpret_cast(feature_ptr); - accel.x = (x >> 2) & 0xFF; - accel.y = (y >> 2) & 0xFF; - accel.z = (z >> 2) & 0xFF; - - // Set the LSBs - core.acc_bits = x & 0b11; - core.acc_bits2 = (y >> 1) & 0x1; - core.acc_bits2 |= ((z >> 1) & 0x1) << 1; - } - - feature_ptr += rptf.accel_size; + // Calibration values are 8-bit but we want 10-bit precision, so << 2. + DataReportBuilder::AccelData accel = + DenormalizeAccelData(norm_accel, ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2); + rpt_builder.SetAccelData(accel); } - // IR Camera - // TODO: kill use_accel param, I think it exists for TAS reasons.. - if (m_status.ir) - UpdateIRData(rptf.accel_size != 0); + // IR - if (rptf.ir_size) + if (rpt_builder.HasIR()) { + const auto cursor = m_ir->GetState(true); + m_camera_logic.Update(cursor, norm_accel, m_sensor_bar_on_top); + if (!m_status.ir) { - // TODO: Does a real wiimote send 0xFFs in this case? - WARN_LOG(WIIMOTE, "Game is reading IR data without enabling IR logic first."); + // TODO: What does a real wiimote send in this case? 0xFFs ? + // I'm assuming it still reads from the bus? + DEBUG_LOG(WIIMOTE, "Game is reading IR data without enabling IR logic first."); } - // The real wiimote reads camera data from the i2c bus at offset 0x37: - u8 camera_data_offset = offsetof(IRCameraLogic::RegData, camera_data); + // The real wiimote reads camera data from the i2c bus starting at offset 0x37: + const u8 camera_data_offset = + CameraLogic::REPORT_DATA_OFFSET + rpt_builder.GetIRDataFormatOffset(); - if (RT_REPORT_INTERLEAVE2 == m_reporting_mode) - { - // Interleave2 reads the 2nd half of the "FULL" IR data - camera_data_offset += sizeof(wm_ir_full) * 2; - } - - m_i2c_bus.BusRead(IRCameraLogic::DEVICE_ADDR, camera_data_offset, rptf.ir_size, feature_ptr); - - feature_ptr += rptf.ir_size; + m_i2c_bus.BusRead(CameraLogic::I2C_ADDR, camera_data_offset, rpt_builder.GetIRDataSize(), + rpt_builder.GetIRDataPtr()); } - // extension / motion-plus - if (rptf.ext_size) - { - // Update extension first as motion-plus will read from it. - m_ext_logic.Update(); - m_motion_plus_logic.Update(); + // EXT - const u8 slave_addr = ExtensionLogic::DEVICE_ADDR; - if (rptf.ext_size != m_i2c_bus.BusRead(slave_addr, 0x00, rptf.ext_size, feature_ptr)) + if (rpt_builder.HasExt()) + { + // Update extension first as motion-plus may read from it. + GetActiveExtension()->Update(); + m_motion_plus.Update(); + + u8* ext_data = rpt_builder.GetExtDataPtr(); + const u8 ext_size = rpt_builder.GetExtDataSize(); + + if (ext_size != m_i2c_bus.BusRead(ExtensionPort::REPORT_I2C_SLAVE, + ExtensionPort::REPORT_I2C_ADDR, ext_size, ext_data)) { // Real wiimote seems to fill with 0xff on failed bus read - std::fill_n(feature_ptr, rptf.ext_size, 0xff); + std::fill_n(ext_data, ext_size, 0xff); } - - feature_ptr += rptf.ext_size; } - if (feature_ptr != data + rptf_size) - { - PanicAlert("Wiimote input report is the wrong size!"); - } - - Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, - m_ext_logic.ext_key); + Movie::CallWiiInputManip(rpt_builder, m_index, m_active_extension, GetExtensionEncryptionKey()); } if (NetPlay::IsNetPlayRunning()) { - NetPlay_GetWiimoteData(m_index, data, rptf.total_size, m_reporting_mode); - if (rptf.core_size) - m_status.buttons = *reinterpret_cast(data + rptf.core_size); + NetPlay_GetWiimoteData(m_index, rpt_builder.GetDataPtr(), rpt_builder.GetDataSize(), + u8(m_reporting_mode)); + + // TODO: clean up how m_status.buttons is updated. + rpt_builder.GetCoreData(&m_status.buttons); } - Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension, - m_ext_logic.ext_key); + Movie::CheckWiimoteStatus(m_index, rpt_builder, m_active_extension, GetExtensionEncryptionKey()); - // send data report - if (rptf_size) - { - Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, rptf_size); - } + // Send the report: + CallbackInterruptChannel(rpt_builder.GetDataPtr(), rpt_builder.GetDataSize()); // The interleaved reporting modes toggle back and forth: - if (RT_REPORT_INTERLEAVE1 == m_reporting_mode) - m_reporting_mode = RT_REPORT_INTERLEAVE2; - else if (RT_REPORT_INTERLEAVE2 == m_reporting_mode) - m_reporting_mode = RT_REPORT_INTERLEAVE1; + if (InputReportID::REPORT_INTERLEAVE1 == m_reporting_mode) + m_reporting_mode = InputReportID::REPORT_INTERLEAVE2; + else if (InputReportID::REPORT_INTERLEAVE2 == m_reporting_mode) + m_reporting_mode = InputReportID::REPORT_INTERLEAVE1; } void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size) @@ -1048,27 +501,41 @@ void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size) // Check for custom communication if (99 == channel_id) { - // Wii Remote disconnected - // reset eeprom/register/reporting mode + // Wii Remote disconnected. Reset(); + + return; + } + + if (!size) + { + ERROR_LOG(WIIMOTE, "ControlChannel: zero sized data"); return; } m_reporting_channel = channel_id; - const hid_packet* hidp = reinterpret_cast(data); + // FYI: ControlChannel is piped through WiimoteEmu before WiimoteReal just so we can sync the + // channel on state load. This is ugly. + if (WIIMOTE_SRC_REAL == g_wiimote_sources[m_index]) + { + WiimoteReal::ControlChannel(m_index, channel_id, data, size); + return; + } + + const auto& hidp = *reinterpret_cast(data); DEBUG_LOG(WIIMOTE, "Emu ControlChannel (page: %i, type: 0x%02x, param: 0x%02x)", m_index, - hidp->type, hidp->param); + hidp.type, hidp.param); - switch (hidp->type) + switch (hidp.type) { case HID_TYPE_HANDSHAKE: - PanicAlert("HID_TYPE_HANDSHAKE - %s", (hidp->param == HID_PARAM_INPUT) ? "INPUT" : "OUPUT"); + PanicAlert("HID_TYPE_HANDSHAKE - %s", (hidp.param == HID_PARAM_INPUT) ? "INPUT" : "OUPUT"); break; case HID_TYPE_SET_REPORT: - if (HID_PARAM_INPUT == hidp->param) + if (HID_PARAM_INPUT == hidp.param) { PanicAlert("HID_TYPE_SET_REPORT - INPUT"); } @@ -1076,46 +543,61 @@ void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size) { // AyuanX: My experiment shows Control Channel is never used // shuffle2: but lwbt uses this, so we'll do what we must :) - HidOutputReport(reinterpret_cast(hidp->data)); + HIDOutputReport(hidp.data, size - hidp.HEADER_SIZE); + // TODO: Should this be above the previous? u8 handshake = HID_HANDSHAKE_SUCCESS; - Core::Callback_WiimoteInterruptChannel(m_index, channel_id, &handshake, 1); + CallbackInterruptChannel(&handshake, sizeof(handshake)); } break; case HID_TYPE_DATA: - PanicAlert("HID_TYPE_DATA - %s", (hidp->param == HID_PARAM_INPUT) ? "INPUT" : "OUTPUT"); + PanicAlert("HID_TYPE_DATA - %s", (hidp.param == HID_PARAM_INPUT) ? "INPUT" : "OUTPUT"); break; default: - PanicAlert("HidControlChannel: Unknown type %x and param %x", hidp->type, hidp->param); + PanicAlert("HidControlChannel: Unknown type %x and param %x", hidp.type, hidp.param); break; } } void Wiimote::InterruptChannel(const u16 channel_id, const void* data, u32 size) { + if (!size) + { + ERROR_LOG(WIIMOTE, "InterruptChannel: zero sized data"); + return; + } + m_reporting_channel = channel_id; - const hid_packet* hidp = reinterpret_cast(data); + // FYI: InterruptChannel is piped through WiimoteEmu before WiimoteReal just so we can sync the + // channel on state load. This is ugly. + if (WIIMOTE_SRC_REAL == g_wiimote_sources[m_index]) + { + WiimoteReal::InterruptChannel(m_index, channel_id, data, size); + return; + } - switch (hidp->type) + const auto& hidp = *reinterpret_cast(data); + + switch (hidp.type) { case HID_TYPE_DATA: - switch (hidp->param) + switch (hidp.param) { case HID_PARAM_OUTPUT: - HidOutputReport(reinterpret_cast(hidp->data)); + HIDOutputReport(hidp.data, size - hidp.HEADER_SIZE); break; default: - PanicAlert("HidInput: HID_TYPE_DATA - param 0x%02x", hidp->param); + PanicAlert("HidInput: HID_TYPE_DATA - param 0x%02x", hidp.param); break; } break; default: - PanicAlert("HidInput: Unknown type 0x%02x and param 0x%02x", hidp->type, hidp->param); + PanicAlert("HidInput: Unknown type 0x%02x and param 0x%02x", hidp.type, hidp.param); break; } } @@ -1127,7 +609,7 @@ bool Wiimote::CheckForButtonPress() m_buttons->GetState(&buttons, button_bitmasks); m_dpad->GetState(&buttons, dpad_bitmasks); - return (buttons != 0 || m_extension->IsButtonPressed()); + return (buttons != 0 || GetActiveExtension()->IsButtonPressed()); } void Wiimote::LoadDefaults(const ControllerInterface& ciface) @@ -1185,228 +667,42 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface) m_dpad->SetControlExpression(3, "Right"); // Right #endif - // ugly stuff - // enable nunchuk - m_extension->switch_extension = 1; - - // set nunchuk defaults - m_extension->attachments[1]->LoadDefaults(ciface); + // Enable Nunchuk: + constexpr ExtensionNumber DEFAULT_EXT = ExtensionNumber::NUNCHUK; + m_attachments->SetSelectedAttachment(DEFAULT_EXT); + m_attachments->GetAttachmentList()[DEFAULT_EXT]->LoadDefaults(ciface); } -int Wiimote::CurrentExtension() const +Extension* Wiimote::GetActiveExtension() const { - return m_extension->active_extension; + return static_cast(m_attachments->GetAttachmentList()[m_active_extension].get()); } -bool Wiimote::ExtensionLogic::ReadDeviceDetectPin() +EncryptionKey Wiimote::GetExtensionEncryptionKey() const { - return extension->active_extension != 0; + if (ExtensionNumber::NONE == GetActiveExtensionNumber()) + return {}; + + return static_cast(GetActiveExtension())->ext_key; } -void Wiimote::ExtensionLogic::Update() +bool Wiimote::IsSideways() const { - // Update controller data from user input: - // Write data to addr 0x00 of extension register - extension->GetState(reg_data.controller_data); + const bool sideways_modifier_toggle = m_hotkeys->getSettingsModifier()[0]; + const bool sideways_modifier_switch = m_hotkeys->getSettingsModifier()[2]; + return m_sideways_setting->GetValue() ^ sideways_modifier_toggle ^ sideways_modifier_switch; } -// TODO: move this to Common if it doesn't exist already? -template -void SetBit(T& value, u32 bit_number, bool bit_value) +bool Wiimote::IsUpright() const { - if (bit_value) - value |= (1 << bit_number); - else - value &= ~(1 << bit_number); + const bool upright_modifier_toggle = m_hotkeys->getSettingsModifier()[1]; + const bool upright_modifier_switch = m_hotkeys->getSettingsModifier()[3]; + return m_upright_setting->GetValue() ^ upright_modifier_toggle ^ upright_modifier_switch; } -void Wiimote::MotionPlusLogic::Update() +void Wiimote::SetRumble(bool on) { - if (!IsActive()) - { - return; - } - - auto& data = reg_data.controller_data; - auto& mplus_data = *reinterpret_cast(data); - - if (0x0 == reg_data.cert_ready) - { - // Without sending this nonsense, inputs are unresponsive.. even regular buttons - // Device still operates when changing the data slightly so its not any sort of encrpytion - // It even works when removing the is_mp_data bit in the last byte - // My M+ non-inside gives: 61,46,45,aa,0,2 or b6,46,45,9a,0,2 - // static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc | 0x01, 0x02}; - static const u8 init_data[6] = {0x81, 0x46, 0x46, 0xb6, 0x01, 0x02}; - std::copy(std::begin(init_data), std::end(init_data), data); - reg_data.cert_ready = 0x2; - return; - } - - if (0x2 == reg_data.cert_ready) - { - static const u8 init_data[6] = {0x7f, 0xcf, 0xdf, 0x8b, 0x4f, 0x82}; - std::copy(std::begin(init_data), std::end(init_data), data); - reg_data.cert_ready = 0x8; - return; - } - - if (0x8 == reg_data.cert_ready) - { - // A real wiimote takes about 2 seconds to reach this state: - reg_data.cert_ready = 0xe; - } - - if (0x18 == reg_data.cert_ready) - { - // TODO: determine the meaning of this - const u8 mp_cert2[64] = { - 0xa5, 0x84, 0x1f, 0xd6, 0xbd, 0xdc, 0x7a, 0x4c, 0xf3, 0xc0, 0x24, 0xe0, 0x92, - 0xef, 0x19, 0x28, 0x65, 0xe0, 0x62, 0x7c, 0x9b, 0x41, 0x6f, 0x12, 0xc3, 0xac, - 0x78, 0xe4, 0xfc, 0x6b, 0x7b, 0x0a, 0xb4, 0x50, 0xd6, 0xf2, 0x45, 0xf7, 0x93, - 0x04, 0xaf, 0xf2, 0xb7, 0x26, 0x94, 0xee, 0xad, 0x92, 0x05, 0x6d, 0xe5, 0xc6, - 0xd6, 0x36, 0xdc, 0xa5, 0x69, 0x0f, 0xc8, 0x99, 0xf2, 0x1c, 0x4e, 0x0d, - }; - - std::copy(std::begin(mp_cert2), std::end(mp_cert2), reg_data.cert_data); - - if (0x01 != reg_data.cert_enable) - { - PanicAlert("M+ Failure! Game requested cert2 with value other than 0x01. M+ will disconnect " - "shortly unfortunately. Reconnect wiimote and hope for the best."); - } - - // A real wiimote takes about 2 seconds to reach this state: - reg_data.cert_ready = 0x1a; - INFO_LOG(WIIMOTE, "M+ cert 2 ready!"); - } - - // TODO: make sure a motion plus report is sent first after init - - // On real mplus: - // For some reason the first read seems to have garbage data - // is_mp_data and extension_connected are set, but the data is junk - // it does seem to have some sort of pattern though, byte 5 is always 2 - // something like: d5, b0, 4e, 6e, fc, 2 - // When a passthrough mode is set: - // the second read is valid mplus data, which then triggers a read from the extension - // the third read is finally extension data - // If an extension is not attached the data is always mplus data - // even when passthrough is enabled - - // Real M+ seems to only ever read 6 bytes from the extension. - // Data after 6 bytes seems to be zero-filled. - // After reading, the real M+ uses that data for the next frame. - // But we are going to use it for the current frame instead. - const int ext_amt = 6; - // Always read from 0x52 @ 0x00: - const u8 ext_slave = ACTIVE_DEVICE_ADDR; - const u8 ext_addr = 0x00; - - // Try to alternate between M+ and EXT data: - mplus_data.is_mp_data ^= true; - - // hax!!! - static const u8 hacky_mp_data[6] = {0x1d, 0x91, 0x49, 0x87, 0x73, 0x7a}; - static const u8 hacky_nc_data[6] = {0x79, 0x7f, 0x4b, 0x83, 0x8b, 0xec}; - auto& hacky_ptr = mplus_data.is_mp_data ? hacky_mp_data : hacky_nc_data; - std::copy(std::begin(hacky_ptr), std::end(hacky_ptr), data); - return; - - // If the last frame had M+ data try to send some non-M+ data: - if (!mplus_data.is_mp_data) - { - switch (GetPassthroughMode()) - { - case PassthroughMode::DISABLED: - { - // Passthrough disabled, always send M+ data: - mplus_data.is_mp_data = true; - break; - } - case PassthroughMode::NUNCHUK: - { - if (ext_amt == i2c_bus.BusRead(ext_slave, ext_addr, ext_amt, data)) - { - // Passthrough data modifications via wiibrew.org - // Data passing through drops the least significant bit of the three accelerometer values - // Bit 7 of byte 5 is moved to bit 6 of byte 5, overwriting it - SetBit(data[5], 6, Common::ExtractBit(data[5], 7)); - // Bit 0 of byte 4 is moved to bit 7 of byte 5 - SetBit(data[5], 7, Common::ExtractBit(data[4], 0)); - // Bit 3 of byte 5 is moved to bit 4 of byte 5, overwriting it - SetBit(data[5], 4, Common::ExtractBit(data[5], 3)); - // Bit 1 of byte 5 is moved to bit 3 of byte 5 - SetBit(data[5], 3, Common::ExtractBit(data[5], 1)); - // Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it - SetBit(data[5], 2, Common::ExtractBit(data[5], 0)); - - // Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below. - mplus_data.is_mp_data = false; - } - else - { - // Read failed (extension unplugged), Send M+ data instead - mplus_data.is_mp_data = true; - } - break; - } - case PassthroughMode::CLASSIC: - { - if (ext_amt == i2c_bus.BusRead(ext_slave, ext_addr, ext_amt, data)) - { - // Passthrough data modifications via wiibrew.org - // Data passing through drops the least significant bit of the axes of the left (or only) - // joystick Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] Bits 0 and 1 - // of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before - SetBit(data[0], 0, Common::ExtractBit(data[5], 0)); - SetBit(data[1], 0, Common::ExtractBit(data[5], 1)); - - // Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below. - mplus_data.is_mp_data = false; - } - else - { - // Read failed (extension unplugged), Send M+ data instead - mplus_data.is_mp_data = true; - } - break; - } - default: - PanicAlert("MotionPlus unknown passthrough-mode %d", (int)GetPassthroughMode()); - break; - } - } - - // If the above logic determined this should be M+ data, update it here - if (mplus_data.is_mp_data) - { - // Wiibrew: "While the Wiimote is still, the values will be about 0x1F7F (8,063)" - // high-velocity range should be about +/- 1500 or 1600 dps - // low-velocity range should be about +/- 400 dps - // Wiibrew implies it shoould be +/- 595 and 2700 - - u16 yaw_value = 0x2000; - u16 roll_value = 0x2000; - u16 pitch_value = 0x2000; - - mplus_data.yaw_slow = 1; - mplus_data.roll_slow = 1; - mplus_data.pitch_slow = 1; - - // Bits 0-7 - mplus_data.yaw1 = yaw_value & 0xff; - mplus_data.roll1 = roll_value & 0xff; - mplus_data.pitch1 = pitch_value & 0xff; - - // Bits 8-13 - mplus_data.yaw2 = yaw_value >> 8; - mplus_data.roll2 = roll_value >> 8; - mplus_data.pitch2 = pitch_value >> 8; - } - - mplus_data.extension_connected = extension_port.IsDeviceConnected(); - mplus_data.zero = 0; + m_motor->control_ref->State(on); } } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index ebb1c97397..5d6293f47f 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -4,25 +4,25 @@ #pragma once -#include #include +#include #include -#include "Common/Logging/Log.h" -#include "Core/HW/WiimoteCommon/WiimoteConstants.h" -#include "Core/HW/WiimoteCommon/WiimoteHid.h" #include "Core/HW/WiimoteCommon/WiimoteReport.h" -#include "Core/HW/WiimoteEmu/Encryption.h" -#include "InputCommon/ControllerEmu/ControllerEmu.h" -// Registry sizes -#define WIIMOTE_EEPROM_SIZE (16 * 1024) -#define WIIMOTE_EEPROM_FREE_SIZE 0x1700 +#include "Core/HW/WiimoteEmu/Camera.h" +#include "Core/HW/WiimoteEmu/Dynamics.h" +#include "Core/HW/WiimoteEmu/Encryption.h" +#include "Core/HW/WiimoteEmu/ExtensionPort.h" +#include "Core/HW/WiimoteEmu/I2CBus.h" +#include "Core/HW/WiimoteEmu/MotionPlus.h" +#include "Core/HW/WiimoteEmu/Speaker.h" class PointerWrap; namespace ControllerEmu { +class Attachments; class BooleanSetting; class Buttons; class ControlGroup; @@ -35,10 +35,12 @@ class Output; class Tilt; } // namespace ControllerEmu +// Needed for friendship: namespace WiimoteReal { class Wiimote; -} +} // namespace WiimoteReal + namespace WiimoteEmu { enum class WiimoteGroup @@ -50,309 +52,51 @@ enum class WiimoteGroup Tilt, Swing, Rumble, - Extension, + Attachments, Options, Hotkeys }; -enum +enum class NunchukGroup; +enum class ClassicGroup; +enum class GuitarGroup; +enum class DrumsGroup; +enum class TurntableGroup; + +template +void UpdateCalibrationDataChecksum(T& data, int cksum_bytes) { - EXT_NONE, + constexpr u8 CALIBRATION_MAGIC_NUMBER = 0x55; - EXT_NUNCHUK, - EXT_CLASSIC, - EXT_GUITAR, - EXT_DRUMS, - EXT_TURNTABLE -}; + static_assert(std::is_same::value, "Only sane for containers of u8!"); -enum class NunchukGroup -{ - Buttons, - Stick, - Tilt, - Swing, - Shake -}; + auto cksum_start = std::end(data) - cksum_bytes; -enum class ClassicGroup -{ - Buttons, - Triggers, - DPad, - LeftStick, - RightStick -}; + // Checksum is a sum of the previous bytes plus a magic value (0x55). + // Extension calibration data has a 2nd checksum byte which is + // the magic value (0x55) added to the previous checksum byte. + u8 checksum = std::accumulate(std::begin(data), cksum_start, CALIBRATION_MAGIC_NUMBER); -enum class GuitarGroup -{ - Buttons, - Frets, - Strum, - Whammy, - Stick, - SliderBar -}; - -enum class DrumsGroup -{ - Buttons, - Pads, - Stick -}; - -enum class TurntableGroup -{ - Buttons, - Stick, - EffectDial, - LeftTable, - RightTable, - Crossfade -}; -#pragma pack(push, 1) - -struct ReportFeatures -{ - // Byte counts: - // Features are always in the following order in an input report: - u8 core_size, accel_size, ir_size, ext_size, total_size; - - int GetCoreOffset() const { return 2; } - int GetAccelOffset() const { return GetCoreOffset() + core_size; } - int GetIROffset() const { return GetAccelOffset() + accel_size; } - int GetExtOffset() const { return GetIROffset() + ir_size; } -}; - -struct AccelData -{ - double x, y, z; -}; - -// Used for a dynamic swing or -// shake -struct DynamicData -{ - std::array timing; // Hold length in frames for each axis - std::array intensity; // Swing or shake intensity - std::array executing_frames_left; // Number of frames to execute the intensity operation -}; - -// Used for a dynamic swing or -// shake. This is used to pass -// in data that defines the dynamic -// action -struct DynamicConfiguration -{ - double low_intensity; - int frames_needed_for_low_intensity; - - double med_intensity; - // Frames needed for med intensity can be calculated between high & low - - double high_intensity; - int frames_needed_for_high_intensity; - - int frames_to_execute; // How many frames should we execute the action for? -}; - -struct ADPCMState -{ - s32 predictor, step; -}; - -struct ExtensionReg -{ - // 16 bytes of possible extension data - u8 controller_data[0x10]; - - u8 unknown2[0x10]; - - // address 0x20 - u8 calibration[0x10]; - u8 unknown3[0x10]; - - // address 0x40 - u8 encryption_key[0x10]; - u8 unknown4[0xA0]; - - // address 0xF0 - u8 encryption; - u8 unknown5[0x9]; - - // address 0xFA - u8 constant_id[6]; -}; -#pragma pack(pop) - -static_assert(0x100 == sizeof(ExtensionReg)); - -void UpdateCalibrationDataChecksum(std::array& data); - -void EmulateShake(AccelData* accel, ControllerEmu::Buttons* buttons_group, double intensity, - u8* shake_step); - -void EmulateDynamicShake(AccelData* accel, DynamicData& dynamic_data, - ControllerEmu::Buttons* buttons_group, const DynamicConfiguration& config, - u8* shake_step); - -void EmulateTilt(AccelData* accel, ControllerEmu::Tilt* tilt_group, bool sideways = false, - bool upright = false); - -void EmulateSwing(AccelData* accel, ControllerEmu::Force* swing_group, double intensity, - bool sideways = false, bool upright = false); - -void EmulateDynamicSwing(AccelData* accel, DynamicData& dynamic_data, - ControllerEmu::Force* swing_group, const DynamicConfiguration& config, - bool sideways = false, bool upright = false); - -enum -{ - ACCEL_ZERO_G = 0x80, - ACCEL_ONE_G = 0x9A, - ACCEL_RANGE = (ACCEL_ONE_G - ACCEL_ZERO_G), -}; - -class I2CSlave -{ -public: - virtual ~I2CSlave() = default; - - virtual int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) = 0; - virtual int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) = 0; - - template - static int RawRead(T* reg_data, u8 addr, int count, u8* data_out) + for (auto& i = cksum_start; i != std::end(data); ++i) { - static_assert(std::is_pod::value); - static_assert(0x100 == sizeof(T)); - - // TODO: addr wraps around after 0xff - - u8* src = reinterpret_cast(reg_data) + addr; - count = std::min(count, int(reinterpret_cast(reg_data + 1) - src)); - - std::copy_n(src, count, data_out); - - return count; + *i = checksum; + checksum += CALIBRATION_MAGIC_NUMBER; } - - template - static int RawWrite(T* reg_data, u8 addr, int count, const u8* data_in) - { - static_assert(std::is_pod::value); - static_assert(0x100 == sizeof(T)); - - // TODO: addr wraps around after 0xff - - u8* dst = reinterpret_cast(reg_data) + addr; - count = std::min(count, int(reinterpret_cast(reg_data + 1) - dst)); - - std::copy_n(data_in, count, dst); - - return count; - } -}; - -class I2CBus -{ -public: - void AddSlave(I2CSlave* slave) { m_slaves.emplace_back(slave); } - - void RemoveSlave(I2CSlave* slave) - { - m_slaves.erase(std::remove(m_slaves.begin(), m_slaves.end(), slave), m_slaves.end()); - } - - void Reset() { m_slaves.clear(); } - - // TODO: change int to u16 or something - int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) - { - // TODO: the real bus seems to read in blocks of 8. - // peripherals NACK after each 8 bytes. - - // INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); - for (auto& slave : m_slaves) - { - auto const bytes_read = slave->BusRead(slave_addr, addr, count, data_out); - - // A slave responded, we are done. - if (bytes_read) - return bytes_read; - } - - return 0; - } - - // TODO: change int to u16 or something - int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) - { - // TODO: write in blocks of 6 to simulate the real bus - // sometimes it writes in blocks of 8, (speaker data) - // this might trigger activation writes more accurately - - // INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count); - for (auto& slave : m_slaves) - { - auto const bytes_written = slave->BusWrite(slave_addr, addr, count, data_in); - - // A slave responded, we are done. - if (bytes_written) - return bytes_written; - } - - return 0; - } - -private: - // Pointers are unowned: - std::vector m_slaves; -}; - -class ExtensionAttachment : public I2CSlave -{ -public: - virtual bool ReadDeviceDetectPin() = 0; -}; - -class ExtensionPort -{ -public: - ExtensionPort(I2CBus& _i2c_bus) : m_i2c_bus(_i2c_bus) {} - - // Simulates the "device-detect" pin. - // Wiimote uses this to detect extension change.. - // and then send a status report.. - bool IsDeviceConnected() - { - if (m_attachment) - return m_attachment->ReadDeviceDetectPin(); - else - return false; - } - - void SetAttachment(ExtensionAttachment* dev) - { - m_i2c_bus.RemoveSlave(m_attachment); - m_attachment = dev; - - if (dev) - m_i2c_bus.AddSlave(dev); - } - -private: - ExtensionAttachment* m_attachment; - I2CBus& m_i2c_bus; -}; +} class Wiimote : public ControllerEmu::EmulatedController { friend class WiimoteReal::Wiimote; public: - enum + enum : u8 + { + ACCEL_ZERO_G = 0x80, + ACCEL_ONE_G = 0x9A, + }; + + enum : u16 { PAD_LEFT = 0x01, PAD_RIGHT = 0x02, @@ -369,7 +113,10 @@ public: }; explicit Wiimote(unsigned int index); + std::string GetName() const override; + void LoadDefaults(const ControllerInterface& ciface) override; + ControllerEmu::ControlGroup* GetWiimoteGroup(WiimoteGroup group); ControllerEmu::ControlGroup* GetNunchukGroup(NunchukGroup group); ControllerEmu::ControlGroup* GetClassicGroup(ClassicGroup group); @@ -378,409 +125,109 @@ public: ControllerEmu::ControlGroup* GetTurntableGroup(TurntableGroup group); void Update(); + void InterruptChannel(u16 channel_id, const void* data, u32 size); void ControlChannel(u16 channel_id, const void* data, u32 size); - void SpeakerData(const u8* data, int length); bool CheckForButtonPress(); void Reset(); void DoState(PointerWrap& p); - void RealState(); - void LoadDefaults(const ControllerInterface& ciface) override; - - int CurrentExtension() const; - -protected: - bool Step(); - void HidOutputReport(const wm_report* sr, bool send_ack = true); - void HandleExtensionSwap(); - void UpdateButtonsStatus(); - - void GetButtonData(u8* data); - void GetAccelData(s16* x, s16* y, s16* z); - void UpdateIRData(bool use_accel); + // Active extension number is exposed for TAS. + ExtensionNumber GetActiveExtensionNumber() const; private: - I2CBus m_i2c_bus; + // Used only for error generation: + static constexpr u8 EEPROM_I2C_ADDR = 0x50; - ExtensionPort m_extension_port{m_i2c_bus}; + // static constexpr int EEPROM_SIZE = 16 * 1024; + // This is the region exposed over bluetooth: + static constexpr int EEPROM_FREE_SIZE = 0x1700; - struct IRCameraLogic : public I2CSlave - { - // TODO: some of this memory is write-only and should return error 7. + void UpdateButtonsStatus(); - struct RegData - { - // Contains sensitivity and other unknown data - // TODO: Do the IR and Camera enabling reports write to the i2c bus? - // TODO: does disabling the camera peripheral reset the mode or sensitivity? - // TODO: break out this "data" array into some known members - u8 data[0x33]; - u8 mode; - u8 unk[3]; - // addr 0x37 - u8 camera_data[36]; - u8 unk2[165]; - } reg_data; + void GetAccelData(NormalizedAccelData* accel); - static_assert(0x100 == sizeof(reg_data)); + void HIDOutputReport(const void* data, u32 size); - static const u8 DEVICE_ADDR = 0x58; + void HandleReportRumble(const WiimoteCommon::OutputReportRumble&); + void HandleReportLeds(const WiimoteCommon::OutputReportLeds&); + void HandleReportMode(const WiimoteCommon::OutputReportMode&); + void HandleRequestStatus(const WiimoteCommon::OutputReportRequestStatus&); + void HandleReadData(const WiimoteCommon::OutputReportReadData&); + void HandleWriteData(const WiimoteCommon::OutputReportWriteData&); + void HandleIRPixelClock(const WiimoteCommon::OutputReportEnableFeature&); + void HandleIRLogic(const WiimoteCommon::OutputReportEnableFeature&); + void HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature&); + void HandleSpeakerEnable(const WiimoteCommon::OutputReportEnableFeature&); + void HandleSpeakerData(const WiimoteCommon::OutputReportSpeakerData&); - int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override - { - if (DEVICE_ADDR != slave_addr) - return 0; + template + void InvokeHandler(H&& handler, const WiimoteCommon::OutputReportGeneric& rpt, u32 size); - return RawRead(®_data, addr, count, data_out); - } - - int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override - { - if (DEVICE_ADDR != slave_addr) - return 0; - - return RawWrite(®_data, addr, count, data_in); - } - - } m_camera_logic; - - class ExtensionLogic : public ExtensionAttachment - { - public: - ExtensionReg reg_data; - wiimote_key ext_key; - - ControllerEmu::Extension* extension; - - static const u8 DEVICE_ADDR = 0x52; - - void Update(); - - private: - int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override - { - if (DEVICE_ADDR != slave_addr) - return 0; - - if (0x00 == addr) - { - // Here we should sample user input and update controller data - // Moved into Update() function for TAS determinism - // TAS code fails to sync data reads and such.. - // extension->GetState(reg_data.controller_data); - } - - auto const result = RawRead(®_data, addr, count, data_out); - - // Encrypt data read from extension register - // Check if encrypted reads is on - if (0xaa == reg_data.encryption) - { - // INFO_LOG(WIIMOTE, "Encrypted read."); - WiimoteEncrypt(&ext_key, data_out, addr, (u8)count); - } - else - { - // INFO_LOG(WIIMOTE, "Unencrypted read."); - } - - return result; - } - - int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override - { - if (DEVICE_ADDR != slave_addr) - return 0; - - auto const result = RawWrite(®_data, addr, count, data_in); - - if (addr + count > 0x40 && addr < 0x50) - { - // Run the key generation on all writes in the key area, it doesn't matter - // that we send it parts of a key, only the last full key will have an effect - WiimoteGenerateKey(&ext_key, reg_data.encryption_key); - } - - return result; - } - - bool ReadDeviceDetectPin() override; - - } m_ext_logic; - - struct SpeakerLogic : public I2CSlave - { - // 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 - { - // 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]; - } reg_data; -#pragma pack(pop) - - static_assert(0x100 == sizeof(reg_data)); - - // TODO: What actions should reset this state? - ADPCMState adpcm_state; - - static const u8 DEVICE_ADDR = 0x51; - - int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override - { - if (DEVICE_ADDR != slave_addr) - return 0; - - return RawRead(®_data, addr, count, data_out); - } - - int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override - { - if (DEVICE_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(®_data, addr, count, data_in); - } - } - - } m_speaker_logic; - - struct MotionPlusLogic : public ExtensionAttachment - { - private: - // The bus on the end of the motion plus: - I2CBus i2c_bus; - - public: - // The port on the end of the motion plus: - ExtensionPort extension_port{i2c_bus}; - - void Update(); - -#pragma pack(push, 1) - struct MotionPlusRegister - { - 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]; - } reg_data; -#pragma pack(pop) - - static_assert(0x100 == sizeof(reg_data)); - - private: - 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 { return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; } - - PassthroughMode GetPassthroughMode() const - { - return static_cast(reg_data.ext_identifier[4]); - } - - // 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 - { - if (IsActive()) - { - // Motion plus does not respond to 0x53 when activated - if (ACTIVE_DEVICE_ADDR == slave_addr) - return RawRead(®_data, addr, count, data_out); - else - return 0; - } - else - { - if (INACTIVE_DEVICE_ADDR == slave_addr) - return RawRead(®_data, addr, count, data_out); - else - { - // Passthrough to the connected extension (if any) - return i2c_bus.BusRead(slave_addr, addr, count, data_out); - } - } - } - - int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override - { - if (IsActive()) - { - // Motion plus does not respond to 0x53 when activated - if (ACTIVE_DEVICE_ADDR == slave_addr) - { - auto const result = RawWrite(®_data, addr, count, data_in); - - // It seems a write of any value triggers deactivation. - // TODO: kill magic number - if (0xf0 == addr) - { - // Deactivate motion plus: - reg_data.ext_identifier[2] = INACTIVE_DEVICE_ADDR << 1; - reg_data.cert_ready = 0x0; - - // Pass through the activation write to the attached extension: - // The M+ deactivation signal is cleverly the same as EXT activation: - i2c_bus.BusWrite(slave_addr, addr, count, data_in); - } - // TODO: kill magic number - else if (0xf1 == addr) - { - INFO_LOG(WIIMOTE, "M+ cert activation: 0x%x", reg_data.cert_enable); - // 0x14,0x18 is also a valid value - // 0x1a is final value - reg_data.cert_ready = 0x18; - } - // TODO: kill magic number - else if (0xf2 == addr) - { - INFO_LOG(WIIMOTE, "M+ calibration ?? : 0x%x", reg_data.unknown_0xf2[0]); - } - - return result; - } - else - { - // No i2c passthrough when activated. - return 0; - } - } - else - { - if (INACTIVE_DEVICE_ADDR == slave_addr) - { - auto const result = RawWrite(®_data, addr, count, data_in); - - // It seems a write of any value triggers activation. - if (0xfe == addr) - { - INFO_LOG(WIIMOTE, "M+ has been activated: %d", data_in[0]); - - // Activate motion plus: - reg_data.ext_identifier[2] = ACTIVE_DEVICE_ADDR << 1; - // TODO: kill magic number - // reg_data.cert_ready = 0x2; - - // A real M+ is unresponsive on the bus for some time during activation - // Reads fail to ack and ext data gets filled with 0xff for a frame or two - // I don't think we need to emulate that. - - // TODO: activate extension and disable encrption - // also do this if an extension is attached after activation. - std::array data = {0x55}; - i2c_bus.BusWrite(ACTIVE_DEVICE_ADDR, 0xf0, (int)data.size(), data.data()); - } - - return result; - } - else - { - // Passthrough to the connected extension (if any) - return i2c_bus.BusWrite(slave_addr, addr, count, data_in); - } - } - } - - private: - bool ReadDeviceDetectPin() override - { - if (IsActive()) - { - return true; - } - else - { - // When inactive the device detect pin reads from ext port: - return extension_port.IsDeviceConnected(); - } - } - } m_motion_plus_logic; - - void ReportMode(const wm_report_mode* dr); - void SendAck(u8 report_id, WiimoteErrorCode error_code = WiimoteErrorCode::SUCCESS); - void RequestStatus(const wm_request_status* rs = nullptr); - void ReadData(const wm_read_data* rd); - void WriteData(const wm_write_data* wd); + void HandleExtensionSwap(); + bool ProcessExtensionPortEvent(); + void SendDataReport(); bool ProcessReadDataRequest(); + + void RealState(); + + void SetRumble(bool on); + + void CallbackInterruptChannel(const u8* data, u32 size); + void SendAck(WiimoteCommon::OutputReportID rpt_id, WiimoteCommon::ErrorCode err); + + bool IsSideways() const; + bool IsUpright() const; + + Extension* GetActiveExtension() const; + bool NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size, u8 reporting_mode); - // control groups + // TODO: Kill this nonsensical function used for TAS: + EncryptionKey GetExtensionEncryptionKey() const; + + struct ReadRequest + { + WiimoteCommon::AddressSpace space; + u8 slave_address; + u16 address; + u16 size; + }; + + // This is just the usable 0x1700 bytes: + union UsableEEPROMData + { + struct + { + // addr: 0x0000 + std::array ir_calibration_1; + std::array ir_calibration_2; + + std::array accel_calibration_1; + std::array accel_calibration_2; + + // addr: 0x002A + std::array user_data; + + // addr: 0x0FCA + std::array mii_data_1; + std::array mii_data_2; + + // addr: 0x15AA + std::array unk_1; + + // addr: 0x16D0 + std::array unk_2; + std::array unk_3; + }; + + std::array data; + }; + + static_assert(EEPROM_FREE_SIZE == sizeof(UsableEEPROMData)); + + // Control groups for user input: ControllerEmu::Buttons* m_buttons; ControllerEmu::Buttons* m_dpad; ControllerEmu::Buttons* m_shake; @@ -795,49 +242,46 @@ private: ControllerEmu::Force* m_swing_dynamic; ControllerEmu::ControlGroup* m_rumble; ControllerEmu::Output* m_motor; - ControllerEmu::Extension* m_extension; + ControllerEmu::Attachments* m_attachments; ControllerEmu::ControlGroup* m_options; ControllerEmu::BooleanSetting* m_sideways_setting; ControllerEmu::BooleanSetting* m_upright_setting; ControllerEmu::NumericSetting* m_battery_setting; ControllerEmu::ModifySettingsButton* m_hotkeys; - DynamicData m_swing_dynamic_data; - DynamicData m_shake_dynamic_data; + SpeakerLogic m_speaker_logic; + MotionPlus m_motion_plus; + CameraLogic m_camera_logic; - // Wiimote accel data - // TODO: can this member be eliminated? - AccelData m_accel; + I2CBus m_i2c_bus; + + ExtensionPort m_extension_port{&m_i2c_bus}; // Wiimote index, 0-3 const u8 m_index; - double ir_sin, ir_cos; // for the low pass filter + u16 m_reporting_channel; + WiimoteCommon::InputReportID m_reporting_mode; + bool m_reporting_continuous; - bool m_rumble_on; bool m_speaker_mute; - bool m_reporting_auto; - u8 m_reporting_mode; - u16 m_reporting_channel; + // This is just for the IR Camera to compensate for the sensor bar position. + bool m_sensor_bar_on_top; + WiimoteCommon::InputReportStatus m_status; + + ExtensionNumber m_active_extension; + + ReadRequest m_read_request; + UsableEEPROMData m_eeprom; + + // Dynamics: std::array m_shake_step{}; std::array m_shake_soft_step{}; std::array m_shake_hard_step{}; std::array m_shake_dynamic_step{}; - - bool m_sensor_bar_on_top; - - wm_status_report m_status; - - struct ReadRequest - { - WiimoteAddressSpace space; - u8 slave_address; - u16 address; - u16 size; - } m_read_request; - - u8 m_eeprom[WIIMOTE_EEPROM_SIZE]; + DynamicData m_swing_dynamic_data; + DynamicData m_shake_dynamic_data; }; } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteReal/IOWin.cpp b/Source/Core/Core/HW/WiimoteReal/IOWin.cpp index 244fe07f69..a5f9b569ad 100644 --- a/Source/Core/Core/HW/WiimoteReal/IOWin.cpp +++ b/Source/Core/Core/HW/WiimoteReal/IOWin.cpp @@ -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& 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& 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::REQUEST_STATUS), 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& 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(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::READ_DATA_REPLY: + 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 diff --git a/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm b/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm index 2b5df642dd..5436692076 100644 --- a/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm +++ b/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm @@ -298,7 +298,7 @@ void WiimoteDarwin::DisablePowerAssertionInternal() return; } - if (length > MAX_PAYLOAD) + if (length > WiimoteCommon::MAX_PAYLOAD) { WARN_LOG(WIIMOTE, "Dropping packet for Wiimote %i, too large", wm->GetIndex() + 1); return; diff --git a/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp b/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp index 34e7b0c9f8..2d5a89672c 100644 --- a/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp +++ b/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp @@ -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::REQUEST_STATUS), 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 diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp index 51f4969fb6..e930a2b547 100644 --- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp +++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp @@ -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,22 +97,22 @@ void Wiimote::DisableDataReporting() { m_last_input_report.clear(); - // This probably accomplishes nothing. - wm_report_mode rpt = {}; - rpt.mode = RT_REPORT_CORE; + // This accomplishes very little: + OutputReportMode rpt = {}; + rpt.mode = InputReportID::REPORT_CORE; rpt.continuous = 0; rpt.rumble = 0; - QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt)); + QueueReport(u8(OutputReportID::REPORT_MODE), &rpt, sizeof(rpt)); } void Wiimote::EnableDataReporting(u8 mode) { m_last_input_report.clear(); - wm_report_mode rpt = {}; - rpt.mode = mode; + OutputReportMode rpt = {}; + rpt.mode = InputReportID(mode); rpt.continuous = 1; - QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt)); + QueueReport(u8(OutputReportID::REPORT_MODE), &rpt, sizeof(rpt)); } void Wiimote::SetChannel(u16 channel) @@ -139,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(data); - if (hidp->type == HID_TYPE_SET_REPORT) + const auto& hidp = *static_cast(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)); } } } @@ -175,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::LEDS)) { - auto& leds_rpt = *reinterpret_cast(&rpt[2]); + auto& leds_rpt = *reinterpret_cast(&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::SPEAKER_DATA) && + (!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); @@ -251,11 +254,25 @@ bool Wiimote::IsBalanceBoard() if (!ConnectInternal()) return false; // Initialise the extension by writing 0x55 to 0xa400f0, then writing 0x00 to 0xa400fb. - static const u8 init_extension_rpt1[MAX_PAYLOAD] = { - WR_SET_REPORT | BT_OUTPUT, RT_WRITE_DATA, 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}; + // TODO: Use the structs for building these reports.. + static const u8 init_extension_rpt1[MAX_PAYLOAD] = {WR_SET_REPORT | BT_OUTPUT, + u8(OutputReportID::WRITE_DATA), + 0x04, + 0xa4, + 0x00, + 0xf0, + 0x01, + 0x55}; + static const u8 init_extension_rpt2[MAX_PAYLOAD] = {WR_SET_REPORT | BT_OUTPUT, + u8(OutputReportID::WRITE_DATA), + 0x04, + 0xa4, + 0x00, + 0xfb, + 0x01, + 0x00}; + static const u8 status_report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::REQUEST_STATUS), + 0}; if (!IOWrite(init_extension_rpt1, sizeof(init_extension_rpt1)) || !IOWrite(init_extension_rpt2, sizeof(init_extension_rpt2))) { @@ -271,23 +288,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(&buf[2]); + const auto* status = reinterpret_cast(&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::READ_DATA), + 0x04, + 0xa4, + 0x00, + 0xfe, + 0x02, + 0x00}; ret = IOWrite(identify_ext_rpt, sizeof(identify_ext_rpt)); break; } - case RT_READ_DATA_REPLY: + case InputReportID::READ_DATA_REPLY: { - const auto* reply = reinterpret_cast(&buf[2]); + const auto* reply = reinterpret_cast(&buf[2]); if (Common::swap16(reply->address) != 0x00fe) { ERROR_LOG(WIIMOTE, "IsBalanceBoard(): Received unexpected data reply for address %X", @@ -297,15 +320,17 @@ 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(&buf[2]); - if (ack->reportID == RT_READ_DATA && ack->errorID != 0x00) + const auto* ack = reinterpret_cast(&buf[2]); + if (ack->rpt_id == OutputReportID::READ_DATA && 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; @@ -313,7 +338,7 @@ bool Wiimote::IsBalanceBoard() static bool IsDataReport(const Report& rpt) { - return rpt.size() >= 2 && rpt[1] >= RT_REPORT_CORE; + return rpt.size() >= 2 && rpt[1] >= u8(InputReportID::REPORT_CORE); } // Returns the next report that should be sent @@ -365,19 +390,20 @@ bool Wiimote::CheckForButtonPress() const Report& rpt = ProcessReadQueue(); if (rpt.size() >= 4) { - switch (rpt[1]) + switch (InputReportID(rpt[1])) { - 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: + case InputReportID::REPORT_CORE: + case InputReportID::REPORT_CORE_ACCEL: + case InputReportID::REPORT_CORE_EXT8: + case InputReportID::REPORT_CORE_ACCEL_IR12: + case InputReportID::REPORT_CORE_EXT19: + case InputReportID::REPORT_CORE_ACCEL_EXT16: + case InputReportID::REPORT_CORE_IR10_EXT9: + case InputReportID::REPORT_CORE_ACCEL_IR10_EXT6: + case InputReportID::REPORT_INTERLEAVE1: + case InputReportID::REPORT_INTERLEAVE2: // check any button without checking accelerometer data + // TODO: use the structs! if ((rpt[2] & 0x1F) != 0 || (rpt[3] & 0x9F) != 0) { return true; @@ -399,17 +425,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::REPORT_MODE), 0, + u8(InputReportID::REPORT_CORE)}; // 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::LEDS), 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::REQUEST_STATUS), 0}; // TODO: check for sane response? return (IOWrite(mode_report, sizeof(mode_report)) && IOWrite(led_report, sizeof(led_report)) && @@ -441,10 +470,10 @@ void Wiimote::EmuResume() m_last_input_report.clear(); - wm_report_mode rpt = {}; + OutputReportMode rpt = {}; rpt.mode = wm->m_reporting_mode; rpt.continuous = 1; - QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt)); + QueueReport(u8(OutputReportID::REPORT_MODE), &rpt, sizeof(rpt)); NOTICE_LOG(WIIMOTE, "Resuming Wiimote data reporting."); @@ -455,10 +484,10 @@ void Wiimote::EmuPause() { m_last_input_report.clear(); - wm_report_mode rpt = {}; - rpt.mode = RT_REPORT_CORE; + OutputReportMode rpt = {}; + rpt.mode = InputReportID::REPORT_CORE; rpt.continuous = 0; - QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt)); + QueueReport(u8(OutputReportID::REPORT_MODE), &rpt, sizeof(rpt)); NOTICE_LOG(WIIMOTE, "Pausing Wiimote data reporting."); diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h index d1b4c65086..15c955986f 100644 --- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h +++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h @@ -23,6 +23,37 @@ class PointerWrap; namespace WiimoteReal { +using WiimoteCommon::MAX_PAYLOAD; + +typedef std::vector Report; + +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 diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index ceb1cf5a01..ae12975895 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -45,8 +45,13 @@ #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/IOS/USB/Bluetooth/BTEmu.h" #include "Core/IOS/USB/Bluetooth/WiimoteDevice.h" #include "Core/NetPlayProto.h" @@ -64,6 +69,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 +648,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_size ? (data + rptf.GetCoreOffset()) : nullptr; - const u8* const accelData = rptf.accel_size ? (data + rptf.GetAccelOffset()) : nullptr; - const u8* const irData = rptf.ir_size ? (data + rptf.GetIROffset()) : nullptr; - const u8* const extData = rptf.ext_size ? (data + rptf.GetExtOffset()) : 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,31 +682,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)); - - display_str += - StringFromFormat(" ACC:%d,%d,%d (LSB not shown)", dt.x << 2, dt.y << 2, dt.z << 2); - } } - if (irData) + if (rpt.HasAccel()) { + DataReportBuilder::AccelData accel_data; + rpt.GetAccelData(&accel_data); + + // 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 (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 == 1) { - 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( @@ -720,20 +728,22 @@ static void SetWiiInputDisplayString(int remoteID, const u8* const data, } // Classic controller - if (extData && ext == 2) + if (rpt.HasExt() && ext == 2) { - 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"; @@ -756,7 +766,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); } @@ -814,13 +824,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.total_size); + RecordWiimote(wiimote, rpt.GetDataPtr(), rpt.GetDataSize()); } void RecordWiimote(int wiimote, const u8* data, u8 size) @@ -1189,8 +1199,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; @@ -1203,9 +1213,8 @@ bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf, return false; } - u8 size = rptf.total_size; - - u8 sizeInMovie = s_temp_input[s_currentByte]; + const u8 size = rpt.GetDataSize(); + const u8 sizeInMovie = s_temp_input[s_currentByte]; if (size != sizeInMovie) { @@ -1229,7 +1238,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++; @@ -1348,11 +1357,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 diff --git a/Source/Core/Core/Movie.h b/Source/Core/Core/Movie.h index b4e88cc47d..aac76fdd84 100644 --- a/Source/Core/Core/Movie.h +++ b/Source/Core/Core/Movie.h @@ -10,17 +10,13 @@ #include #include "Common/CommonTypes.h" +#include "Core/HW/WiimoteCommon/DataReport.h" +#include "Core/HW/WiimoteEmu/Encryption.h" struct BootParameters; struct GCPadStatus; class PointerWrap; -struct wiimote_key; - -namespace WiimoteEmu -{ -struct ReportFeatures; -} // Per-(video )Movie actions @@ -165,27 +161,27 @@ bool PlayInput(const std::string& movie_path, std::optional* 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; -using WiiManipFunction = - std::function; +using WiiManipFunction = std::function; 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 diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index e81a885267..0acee31f3a 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -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" @@ -61,6 +62,8 @@ namespace NetPlay { +using namespace WiimoteCommon; + static std::mutex crit_netplay_client; static NetPlayClient* netplay_client = nullptr; static std::unique_ptr s_wii_sync_fs; diff --git a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp index 6110fac43b..9c808eae0a 100644 --- a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp @@ -9,6 +9,11 @@ #include #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" diff --git a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuGeneral.cpp b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuGeneral.cpp index 11a7533589..3c34319790 100644 --- a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuGeneral.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuGeneral.cpp @@ -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(extension_group); + auto* ce_extension = static_cast(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( - Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Extension)); - ce_extension->switch_extension = extension; + auto* ce_extension = static_cast( + Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Attachments)); + ce_extension->SetSelectedAttachment(extension); SaveSettings(); } void WiimoteEmuGeneral::Update() { - auto* ce_extension = static_cast( - Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Extension)); + auto* ce_extension = static_cast( + Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Attachments)); - m_extension_combo->setCurrentIndex(ce_extension->switch_extension); + m_extension_combo->setCurrentIndex(ce_extension->GetSelectedAttachment()); } void WiimoteEmuGeneral::LoadSettings() diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 5759f0e70a..14230bf94d 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -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); diff --git a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp index bc7ab87703..f0a837db38 100644 --- a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp +++ b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp @@ -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(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_size ? (report_data + rptf.GetCoreOffset()) : nullptr; - u8* const accel_data = rptf.accel_size ? (report_data + rptf.GetAccelOffset()) : nullptr; - u8* const ir_data = rptf.ir_size ? (report_data + rptf.GetIROffset()) : nullptr; - u8* const ext_data = rptf.ext_size ? (report_data + rptf.GetExtOffset()) : nullptr; - - if (m_remote_buttons_box->isVisible() && buttons_data) + if (m_remote_buttons_box->isVisible() && rpt.HasCore()) { - u16& buttons = (reinterpret_cast(buttons_data))->hex; + DataReportBuilder::CoreData core; + rpt.GetCoreData(&core); + + u16& buttons = core.hex; GetButton(m_a_button, buttons, WiimoteEmu::Wiimote::BUTTON_A); GetButton(m_b_button, buttons, WiimoteEmu::Wiimote::BUTTON_B); GetButton(m_1_button, buttons, WiimoteEmu::Wiimote::BUTTON_ONE); @@ -345,34 +351,28 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp GetButton(m_up_button, buttons, WiimoteEmu::Wiimote::PAD_UP); GetButton(m_down_button, buttons, WiimoteEmu::Wiimote::PAD_DOWN); GetButton(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(accel_data); - //wm_buttons& buttons = *reinterpret_cast(buttons_data); + // FYI: Interleaved reports may behave funky as not all data is always available. - // TODO: lsb - 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; - - // TODO: lsb - //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 x; x[0] = m_ir_x_value->value(); @@ -380,18 +380,19 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp x[2] = x[0] - 10; x[3] = x[1] + 10; - u8 mode; - // Mode 5 not supported in core anyway. - // TODO: Can just use ir_size to determine mode - if (rptf.ext_size) - mode = (rptf.GetExtOffset() - rptf.GetIROffset()) == 10 ? 1 : 3; - else - mode = (rptf.total_size - rptf.GetIROffset()) == 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(ir_data); + memset(ir_data, 0xFF, sizeof(WiimoteEmu::IRBasic) * 2); + auto* const ir_basic = reinterpret_cast(ir_data); for (int i = 0; i < 2; ++i) { if (x[i * 2] < 1024 && y < 768) @@ -416,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(ir_data); + memset(ir_data, 0xFF, sizeof(WiimoteEmu::IRExtended) * 4); + auto* const ir_extended = reinterpret_cast(ir_data); for (size_t i = 0; i < x.size(); ++i) { if (x[i] < 1024 && y < 768) @@ -434,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(ext_data); + u8* const ext_data = rpt.GetExtDataPtr(); + + auto& nunchuk = *reinterpret_cast(ext_data); GetSpinBoxU8(m_nunchuk_stick_x_value, nunchuk.jx); GetSpinBoxU8(m_nunchuk_stick_y_value, nunchuk.jy); @@ -462,13 +465,15 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp GetButton(m_z_button, nunchuk.bt.hex, WiimoteEmu::Nunchuk::BUTTON_Z); nunchuk.bt.hex ^= 0b11; - WiimoteEncrypt(&key, reinterpret_cast(&nunchuk), 0, sizeof(wm_nc)); + key.Encrypt(reinterpret_cast(&nunchuk), 0, sizeof(nunchuk)); } if (m_classic_left_stick_box->isVisible()) { - wm_classic_extension& cc = *reinterpret_cast(ext_data); - WiimoteDecrypt(&key, reinterpret_cast(&cc), 0, sizeof(wm_classic_extension)); + u8* const ext_data = rpt.GetExtDataPtr(); + + auto& cc = *reinterpret_cast(ext_data); + key.Decrypt(reinterpret_cast(&cc), 0, sizeof(cc)); cc.bt.hex ^= 0xFFFF; GetButton(m_classic_a_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_A); @@ -498,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); @@ -515,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(&cc), 0, sizeof(wm_classic_extension)); + key.Encrypt(reinterpret_cast(&cc), 0, sizeof(cc)); } } diff --git a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h index a8decedbf6..d3a830b138 100644 --- a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h +++ b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h @@ -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); diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index ece9bbbde8..d67d1654b3 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -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 diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp new file mode 100644 index 0000000000..e104918fe2 --- /dev/null +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.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 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>& Attachments::GetAttachmentList() const +{ + return m_attachments; +} + +} // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h new file mode 100644 index 0000000000..746bc523ab --- /dev/null +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h @@ -0,0 +1,37 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +#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 att); + + u32 GetSelectedAttachment() const; + void SetSelectedAttachment(u32 val); + + const std::vector>& GetAttachmentList() const; + +private: + std::vector> m_attachments; + + std::atomic m_selected_attachment; +}; +} // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.cpp index 1cc2ac1865..6e8e5cfb70 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.cpp @@ -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(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(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() + "/"); } } diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h index b771adcdb9..8e9fa3f24a 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h @@ -24,7 +24,7 @@ enum class GroupType MixedTriggers, Buttons, Force, - Extension, + Attachments, Tilt, Cursor, Triggers, diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.cpp deleted file mode 100644 index eb9dbc8a99..0000000000 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2017 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "InputCommon/ControllerEmu/ControlGroup/Extension.h" - -#include - -#include "InputCommon/ControllerEmu/ControllerEmu.h" - -namespace ControllerEmu -{ -Extension::Extension(const std::string& name_) : ControlGroup(name_, GroupType::Extension) -{ -} -} // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.h deleted file mode 100644 index 366e94f248..0000000000 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include - -#include "Common/CommonTypes.h" -#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" - -namespace ControllerEmu -{ -class EmulatedController; - -class Extension : public ControlGroup -{ -public: - explicit Extension(const std::string& name); - - void GetState(u8* data); - bool IsButtonPressed() const; - - std::vector> attachments; - - int switch_extension = 0; - int active_extension = 0; -}; -} // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.h index 03dc07ecbd..7b55d3535f 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.h @@ -19,8 +19,5 @@ public: explicit Force(const std::string& name); StateData GetState(); - -private: - StateData m_swing{}; }; } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp index 98b9e5828b..e6623ea754 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp @@ -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(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(ctrlGroup.get())->GetAttachmentList()) { ai->SetDefaultDevice(m_default_device); } diff --git a/Source/Core/InputCommon/InputCommon.vcxproj b/Source/Core/InputCommon/InputCommon.vcxproj index 2bef816726..0c8e95d400 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj +++ b/Source/Core/InputCommon/InputCommon.vcxproj @@ -45,7 +45,7 @@ - + @@ -84,7 +84,7 @@ - + @@ -121,4 +121,4 @@ - + \ No newline at end of file diff --git a/Source/Core/InputCommon/InputCommon.vcxproj.filters b/Source/Core/InputCommon/InputCommon.vcxproj.filters index 01c5c371b6..ec95436d76 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj.filters +++ b/Source/Core/InputCommon/InputCommon.vcxproj.filters @@ -56,9 +56,6 @@ ControllerEmu\ControlGroup - - ControllerEmu\ControlGroup - ControllerEmu\ControlGroup @@ -114,6 +111,9 @@ ControllerInterface + + ControllerEmu\ControlGroup + @@ -146,9 +146,6 @@ ControllerEmu\ControlGroup - - ControllerEmu\ControlGroup - ControllerEmu\ControlGroup @@ -210,8 +207,11 @@ ControllerInterface + + ControllerEmu\ControlGroup + - + \ No newline at end of file From 6c0902ab0a92abe807c201bbd4b1ef912931a17f Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Fri, 4 Jan 2019 15:54:16 -0600 Subject: [PATCH 20/22] WiimoteEmu: Add logic for enabling/disabling M+ and force it disabled for now. --- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 75 ++++++++++++++++--- Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp | 14 ++-- Source/Core/Core/HW/WiimoteEmu/MotionPlus.h | 4 +- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 24 ++++-- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 4 + 5 files changed, 96 insertions(+), 25 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index cf71ba7b50..30a5fa1efc 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -16,6 +16,7 @@ #include "Core/HW/WiimoteReal/WiimoteReal.h" #include "InputCommon/ControllerEmu/ControlGroup/Attachments.h" #include "InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.h" +#include "InputCommon/ControllerEmu/Setting/BooleanSetting.h" namespace WiimoteEmu { @@ -145,12 +146,53 @@ void Wiimote::SendAck(OutputReportID rpt_id, ErrorCode error_code) void Wiimote::HandleExtensionSwap() { - const ExtensionNumber desired_extension = + ExtensionNumber desired_extension_number = static_cast(m_attachments->GetSelectedAttachment()); - if (GetActiveExtensionNumber() != desired_extension) + // const bool desired_motion_plus = m_motion_plus_setting->GetValue(); + const bool desired_motion_plus = false; + + // FYI: AttachExtension also connects devices to the i2c bus + + if (m_is_motion_plus_attached && !desired_motion_plus) { - if (GetActiveExtensionNumber()) + // M+ is attached and it's not wanted, so remove it. + m_extension_port.AttachExtension(GetNoneExtension()); + m_is_motion_plus_attached = false; + + // Also remove extension (if any) from the M+'s ext port. + m_active_extension = ExtensionNumber::NONE; + m_motion_plus.GetExtPort().AttachExtension(GetNoneExtension()); + + // Don't do anything else this update cycle. + return; + } + + if (desired_motion_plus && !m_is_motion_plus_attached) + { + // M+ is wanted and it's not attached + + if (GetActiveExtensionNumber() != ExtensionNumber::NONE) + { + // But an extension is attached. Remove it first. + // (handled below) + desired_extension_number = ExtensionNumber::NONE; + } + else + { + // No extension attached so attach M+. + m_is_motion_plus_attached = true; + m_extension_port.AttachExtension(&m_motion_plus); + m_motion_plus.Reset(); + + // Also attach extension below if desired: + } + } + + if (GetActiveExtensionNumber() != desired_extension_number) + { + // A different extension is wanted (either by user or by the M+ logic above) + if (GetActiveExtensionNumber() != ExtensionNumber::NONE) { // First we must detach the current extension. // The next call will change to the new extension if needed. @@ -158,11 +200,20 @@ void Wiimote::HandleExtensionSwap() } else { - m_active_extension = desired_extension; + m_active_extension = desired_extension_number; + } + + if (m_is_motion_plus_attached) + { + // M+ is attached so attach to it. + m_motion_plus.GetExtPort().AttachExtension(GetActiveExtension()); + } + else + { + // M+ is not attached so attach directly. + m_extension_port.AttachExtension(GetActiveExtension()); } - // TODO: Attach directly when not using M+. - m_motion_plus.AttachExtension(GetActiveExtension()); GetActiveExtension()->Reset(); } } @@ -503,14 +554,18 @@ void Wiimote::DoState(PointerWrap& p) // Sub-devices: m_speaker_logic.DoState(p); - m_motion_plus.DoState(p); m_camera_logic.DoState(p); + p.Do(m_is_motion_plus_attached); p.Do(m_active_extension); - GetActiveExtension()->DoState(p); - // TODO: Handle motion plus being disabled. - m_motion_plus.AttachExtension(GetActiveExtension()); + // Attach M+/Extensions. + m_extension_port.AttachExtension(m_is_motion_plus_attached ? &m_motion_plus : GetNoneExtension()); + (m_is_motion_plus_attached ? m_motion_plus.GetExtPort() : m_extension_port) + .AttachExtension(GetActiveExtension()); + + m_motion_plus.DoState(p); + GetActiveExtension()->DoState(p); // Dynamics // TODO: clean this up: diff --git a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp index 6734c32e53..6c3787a83e 100644 --- a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp @@ -50,11 +50,6 @@ void MotionPlus::DoState(PointerWrap& p) p.Do(reg_data); } -void MotionPlus::AttachExtension(Extension* ext) -{ - extension_port.AttachExtension(ext); -} - bool MotionPlus::IsActive() const { return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; @@ -65,6 +60,11 @@ MotionPlus::PassthroughMode MotionPlus::GetPassthroughMode() const return static_cast(reg_data.ext_identifier[4]); } +ExtensionPort& MotionPlus::GetExtPort() +{ + return m_extension_port; +} + int MotionPlus::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) { if (IsActive()) @@ -175,7 +175,7 @@ bool MotionPlus::ReadDeviceDetectPin() const else { // When inactive the device detect pin reads from the ext port: - return extension_port.IsDeviceConnected(); + return m_extension_port.IsDeviceConnected(); } } @@ -369,7 +369,7 @@ void MotionPlus::Update() mplus_data.pitch2 = pitch_value >> 8; } - mplus_data.extension_connected = extension_port.IsDeviceConnected(); + mplus_data.extension_connected = m_extension_port.IsDeviceConnected(); mplus_data.zero = 0; } diff --git a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h index cf1b1bfa7e..211e666153 100644 --- a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h +++ b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h @@ -21,7 +21,7 @@ public: void Reset() override; void DoState(PointerWrap& p) override; - void AttachExtension(Extension* ext); + ExtensionPort& GetExtPort(); private: #pragma pack(push, 1) @@ -126,6 +126,6 @@ private: // The port on the end of the motion plus: I2CBus i2c_bus; - ExtensionPort extension_port{&i2c_bus}; + ExtensionPort m_extension_port{&i2c_bus}; }; } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index a7ea01b3dd..2dcf99a898 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -112,13 +112,13 @@ void Wiimote::Reset() m_i2c_bus.AddSlave(&m_speaker_logic); m_i2c_bus.AddSlave(&m_camera_logic); - // FYI: AttachExtension also connects devices to the i2c bus - // TODO: Only attach M+ when enabled in settings. - m_extension_port.AttachExtension(&m_motion_plus); - + // Reset extension connections: + m_is_motion_plus_attached = false; m_active_extension = ExtensionNumber::NONE; - m_motion_plus.AttachExtension(GetActiveExtension()); - // Switch to the desired extension (if any). + m_extension_port.AttachExtension(GetNoneExtension()); + m_motion_plus.GetExtPort().AttachExtension(GetNoneExtension()); + + // Switch to desired M+ status and extension (if any). HandleExtensionSwap(); // Reset sub-devices: @@ -215,6 +215,13 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index) // options groups.emplace_back(m_options = new ControllerEmu::ControlGroup(_trans("Options"))); + + // m_options->boolean_settings.emplace_back( + // m_motion_plus_setting = + // new ControllerEmu::BooleanSetting("Attach MotionPlus", _trans("Attach MotionPlus"), + // true, + // ControllerEmu::SettingType::NORMAL, false)); + m_options->boolean_settings.emplace_back( new ControllerEmu::BooleanSetting("Forward Wiimote", _trans("Forward Wii Remote"), true, ControllerEmu::SettingType::NORMAL, true)); @@ -673,6 +680,11 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface) m_attachments->GetAttachmentList()[DEFAULT_EXT]->LoadDefaults(ciface); } +Extension* Wiimote::GetNoneExtension() const +{ + return static_cast(m_attachments->GetAttachmentList()[ExtensionNumber::NONE].get()); +} + Extension* Wiimote::GetActiveExtension() const { return static_cast(m_attachments->GetAttachmentList()[m_active_extension].get()); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 5d6293f47f..cfc9054d18 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -181,6 +181,7 @@ private: bool IsUpright() const; Extension* GetActiveExtension() const; + Extension* GetNoneExtension() const; bool NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size, u8 reporting_mode); @@ -247,6 +248,7 @@ private: ControllerEmu::BooleanSetting* m_sideways_setting; ControllerEmu::BooleanSetting* m_upright_setting; ControllerEmu::NumericSetting* m_battery_setting; + // ControllerEmu::BooleanSetting* m_motion_plus_setting; ControllerEmu::ModifySettingsButton* m_hotkeys; SpeakerLogic m_speaker_logic; @@ -273,6 +275,8 @@ private: ExtensionNumber m_active_extension; + bool m_is_motion_plus_attached; + ReadRequest m_read_request; UsableEEPROMData m_eeprom; From 3945aa624203c2e82160ef7fd045ab74fa47ca95 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Fri, 4 Jan 2019 18:18:44 -0600 Subject: [PATCH 21/22] WiimoteEmu: Fix battery level handling and remove old comments. --- .../Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp | 18 +++++++++++------- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 30a5fa1efc..f989d70576 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -225,14 +225,18 @@ void Wiimote::HandleRequestStatus(const OutputReportRequestStatus&) // Update status struct m_status.extension = m_extension_port.IsDeviceConnected(); - // TODO: Battery level will break determinism in TAS/Netplay + // Based on testing, old WiiLi.org docs, and WiiUse library: + // Max battery level seems to be 0xc8 (decimal 200) + constexpr u8 MAX_BATTERY_LEVEL = 0xc8; + + m_status.battery = (u8)(m_battery_setting->GetValue() * MAX_BATTERY_LEVEL); + + if (Core::WantsDeterminism()) + { + // One less thing to break determinism: + m_status.battery = MAX_BATTERY_LEVEL; + } - // Battery levels in voltage - // 0x00 - 0x32: level 1 - // 0x33 - 0x43: level 2 - // 0x33 - 0x54: level 3 - // 0x55 - 0xff: level 4 - m_status.battery = (u8)(m_battery_setting->GetValue() * 0xff); // Less than 0x20 triggers the low-battery flag: m_status.battery_low = m_status.battery < 0x20; diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 2dcf99a898..cd2b10347c 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -235,7 +235,7 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index) m_options->numeric_settings.emplace_back( std::make_unique(_trans("Speaker Pan"), 0, -127, 127)); m_options->numeric_settings.emplace_back( - m_battery_setting = new ControllerEmu::NumericSetting(_trans("Battery"), 95.0 / 100, 0, 255)); + m_battery_setting = new ControllerEmu::NumericSetting(_trans("Battery"), 95.0 / 100, 0, 100)); // hotkeys groups.emplace_back(m_hotkeys = new ControllerEmu::ModifySettingsButton(_trans("Hotkeys"))); From 9c1a8891e5e33955781b1f07abd290742644586d Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 5 Jan 2019 07:09:11 -0600 Subject: [PATCH 22/22] WiimoteEmu: Code cleanups. --- Source/Core/Common/BitUtils.h | 46 ++++++- .../Core/Core/HW/WiimoteCommon/DataReport.cpp | 58 +++++---- .../Core/Core/HW/WiimoteCommon/DataReport.h | 11 +- .../Core/HW/WiimoteCommon/WiimoteConstants.h | 69 +++++------ .../Core/Core/HW/WiimoteCommon/WiimoteHid.h | 2 +- Source/Core/Core/HW/WiimoteEmu/Camera.cpp | 113 ++++++++++-------- Source/Core/Core/HW/WiimoteEmu/Camera.h | 18 +-- Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp | 2 + Source/Core/Core/HW/WiimoteEmu/Dynamics.h | 10 +- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 84 ++++++------- .../Core/HW/WiimoteEmu/Extension/Classic.cpp | 6 +- .../Core/HW/WiimoteEmu/Extension/Drums.cpp | 6 +- .../Core/HW/WiimoteEmu/Extension/Extension.h | 2 +- .../Core/HW/WiimoteEmu/Extension/Guitar.cpp | 6 +- .../Core/HW/WiimoteEmu/Extension/Nunchuk.cpp | 6 +- .../HW/WiimoteEmu/Extension/Turntable.cpp | 6 +- .../Core/Core/HW/WiimoteEmu/ExtensionPort.h | 2 +- Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp | 27 +++-- Source/Core/Core/HW/WiimoteEmu/MotionPlus.h | 6 +- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 20 ++-- Source/Core/Core/HW/WiimoteReal/IOWin.cpp | 10 +- Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp | 2 +- .../Core/Core/HW/WiimoteReal/WiimoteReal.cpp | 93 ++++++-------- Source/Core/Core/HW/WiimoteReal/WiimoteReal.h | 2 +- Source/Core/Core/Movie.cpp | 5 +- Source/Core/Core/Movie.h | 12 +- Source/Core/Core/NetPlayClient.cpp | 11 +- Source/Core/Core/NetPlayServer.cpp | 12 +- .../ControllerEmu/ControlGroup/Attachments.h | 2 +- 29 files changed, 352 insertions(+), 297 deletions(-) diff --git a/Source/Core/Common/BitUtils.h b/Source/Core/Common/BitUtils.h index 62b9604fab..8b64a92508 100644 --- a/Source/Core/Common/BitUtils.h +++ b/Source/Core/Common/BitUtils.h @@ -200,13 +200,55 @@ inline To BitCast(const From& source) noexcept return reinterpret_cast(storage); } +template +class BitCastPtrType +{ +public: + static_assert(std::is_trivially_copyable(), + "BitCastPtr source type must be trivially copyable."); + static_assert(std::is_trivially_copyable(), + "BitCastPtr destination type must be trivially copyable."); + + explicit BitCastPtrType(PtrType* ptr) : m_ptr(ptr) {} + + // Enable operator= only for pointers to non-const data + template + inline typename std::enable_if() && !std::is_const()>::type + operator=(const S& source) + { + std::memcpy(m_ptr, &source, sizeof(source)); + } + + inline operator T() const + { + T result; + std::memcpy(&result, m_ptr, sizeof(result)); + return result; + } + +private: + PtrType* m_ptr; +}; + +// Provides an aliasing-safe alternative to reinterpret_cast'ing pointers to structs +// Conversion constructor and operator= provided for a convenient syntax. +// Usage: MyStruct s = BitCastPtr(some_ptr); +// BitCastPtr(some_ptr) = s; +template +inline auto BitCastPtr(PtrType* ptr) noexcept -> BitCastPtrType +{ + return BitCastPtrType{ptr}; +} + template void SetBit(T& value, size_t bit_number, bool bit_value) { + static_assert(std::is_unsigned(), "SetBit is only sane on unsigned types."); + if (bit_value) - value |= (1 << bit_number); + value |= (T{1} << bit_number); else - value &= ~(1 << bit_number); + value &= ~(T{1} << bit_number); } } // namespace Common diff --git a/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp b/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp index 47b01d2a65..28f5736153 100644 --- a/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp +++ b/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp @@ -4,6 +4,7 @@ #include +#include "Common/BitUtils.h" #include "Core/HW/WiimoteCommon/DataReport.h" namespace WiimoteCommon @@ -34,7 +35,7 @@ struct IncludeCore : virtual DataReportManipulator void GetCoreData(CoreData* result) const override { - *result = *reinterpret_cast(data_ptr); + *result = Common::BitCastPtr(data_ptr); // Remove accel LSBs. result->hex &= CoreData::BUTTON_MASK; @@ -42,11 +43,13 @@ struct IncludeCore : virtual DataReportManipulator void SetCoreData(const CoreData& new_core) override { - auto& core = *reinterpret_cast(data_ptr); + CoreData core = Common::BitCastPtr(data_ptr); // Don't overwrite accel LSBs. core.hex &= ~CoreData::BUTTON_MASK; core.hex |= new_core.hex & CoreData::BUTTON_MASK; + + Common::BitCastPtr(data_ptr) = core; } }; @@ -71,13 +74,13 @@ struct IncludeAccel : virtual DataReportManipulator { void GetAccelData(AccelData* result) const override { - const auto& accel = *reinterpret_cast(data_ptr + 2); + const AccelMSB accel = Common::BitCastPtr(data_ptr + 2); result->x = accel.x << 2; result->y = accel.y << 2; result->z = accel.z << 2; // LSBs - const auto& core = *reinterpret_cast(data_ptr); + const CoreData core = Common::BitCastPtr(data_ptr); result->x |= core.acc_bits & 0b11; result->y |= (core.acc_bits2 & 0b1) << 1; result->z |= core.acc_bits2 & 0b10; @@ -85,16 +88,18 @@ struct IncludeAccel : virtual DataReportManipulator void SetAccelData(const AccelData& new_accel) override { - auto& accel = *reinterpret_cast(data_ptr + 2); + AccelMSB accel = {}; accel.x = new_accel.x >> 2; accel.y = new_accel.y >> 2; accel.z = new_accel.z >> 2; + Common::BitCastPtr(data_ptr + 2) = accel; // LSBs - auto& core = *reinterpret_cast(data_ptr); + CoreData core = Common::BitCastPtr(data_ptr); core.acc_bits = (new_accel.x >> 0) & 0b11; core.acc_bits2 = (new_accel.y >> 1) & 0x1; core.acc_bits2 |= (new_accel.z & 0xb10); + Common::BitCastPtr(data_ptr) = core; } bool HasAccel() const override { return true; } @@ -196,10 +201,10 @@ struct ReportInterleave1 : IncludeCore, IncludeIR<3, 18, 0>, NoExt { accel->x = data_ptr[2] << 2; - // Retain lower 6LSBs. + // Retain lower 6 bits. accel->z &= 0b111111; - const auto& core = *reinterpret_cast(data_ptr); + const CoreData core = Common::BitCastPtr(data_ptr); accel->z |= (core.acc_bits << 6) | (core.acc_bits2 << 8); } @@ -207,9 +212,10 @@ struct ReportInterleave1 : IncludeCore, IncludeIR<3, 18, 0>, NoExt { data_ptr[2] = accel.x >> 2; - auto& core = *reinterpret_cast(data_ptr); + CoreData core = Common::BitCastPtr(data_ptr); core.acc_bits = (accel.z >> 6) & 0b11; core.acc_bits2 = (accel.z >> 8) & 0b11; + Common::BitCastPtr(data_ptr) = core; } bool HasAccel() const override { return true; } @@ -226,10 +232,10 @@ struct ReportInterleave2 : IncludeCore, IncludeIR<3, 18, 18>, NoExt { accel->y = data_ptr[2] << 2; - // Retain upper 4MSBs. + // Retain upper 4 bits. accel->z &= ~0b111111; - const auto& core = *reinterpret_cast(data_ptr); + const CoreData core = Common::BitCastPtr(data_ptr); accel->z |= (core.acc_bits << 2) | (core.acc_bits2 << 4); } @@ -237,9 +243,10 @@ struct ReportInterleave2 : IncludeCore, IncludeIR<3, 18, 18>, NoExt { data_ptr[2] = accel.y >> 2; - auto& core = *reinterpret_cast(data_ptr); + CoreData core = Common::BitCastPtr(data_ptr); core.acc_bits = (accel.z >> 2) & 0b11; core.acc_bits2 = (accel.z >> 4) & 0b11; + Common::BitCastPtr(data_ptr) = core; } bool HasAccel() const override { return true; } @@ -257,47 +264,47 @@ std::unique_ptr MakeDataReportManipulator(InputReportID r switch (rpt_id) { - case InputReportID::REPORT_CORE: + case InputReportID::ReportCore: // 0x30: Core Buttons ptr = std::make_unique(); break; - case InputReportID::REPORT_CORE_ACCEL: + case InputReportID::ReportCoreAccel: // 0x31: Core Buttons and Accelerometer ptr = std::make_unique(); break; - case InputReportID::REPORT_CORE_EXT8: + case InputReportID::ReportCoreExt8: // 0x32: Core Buttons with 8 Extension bytes ptr = std::make_unique(); break; - case InputReportID::REPORT_CORE_ACCEL_IR12: + case InputReportID::ReportCoreAccelIR12: // 0x33: Core Buttons and Accelerometer with 12 IR bytes ptr = std::make_unique(); break; - case InputReportID::REPORT_CORE_EXT19: + case InputReportID::ReportCoreExt19: // 0x34: Core Buttons with 19 Extension bytes ptr = std::make_unique(); break; - case InputReportID::REPORT_CORE_ACCEL_EXT16: + case InputReportID::ReportCoreAccelExt16: // 0x35: Core Buttons and Accelerometer with 16 Extension Bytes ptr = std::make_unique(); break; - case InputReportID::REPORT_CORE_IR10_EXT9: + case InputReportID::ReportCoreIR10Ext9: // 0x36: Core Buttons with 10 IR bytes and 9 Extension Bytes ptr = std::make_unique(); break; - case InputReportID::REPORT_CORE_ACCEL_IR10_EXT6: + case InputReportID::ReportCoreAccelIR10Ext6: // 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes ptr = std::make_unique(); break; - case InputReportID::REPORT_EXT21: + case InputReportID::ReportExt21: // 0x3d: 21 Extension Bytes ptr = std::make_unique(); break; - case InputReportID::REPORT_INTERLEAVE1: + case InputReportID::ReportInterleave1: // 0x3e - 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes ptr = std::make_unique(); break; - case InputReportID::REPORT_INTERLEAVE2: + case InputReportID::ReportInterleave2: ptr = std::make_unique(); break; default: @@ -327,9 +334,8 @@ InputReportID DataReportBuilder::GetMode() const bool DataReportBuilder::IsValidMode(InputReportID mode) { - return (mode >= InputReportID::REPORT_CORE && - mode <= InputReportID::REPORT_CORE_ACCEL_IR10_EXT6) || - (mode >= InputReportID::REPORT_EXT21 && InputReportID::REPORT_INTERLEAVE2 <= mode); + return (mode >= InputReportID::ReportCore && mode <= InputReportID::ReportCoreAccelIR10Ext6) || + (mode >= InputReportID::ReportExt21 && InputReportID::ReportInterleave2 <= mode); } bool DataReportBuilder::HasCore() const diff --git a/Source/Core/Core/HW/WiimoteCommon/DataReport.h b/Source/Core/Core/HW/WiimoteCommon/DataReport.h index f48981677b..dd2833728d 100644 --- a/Source/Core/Core/HW/WiimoteCommon/DataReport.h +++ b/Source/Core/Core/HW/WiimoteCommon/DataReport.h @@ -27,7 +27,7 @@ public: u16 x, y, z; }; - typedef ButtonData CoreData; + using CoreData = ButtonData; virtual bool HasCore() const = 0; virtual bool HasAccel() const = 0; @@ -63,13 +63,10 @@ std::unique_ptr MakeDataReportManipulator(InputReportID r class DataReportBuilder { public: - DataReportBuilder(InputReportID rpt_id); + explicit DataReportBuilder(InputReportID rpt_id); - typedef ButtonData CoreData; - typedef DataReportManipulator::AccelData AccelData; - - typedef std::vector IRData; - typedef std::vector ExtData; + using CoreData = ButtonData; + using AccelData = DataReportManipulator::AccelData; void SetMode(InputReportID rpt_id); InputReportID GetMode() const; diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h index 7b0e5dc30d..6b57568f9a 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h @@ -12,45 +12,45 @@ constexpr u8 MAX_PAYLOAD = 23; enum class InputReportID : u8 { - STATUS = 0x20, - READ_DATA_REPLY = 0x21, - ACK = 0x22, + Status = 0x20, + ReadDataReply = 0x21, + Ack = 0x22, // Not a real value on the wiimote, just a state to disable reports: - REPORT_DISABLED = 0x00, + ReportDisabled = 0x00, - REPORT_CORE = 0x30, - REPORT_CORE_ACCEL = 0x31, - REPORT_CORE_EXT8 = 0x32, - REPORT_CORE_ACCEL_IR12 = 0x33, - REPORT_CORE_EXT19 = 0x34, - REPORT_CORE_ACCEL_EXT16 = 0x35, - REPORT_CORE_IR10_EXT9 = 0x36, - REPORT_CORE_ACCEL_IR10_EXT6 = 0x37, + ReportCore = 0x30, + ReportCoreAccel = 0x31, + ReportCoreExt8 = 0x32, + ReportCoreAccelIR12 = 0x33, + ReportCoreExt19 = 0x34, + ReportCoreAccelExt16 = 0x35, + ReportCoreIR10Ext9 = 0x36, + ReportCoreAccelIR10Ext6 = 0x37, - REPORT_EXT21 = 0x3d, - REPORT_INTERLEAVE1 = 0x3e, - REPORT_INTERLEAVE2 = 0x3f, + ReportExt21 = 0x3d, + ReportInterleave1 = 0x3e, + ReportInterleave2 = 0x3f, }; enum class OutputReportID : u8 { - RUMBLE = 0x10, - LEDS = 0x11, - REPORT_MODE = 0x12, - IR_PIXEL_CLOCK = 0x13, - SPEAKER_ENABLE = 0x14, - REQUEST_STATUS = 0x15, - WRITE_DATA = 0x16, - READ_DATA = 0x17, - SPEAKER_DATA = 0x18, - SPEAKER_MUTE = 0x19, - IR_LOGIC = 0x1A, + Rumble = 0x10, + LED = 0x11, + ReportMode = 0x12, + IRPixelClock = 0x13, + SpeakerEnable = 0x14, + RequestStatus = 0x15, + WriteData = 0x16, + ReadData = 0x17, + SpeakerData = 0x18, + SpeakerMute = 0x19, + IRLogic = 0x1a, }; enum class LED : u8 { - NONE = 0x00, + None = 0x00, LED_1 = 0x10, LED_2 = 0x20, LED_3 = 0x40, @@ -63,19 +63,16 @@ enum class AddressSpace : u8 // However attempting to access this device directly results in an error. EEPROM = 0x00, // 0x01 is never used but it does function on a real wiimote: - I2C_BUS_ALT = 0x01, - I2C_BUS = 0x02, + I2CBusAlt = 0x01, + I2CBus = 0x02, }; enum class ErrorCode : u8 { - SUCCESS = 0, - INVALID_SPACE = 6, - NACK = 7, - INVALID_ADDRESS = 8, - - // Not a real value: - DO_NOT_SEND_ACK = 0xff, + Success = 0, + InvalidSpace = 6, + Nack = 7, + InvalidAddress = 8, }; } // namespace WiimoteCommon diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h index ccd05a9303..fbcbd8b9e2 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h @@ -54,7 +54,7 @@ struct TypedHIDInputData T data; - static_assert(std::is_pod::value); + static_assert(std::is_pod()); u8* GetData() { return reinterpret_cast(this); } const u8* GetData() const { return reinterpret_cast(this); } diff --git a/Source/Core/Core/HW/WiimoteEmu/Camera.cpp b/Source/Core/Core/HW/WiimoteEmu/Camera.cpp index 0a8d20c67e..b2807e7122 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Camera.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Camera.cpp @@ -4,6 +4,7 @@ #include "Core/HW/WiimoteEmu/Camera.h" +#include "Common/BitUtils.h" #include "Common/ChunkFile.h" #include "Core/HW/WiimoteCommon/WiimoteReport.h" #include "Core/HW/WiimoteEmu/MatrixMath.h" @@ -39,9 +40,6 @@ int CameraLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) void CameraLogic::Update(const ControllerEmu::Cursor::StateData& cursor, const NormalizedAccelData& accel, bool sensor_bar_on_top) { - u16 x[4], y[4]; - memset(x, 0xFF, sizeof(x)); - double nsin, ncos; // Ugly code to figure out the wiimote's current angle. @@ -73,7 +71,10 @@ void CameraLogic::Update(const ControllerEmu::Cursor::StateData& cursor, static constexpr double dist1 = 100.0 / camWidth; // this seems the optimal distance for zelda static constexpr double dist2 = 1.2 * dist1; - std::array v; + constexpr int NUM_POINTS = 4; + + std::array v; + for (auto& vtx : v) { vtx.x = cursor.x * (bndright - bndleft) / 2 + (bndleft + bndright) / 2; @@ -104,6 +105,10 @@ void CameraLogic::Update(const ControllerEmu::Cursor::StateData& cursor, MatrixRotationByZ(rot, ir_sin, ir_cos); MatrixMultiply(tot, scale, rot); + u16 x[NUM_POINTS], y[NUM_POINTS]; + memset(x, 0xFF, sizeof(x)); + memset(y, 0xFF, sizeof(y)); + for (std::size_t i = 0; i < v.size(); i++) { MatrixTransformVertex(tot, v[i]); @@ -113,6 +118,12 @@ void CameraLogic::Update(const ControllerEmu::Cursor::StateData& cursor, x[i] = static_cast(lround((v[i].x + 1) / 2 * (camWidth - 1))); y[i] = static_cast(lround((v[i].y + 1) / 2 * (camHeight - 1))); + + if (x[i] >= camWidth || y[i] >= camHeight) + { + x[i] = -1; + y[i] = -1; + } } // IR data is read from offset 0x37 on real hardware @@ -127,70 +138,70 @@ void CameraLogic::Update(const ControllerEmu::Cursor::StateData& cursor, switch (reg_data.mode) { case IR_MODE_BASIC: - { - auto* const irdata = reinterpret_cast(data); for (unsigned int i = 0; i < 2; ++i) { - if (x[i * 2] < 1024 && y[i * 2] < 768) - { - irdata[i].x1 = static_cast(x[i * 2]); - irdata[i].x1hi = x[i * 2] >> 8; + IRBasic irdata = {}; - irdata[i].y1 = static_cast(y[i * 2]); - irdata[i].y1hi = y[i * 2] >> 8; - } - if (x[i * 2 + 1] < 1024 && y[i * 2 + 1] < 768) - { - irdata[i].x2 = static_cast(x[i * 2 + 1]); - irdata[i].x2hi = x[i * 2 + 1] >> 8; + irdata.x1 = static_cast(x[i * 2]); + irdata.x1hi = x[i * 2] >> 8; + irdata.y1 = static_cast(y[i * 2]); + irdata.y1hi = y[i * 2] >> 8; - irdata[i].y2 = static_cast(y[i * 2 + 1]); - irdata[i].y2hi = y[i * 2 + 1] >> 8; + irdata.x2 = static_cast(x[i * 2 + 1]); + irdata.x2hi = x[i * 2 + 1] >> 8; + irdata.y2 = static_cast(y[i * 2 + 1]); + irdata.y2hi = y[i * 2 + 1] >> 8; + + Common::BitCastPtr(data + i * sizeof(IRBasic)) = irdata; + } + break; + case IR_MODE_EXTENDED: + for (unsigned int i = 0; i < 4; ++i) + { + if (x[i] < camWidth) + { + IRExtended irdata = {}; + + irdata.x = static_cast(x[i]); + irdata.xhi = x[i] >> 8; + + irdata.y = static_cast(y[i]); + irdata.yhi = y[i] >> 8; + + irdata.size = 10; + + Common::BitCastPtr(data + i * sizeof(IRExtended)) = irdata; } } break; - } - case IR_MODE_EXTENDED: - { - auto* const irdata = reinterpret_cast(data); - for (unsigned int i = 0; i < 4; ++i) - if (x[i] < 1024 && y[i] < 768) - { - irdata[i].x = static_cast(x[i]); - irdata[i].xhi = x[i] >> 8; - - irdata[i].y = static_cast(y[i]); - irdata[i].yhi = y[i] >> 8; - - irdata[i].size = 10; - } - break; - } case IR_MODE_FULL: - { - auto* const irdata = reinterpret_cast(data); for (unsigned int i = 0; i < 4; ++i) - if (x[i] < 1024 && y[i] < 768) + { + if (x[i] < camWidth) { - irdata[i].x = static_cast(x[i]); - irdata[i].xhi = x[i] >> 8; + IRFull irdata = {}; - irdata[i].y = static_cast(y[i]); - irdata[i].yhi = y[i] >> 8; + irdata.x = static_cast(x[i]); + irdata.xhi = x[i] >> 8; - irdata[i].size = 10; + irdata.y = static_cast(y[i]); + irdata.yhi = y[i] >> 8; + + irdata.size = 10; // TODO: implement these sensibly: // TODO: do high bits of x/y min/max need to be set to zero? - irdata[i].xmin = 0; - irdata[i].ymin = 0; - irdata[i].xmax = 0; - irdata[i].ymax = 0; - irdata[i].zero = 0; - irdata[i].intensity = 0; + irdata.xmin = 0; + irdata.ymin = 0; + irdata.xmax = 0; + irdata.ymax = 0; + irdata.zero = 0; + irdata.intensity = 0; + + Common::BitCastPtr(data + i * sizeof(IRFull)) = irdata; } + } break; - } default: // This seems to be fairly common, 0xff data is sent in this case: // WARN_LOG(WIIMOTE, "Game is requesting IR data before setting IR mode."); diff --git a/Source/Core/Core/HW/WiimoteEmu/Camera.h b/Source/Core/Core/HW/WiimoteEmu/Camera.h index fdcc48858b..b42dddd6b9 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Camera.h +++ b/Source/Core/Core/HW/WiimoteEmu/Camera.h @@ -64,6 +64,13 @@ public: IR_MODE_FULL = 5, }; + void Reset(); + void DoState(PointerWrap& p); + void Update(const ControllerEmu::Cursor::StateData& cursor, const NormalizedAccelData& accel, + bool sensor_bar_on_top); + + static constexpr u8 I2C_ADDR = 0x58; + private: // TODO: some of this memory is write-only and should return error 7. #pragma pack(push, 1) @@ -85,20 +92,13 @@ private: static_assert(0x100 == sizeof(Register)); public: - static constexpr u8 I2C_ADDR = 0x58; - // The real wiimote reads camera data from the i2c bus at offset 0x37: static const u8 REPORT_DATA_OFFSET = offsetof(Register, camera_data); - void Reset(); - void DoState(PointerWrap& p); - void Update(const ControllerEmu::Cursor::StateData& cursor, const NormalizedAccelData& accel, - bool sensor_bar_on_top); - private: - Register reg_data; - 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; }; } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp b/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp index 47214ea46b..2d954d36d4 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp @@ -37,7 +37,9 @@ void EmulateShake(NormalizedAccelData* const accel, ControllerEmu::Buttons* cons shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX; } else + { shake_step[i] = 0; + } } } diff --git a/Source/Core/Core/HW/WiimoteEmu/Dynamics.h b/Source/Core/Core/HW/WiimoteEmu/Dynamics.h index 116c03d912..a1258725d1 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Dynamics.h +++ b/Source/Core/Core/HW/WiimoteEmu/Dynamics.h @@ -15,11 +15,11 @@ namespace WiimoteEmu { struct NormalizedAccelData { + // Unit is 1G double x, y, z; }; -// Used for a dynamic swing or -// shake +// Used for a dynamic swing or shake struct DynamicData { std::array timing; // Hold length in frames for each axis @@ -27,10 +27,8 @@ struct DynamicData std::array executing_frames_left; // Number of frames to execute the intensity operation }; -// Used for a dynamic swing or -// shake. This is used to pass -// in data that defines the dynamic -// action +// Used for a dynamic swing or shake. +// This is used to pass in data that defines the dynamic action struct DynamicConfiguration { double low_intensity; diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index f989d70576..7a47ccf7d2 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include #include +#include "Common/BitUtils.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" @@ -43,7 +45,7 @@ void Wiimote::HandleReportMode(const OutputReportMode& dr) m_reporting_continuous = dr.continuous; m_reporting_mode = dr.mode; - SendAck(OutputReportID::REPORT_MODE, ErrorCode::SUCCESS); + SendAck(OutputReportID::ReportMode, ErrorCode::Success); } // Tests that we have enough bytes for the report before we run the handler. @@ -53,9 +55,10 @@ void Wiimote::InvokeHandler(H&& handler, const WiimoteCommon::OutputReportGeneri if (size < sizeof(T)) { ERROR_LOG(WIIMOTE, "InvokeHandler: report: 0x%02x invalid size: %d", int(rpt.rpt_id), size); + return; } - (this->*handler)(*reinterpret_cast(rpt.data)); + (this->*handler)(Common::BitCastPtr(rpt.data)); } // Here we process the Output Reports that the Wii sends. Our response will be @@ -77,7 +80,7 @@ void Wiimote::HIDOutputReport(const void* data, u32 size) } auto& rpt = *static_cast(data); - const int rpt_size = size - rpt.HEADER_SIZE; + const int rpt_size = size - OutputReportGeneric::HEADER_SIZE; DEBUG_LOG(WIIMOTE, "HIDOutputReport (page: %i, cid: 0x%02x, wm: 0x%02x)", m_index, m_reporting_channel, int(rpt.rpt_id)); @@ -88,37 +91,37 @@ void Wiimote::HIDOutputReport(const void* data, u32 size) switch (rpt.rpt_id) { - case OutputReportID::RUMBLE: + case OutputReportID::Rumble: // This is handled above. break; - case OutputReportID::LEDS: + case OutputReportID::LED: InvokeHandler(&Wiimote::HandleReportLeds, rpt, rpt_size); break; - case OutputReportID::REPORT_MODE: + case OutputReportID::ReportMode: InvokeHandler(&Wiimote::HandleReportMode, rpt, rpt_size); break; - case OutputReportID::IR_PIXEL_CLOCK: + case OutputReportID::IRPixelClock: InvokeHandler(&Wiimote::HandleIRPixelClock, rpt, rpt_size); break; - case OutputReportID::SPEAKER_ENABLE: + case OutputReportID::SpeakerEnable: InvokeHandler(&Wiimote::HandleSpeakerEnable, rpt, rpt_size); break; - case OutputReportID::REQUEST_STATUS: + case OutputReportID::RequestStatus: InvokeHandler(&Wiimote::HandleRequestStatus, rpt, rpt_size); break; - case OutputReportID::WRITE_DATA: + case OutputReportID::WriteData: InvokeHandler(&Wiimote::HandleWriteData, rpt, rpt_size); break; - case OutputReportID::READ_DATA: + case OutputReportID::ReadData: InvokeHandler(&Wiimote::HandleReadData, rpt, rpt_size); break; - case OutputReportID::SPEAKER_DATA: + case OutputReportID::SpeakerData: InvokeHandler(&Wiimote::HandleSpeakerData, rpt, rpt_size); break; - case OutputReportID::SPEAKER_MUTE: + case OutputReportID::SpeakerMute: InvokeHandler(&Wiimote::HandleSpeakerMute, rpt, rpt_size); break; - case OutputReportID::IR_LOGIC: + case OutputReportID::IRLogic: InvokeHandler(&Wiimote::HandleIRLogic, rpt, rpt_size); break; default: @@ -134,7 +137,7 @@ void Wiimote::CallbackInterruptChannel(const u8* data, u32 size) void Wiimote::SendAck(OutputReportID rpt_id, ErrorCode error_code) { - TypedHIDInputData rpt(InputReportID::ACK); + TypedHIDInputData rpt(InputReportID::Ack); auto& ack = rpt.data; ack.buttons = m_status.buttons; @@ -229,7 +232,7 @@ void Wiimote::HandleRequestStatus(const OutputReportRequestStatus&) // Max battery level seems to be 0xc8 (decimal 200) constexpr u8 MAX_BATTERY_LEVEL = 0xc8; - m_status.battery = (u8)(m_battery_setting->GetValue() * MAX_BATTERY_LEVEL); + m_status.battery = u8(std::lround(m_battery_setting->GetValue() * MAX_BATTERY_LEVEL)); if (Core::WantsDeterminism()) { @@ -240,7 +243,7 @@ void Wiimote::HandleRequestStatus(const OutputReportRequestStatus&) // Less than 0x20 triggers the low-battery flag: m_status.battery_low = m_status.battery < 0x20; - TypedHIDInputData rpt(InputReportID::STATUS); + TypedHIDInputData rpt(InputReportID::Status); rpt.data = m_status; CallbackInterruptChannel(rpt.GetData(), rpt.GetSize()); } @@ -261,7 +264,7 @@ void Wiimote::HandleWriteData(const OutputReportWriteData& wd) return; } - ErrorCode error_code = ErrorCode::SUCCESS; + ErrorCode error_code = ErrorCode::Success; switch (static_cast(wd.space)) { @@ -270,7 +273,7 @@ void Wiimote::HandleWriteData(const OutputReportWriteData& wd) if (address + wd.size > EEPROM_FREE_SIZE) { WARN_LOG(WIIMOTE, "WriteData: address + size out of bounds!"); - error_code = ErrorCode::INVALID_ADDRESS; + error_code = ErrorCode::InvalidAddress; } else { @@ -279,7 +282,8 @@ void Wiimote::HandleWriteData(const OutputReportWriteData& wd) // Write mii data to file if (address >= 0x0FCA && address < 0x12C0) { - // TODO: Only write parts of the Mii block + // TODO: Only write parts of the Mii block. + // TODO: Use fifferent files for different wiimote numbers. std::ofstream file; File::OpenFStream(file, File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/mii.bin", std::ios::binary | std::ios::out); @@ -290,14 +294,14 @@ void Wiimote::HandleWriteData(const OutputReportWriteData& wd) } break; - case AddressSpace::I2C_BUS: - case AddressSpace::I2C_BUS_ALT: + case AddressSpace::I2CBus: + case AddressSpace::I2CBusAlt: { // Attempting to access the EEPROM directly over i2c results in error 8. if (EEPROM_I2C_ADDR == m_read_request.slave_address) { WARN_LOG(WIIMOTE, "Attempt to write EEPROM directly."); - error_code = ErrorCode::INVALID_ADDRESS; + error_code = ErrorCode::InvalidAddress; break; } @@ -306,7 +310,7 @@ void Wiimote::HandleWriteData(const OutputReportWriteData& wd) if (bytes_written != wd.size) { // A real wiimote gives error 7 for failed write to i2c bus (mainly a non-existant slave) - error_code = ErrorCode::NACK; + error_code = ErrorCode::Nack; } } break; @@ -314,11 +318,11 @@ void Wiimote::HandleWriteData(const OutputReportWriteData& wd) default: WARN_LOG(WIIMOTE, "WriteData: invalid address space: 0x%x", wd.space); // A real wiimote gives error 6: - error_code = ErrorCode::INVALID_SPACE; + error_code = ErrorCode::InvalidSpace; break; } - SendAck(OutputReportID::WRITE_DATA, error_code); + SendAck(OutputReportID::WriteData, error_code); } void Wiimote::HandleReportRumble(const WiimoteCommon::OutputReportRumble& rpt) @@ -333,7 +337,7 @@ void Wiimote::HandleReportLeds(const WiimoteCommon::OutputReportLeds& rpt) m_status.leds = rpt.leds; if (rpt.ack) - SendAck(OutputReportID::LEDS, ErrorCode::SUCCESS); + SendAck(OutputReportID::LED, ErrorCode::Success); } void Wiimote::HandleIRPixelClock(const WiimoteCommon::OutputReportEnableFeature& rpt) @@ -343,7 +347,7 @@ void Wiimote::HandleIRPixelClock(const WiimoteCommon::OutputReportEnableFeature& // FYI: Camera data is currently always updated. Ignoring pixel clock status. if (rpt.ack) - SendAck(OutputReportID::IR_PIXEL_CLOCK, ErrorCode::SUCCESS); + SendAck(OutputReportID::IRPixelClock, ErrorCode::Success); } void Wiimote::HandleIRLogic(const WiimoteCommon::OutputReportEnableFeature& rpt) @@ -353,7 +357,7 @@ void Wiimote::HandleIRLogic(const WiimoteCommon::OutputReportEnableFeature& rpt) m_status.ir = rpt.enable; if (rpt.ack) - SendAck(OutputReportID::IR_LOGIC, ErrorCode::SUCCESS); + SendAck(OutputReportID::IRLogic, ErrorCode::Success); } void Wiimote::HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature& rpt) @@ -361,7 +365,7 @@ void Wiimote::HandleSpeakerMute(const WiimoteCommon::OutputReportEnableFeature& m_speaker_mute = rpt.enable; if (rpt.ack) - SendAck(OutputReportID::SPEAKER_MUTE, ErrorCode::SUCCESS); + SendAck(OutputReportID::SpeakerMute, ErrorCode::Success); } void Wiimote::HandleSpeakerEnable(const WiimoteCommon::OutputReportEnableFeature& rpt) @@ -370,7 +374,7 @@ void Wiimote::HandleSpeakerEnable(const WiimoteCommon::OutputReportEnableFeature m_status.speaker = rpt.enable; if (rpt.ack) - SendAck(OutputReportID::SPEAKER_ENABLE, ErrorCode::SUCCESS); + SendAck(OutputReportID::SpeakerEnable, ErrorCode::Success); } void Wiimote::HandleSpeakerData(const WiimoteCommon::OutputReportSpeakerData& rpt) @@ -437,7 +441,7 @@ bool Wiimote::ProcessReadDataRequest() return false; } - TypedHIDInputData rpt(InputReportID::READ_DATA_REPLY); + TypedHIDInputData rpt(InputReportID::ReadDataReply); auto& reply = rpt.data; reply.buttons = m_status.buttons; @@ -446,7 +450,7 @@ bool Wiimote::ProcessReadDataRequest() // Pre-fill with zeros in case of read-error or read < 16-bytes: std::fill(std::begin(reply.data), std::end(reply.data), 0x00); - ErrorCode error_code = ErrorCode::SUCCESS; + ErrorCode error_code = ErrorCode::Success; switch (m_read_request.space) { @@ -460,7 +464,7 @@ bool Wiimote::ProcessReadDataRequest() // The real Wiimote generate an error for the first // request to 0x1770 if we dont't replicate that the game will never // read the calibration data at the beginning of Eeprom. - error_code = ErrorCode::INVALID_ADDRESS; + error_code = ErrorCode::InvalidAddress; } else { @@ -483,14 +487,14 @@ bool Wiimote::ProcessReadDataRequest() } break; - case AddressSpace::I2C_BUS: - case AddressSpace::I2C_BUS_ALT: + case AddressSpace::I2CBus: + case AddressSpace::I2CBusAlt: { // Attempting to access the EEPROM directly over i2c results in error 8. if (EEPROM_I2C_ADDR == m_read_request.slave_address) { WARN_LOG(WIIMOTE, "Attempt to read EEPROM directly."); - error_code = ErrorCode::INVALID_ADDRESS; + error_code = ErrorCode::InvalidAddress; break; } @@ -502,7 +506,7 @@ bool Wiimote::ProcessReadDataRequest() { DEBUG_LOG(WIIMOTE, "Responding with read error 7 @ 0x%x @ 0x%x (%d)", m_read_request.slave_address, m_read_request.address, m_read_request.size); - error_code = ErrorCode::NACK; + error_code = ErrorCode::Nack; break; } @@ -513,11 +517,11 @@ bool Wiimote::ProcessReadDataRequest() default: WARN_LOG(WIIMOTE, "ReadData: invalid address space: 0x%x", int(m_read_request.space)); // A real wiimote gives error 6: - error_code = ErrorCode::INVALID_SPACE; + error_code = ErrorCode::InvalidSpace; break; } - if (ErrorCode::SUCCESS != error_code) + if (ErrorCode::Success != error_code) { // Stop processing request on read error: m_read_request.size = 0; diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp index aa6b7e9ccd..9d1e656b85 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp @@ -7,6 +7,7 @@ #include #include +#include "Common/BitUtils.h" #include "Common/Common.h" #include "Common/CommonTypes.h" #include "Core/HW/WiimoteEmu/WiimoteEmu.h" @@ -108,8 +109,7 @@ Classic::Classic() : EncryptedExtension(_trans("Classic")) void Classic::Update() { - auto& classic_data = *reinterpret_cast(&m_reg.controller_data); - classic_data = {}; + DataFormat classic_data = {}; // left stick { @@ -156,6 +156,8 @@ void Classic::Update() // flip button bits classic_data.bt.hex ^= 0xFFFF; + + Common::BitCastPtr(&m_reg.controller_data) = classic_data; } bool Classic::IsButtonPressed() const diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp index 976038711c..1e98529209 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp @@ -8,6 +8,7 @@ #include #include +#include "Common/BitUtils.h" #include "Common/Common.h" #include "Common/CommonTypes.h" #include "Core/HW/WiimoteEmu/WiimoteEmu.h" @@ -66,8 +67,7 @@ Drums::Drums() : EncryptedExtension(_trans("Drums")) void Drums::Update() { - auto& drum_data = reinterpret_cast(m_reg.controller_data); - drum_data = {}; + DataFormat drum_data = {}; // stick { @@ -92,6 +92,8 @@ void Drums::Update() // flip button bits drum_data.bt ^= 0xFFFF; + + Common::BitCastPtr(&m_reg.controller_data) = drum_data; } bool Drums::IsButtonPressed() const diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h index b32b1367bd..6392d9d542 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h @@ -20,7 +20,7 @@ namespace WiimoteEmu class Extension : public ControllerEmu::EmulatedController, public I2CSlave { public: - Extension(const char* name); + explicit Extension(const char* name); std::string GetName() const override; diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp index b3d5ab286b..68454928ce 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp @@ -9,6 +9,7 @@ #include #include +#include "Common/BitUtils.h" #include "Common/Common.h" #include "Common/CommonTypes.h" #include "Core/HW/WiimoteEmu/WiimoteEmu.h" @@ -98,8 +99,7 @@ Guitar::Guitar() : EncryptedExtension(_trans("Guitar")) void Guitar::Update() { - auto& guitar_data = reinterpret_cast(m_reg.controller_data); - guitar_data = {}; + DataFormat guitar_data = {}; // stick { @@ -137,6 +137,8 @@ void Guitar::Update() // flip button bits guitar_data.bt ^= 0xFFFF; + + Common::BitCastPtr(&m_reg.controller_data) = guitar_data; } bool Guitar::IsButtonPressed() const diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp index 91dd8bf176..184ed25fc7 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp @@ -8,6 +8,7 @@ #include #include +#include "Common/BitUtils.h" #include "Common/Common.h" #include "Common/CommonTypes.h" #include "Common/MathUtil.h" @@ -72,8 +73,7 @@ Nunchuk::Nunchuk() : EncryptedExtension(_trans("Nunchuk")) void Nunchuk::Update() { - auto& nc_data = *reinterpret_cast(&m_reg.controller_data); - nc_data = {}; + DataFormat nc_data = {}; // stick const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState(); @@ -129,6 +129,8 @@ void Nunchuk::Update() nc_data.bt.acc_x_lsb = acc.x & 0x3; nc_data.bt.acc_y_lsb = acc.y & 0x3; nc_data.bt.acc_z_lsb = acc.z & 0x3; + + Common::BitCastPtr(&m_reg.controller_data) = nc_data; } bool Nunchuk::IsButtonPressed() const diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp index 2871a73339..30964b91e9 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp @@ -8,6 +8,7 @@ #include #include +#include "Common/BitUtils.h" #include "Common/Common.h" #include "Common/CommonTypes.h" #include "Core/HW/WiimoteEmu/WiimoteEmu.h" @@ -84,8 +85,7 @@ Turntable::Turntable() : EncryptedExtension(_trans("Turntable")) void Turntable::Update() { - auto& tt_data = reinterpret_cast(m_reg.controller_data); - tt_data = {}; + DataFormat tt_data = {}; // stick { @@ -137,6 +137,8 @@ void Turntable::Update() // flip button bits :/ tt_data.bt ^= (BUTTON_L_GREEN | BUTTON_L_RED | BUTTON_L_BLUE | BUTTON_R_GREEN | BUTTON_R_RED | BUTTON_R_BLUE | BUTTON_MINUS | BUTTON_PLUS | BUTTON_EUPHORIA); + + Common::BitCastPtr(&m_reg.controller_data) = tt_data; } bool Turntable::IsButtonPressed() const diff --git a/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h index 13dcc0025d..42640b6edb 100644 --- a/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h +++ b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h @@ -30,7 +30,7 @@ public: static constexpr u8 REPORT_I2C_SLAVE = 0x52; static constexpr u8 REPORT_I2C_ADDR = 0x00; - ExtensionPort(I2CBus* i2c_bus); + explicit ExtensionPort(I2CBus* i2c_bus); bool IsDeviceConnected() const; void AttachExtension(Extension* dev); diff --git a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp index 6c3787a83e..faa4a90ff2 100644 --- a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp @@ -25,7 +25,7 @@ void MotionPlus::Reset() std::copy(std::begin(initial_id), std::end(initial_id), reg_data.ext_identifier); // TODO: determine meaning of calibration data: - static const u8 cdata[32] = { + constexpr std::array cdata = { 0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, 0xcf, 0xf0, 0x31, 0xad, 0xc8, 0x0b, 0x5e, 0x39, 0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60, 0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d, @@ -34,7 +34,7 @@ void MotionPlus::Reset() std::copy(std::begin(cdata), std::end(cdata), reg_data.calibration_data); // TODO: determine the meaning behind this: - static const u8 cert[64] = { + constexpr std::array cert = { 0x99, 0x1a, 0x07, 0x1b, 0x97, 0xf1, 0x11, 0x78, 0x0c, 0x42, 0x2b, 0x68, 0xdf, 0x44, 0x38, 0x0d, 0x2b, 0x7e, 0xd6, 0x84, 0x84, 0x58, 0x65, 0xc9, 0xf2, 0x95, 0xd9, 0xaf, 0xb6, 0xc4, 0x87, 0xd5, 0x18, 0xdb, 0x67, 0x3a, 0xc0, 0x71, 0xec, @@ -52,7 +52,7 @@ void MotionPlus::DoState(PointerWrap& p) bool MotionPlus::IsActive() const { - return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; + return (ACTIVE_DEVICE_ADDR << 1) == reg_data.ext_identifier[2]; } MotionPlus::PassthroughMode MotionPlus::GetPassthroughMode() const @@ -69,7 +69,8 @@ int MotionPlus::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) { if (IsActive()) { - // Motion plus does not respond to 0x53 when activated + // FYI: Motion plus does not respond to 0x53 when activated + if (ACTIVE_DEVICE_ADDR == slave_addr) return RawRead(®_data, addr, count, data_out); else @@ -78,7 +79,9 @@ int MotionPlus::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) else { if (INACTIVE_DEVICE_ADDR == slave_addr) + { return RawRead(®_data, addr, count, data_out); + } else { // Passthrough to the connected extension (if any) @@ -200,7 +203,7 @@ void MotionPlus::Update() // It even works when removing the is_mp_data bit in the last byte // My M+ non-inside gives: 61,46,45,aa,0,2 or b6,46,45,9a,0,2 // static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc | 0x01, 0x02}; - static const u8 init_data[6] = {0x81, 0x46, 0x46, 0xb6, 0x01, 0x02}; + constexpr std::array init_data = {0x81, 0x46, 0x46, 0xb6, 0x01, 0x02}; std::copy(std::begin(init_data), std::end(init_data), data); reg_data.cert_ready = 0x2; return; @@ -208,7 +211,7 @@ void MotionPlus::Update() if (0x2 == reg_data.cert_ready) { - static const u8 init_data[6] = {0x7f, 0xcf, 0xdf, 0x8b, 0x4f, 0x82}; + constexpr std::array init_data = {0x7f, 0xcf, 0xdf, 0x8b, 0x4f, 0x82}; std::copy(std::begin(init_data), std::end(init_data), data); reg_data.cert_ready = 0x8; return; @@ -223,7 +226,7 @@ void MotionPlus::Update() if (0x18 == reg_data.cert_ready) { // TODO: determine the meaning of this - const u8 mp_cert2[64] = { + constexpr std::array mp_cert2 = { 0xa5, 0x84, 0x1f, 0xd6, 0xbd, 0xdc, 0x7a, 0x4c, 0xf3, 0xc0, 0x24, 0xe0, 0x92, 0xef, 0x19, 0x28, 0x65, 0xe0, 0x62, 0x7c, 0x9b, 0x41, 0x6f, 0x12, 0xc3, 0xac, 0x78, 0xe4, 0xfc, 0x6b, 0x7b, 0x0a, 0xb4, 0x50, 0xd6, 0xf2, 0x45, 0xf7, 0x93, @@ -267,7 +270,7 @@ void MotionPlus::Update() constexpr u8 EXT_ADDR = ExtensionPort::REPORT_I2C_ADDR; // Try to alternate between M+ and EXT data: - auto& mplus_data = *reinterpret_cast(data); + DataFormat mplus_data = Common::BitCastPtr(data); mplus_data.is_mp_data ^= true; // hax!!! @@ -282,13 +285,13 @@ void MotionPlus::Update() { switch (GetPassthroughMode()) { - case PassthroughMode::DISABLED: + case PassthroughMode::Disabled: { // Passthrough disabled, always send M+ data: mplus_data.is_mp_data = true; break; } - case PassthroughMode::NUNCHUK: + case PassthroughMode::Nunchuk: { if (EXT_AMT == i2c_bus.BusRead(EXT_SLAVE, EXT_ADDR, EXT_AMT, data)) { @@ -315,7 +318,7 @@ void MotionPlus::Update() } break; } - case PassthroughMode::CLASSIC: + case PassthroughMode::Classic: { if (EXT_AMT == i2c_bus.BusRead(EXT_SLAVE, EXT_ADDR, EXT_AMT, data)) { @@ -371,6 +374,8 @@ void MotionPlus::Update() mplus_data.extension_connected = m_extension_port.IsDeviceConnected(); mplus_data.zero = 0; + + Common::BitCastPtr(data) = mplus_data; } } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h index 211e666153..06bc21b441 100644 --- a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h +++ b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h @@ -96,9 +96,9 @@ private: enum class PassthroughMode : u8 { - DISABLED = 0x04, - NUNCHUK = 0x05, - CLASSIC = 0x07, + Disabled = 0x04, + Nunchuk = 0x05, + Classic = 0x07, }; bool IsActive() const; diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index cd2b10347c..b99b577265 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -71,7 +71,7 @@ void Wiimote::Reset() // Wiimote starts in non-continuous CORE mode: m_reporting_channel = 0; - m_reporting_mode = InputReportID::REPORT_CORE; + m_reporting_mode = InputReportID::ReportCore; m_reporting_continuous = false; m_speaker_mute = false; @@ -325,7 +325,7 @@ bool Wiimote::ProcessExtensionPortEvent() return false; // FYI: This happens even during a read request which continues after the status report is sent. - m_reporting_mode = InputReportID::REPORT_DISABLED; + m_reporting_mode = InputReportID::ReportDisabled; DEBUG_LOG(WIIMOTE, "Sending status report due to extension status change."); @@ -382,14 +382,14 @@ void Wiimote::SendDataReport() { Movie::SetPolledDevice(); - if (InputReportID::REPORT_DISABLED == m_reporting_mode) + if (InputReportID::ReportDisabled == m_reporting_mode) { // The wiimote is in this disabled after an extension change. // Input reports are not sent, even on button change. return; } - if (InputReportID::REPORT_CORE == m_reporting_mode && !m_reporting_continuous) + if (InputReportID::ReportCore == m_reporting_mode && !m_reporting_continuous) { // TODO: we only need to send a report if the data changed when m_reporting_continuous is // disabled. It's probably only sensible to check this with REPORT_CORE @@ -497,10 +497,10 @@ void Wiimote::SendDataReport() CallbackInterruptChannel(rpt_builder.GetDataPtr(), rpt_builder.GetDataSize()); // The interleaved reporting modes toggle back and forth: - if (InputReportID::REPORT_INTERLEAVE1 == m_reporting_mode) - m_reporting_mode = InputReportID::REPORT_INTERLEAVE2; - else if (InputReportID::REPORT_INTERLEAVE2 == m_reporting_mode) - m_reporting_mode = InputReportID::REPORT_INTERLEAVE1; + if (InputReportID::ReportInterleave1 == m_reporting_mode) + m_reporting_mode = InputReportID::ReportInterleave2; + else if (InputReportID::ReportInterleave2 == m_reporting_mode) + m_reporting_mode = InputReportID::ReportInterleave1; } void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size) @@ -550,7 +550,7 @@ void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size) { // AyuanX: My experiment shows Control Channel is never used // shuffle2: but lwbt uses this, so we'll do what we must :) - HIDOutputReport(hidp.data, size - hidp.HEADER_SIZE); + HIDOutputReport(hidp.data, size - HIDPacket::HEADER_SIZE); // TODO: Should this be above the previous? u8 handshake = HID_HANDSHAKE_SUCCESS; @@ -594,7 +594,7 @@ void Wiimote::InterruptChannel(const u16 channel_id, const void* data, u32 size) switch (hidp.param) { case HID_PARAM_OUTPUT: - HIDOutputReport(hidp.data, size - hidp.HEADER_SIZE); + HIDOutputReport(hidp.data, size - HIDPacket::HEADER_SIZE); break; default: diff --git a/Source/Core/Core/HW/WiimoteReal/IOWin.cpp b/Source/Core/Core/HW/WiimoteReal/IOWin.cpp index a5f9b569ad..8f6c69c448 100644 --- a/Source/Core/Core/HW/WiimoteReal/IOWin.cpp +++ b/Source/Core/Core/HW/WiimoteReal/IOWin.cpp @@ -449,7 +449,7 @@ bool IsWiimote(const std::basic_string& 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, u8(OutputReportID::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) @@ -460,7 +460,7 @@ bool IsWiimote(const std::basic_string& device_path, WinWriteMethod& meth switch (InputReportID(buf[1])) { - case InputReportID::STATUS: + case InputReportID::Status: return true; default: WARN_LOG(WIIMOTE, "IsWiimote(): Received unexpected report %02x", buf[1]); @@ -703,11 +703,11 @@ size_t GetReportSize(u8 rid) switch (report_id) { - case InputReportID::STATUS: + case InputReportID::Status: return sizeof(InputReportStatus); - case InputReportID::READ_DATA_REPLY: + case InputReportID::ReadDataReply: return sizeof(InputReportReadDataReply); - case InputReportID::ACK: + case InputReportID::Ack: return sizeof(InputReportAck); default: if (DataReportBuilder::IsValidMode(report_id)) diff --git a/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp b/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp index 2d5a89672c..e2abdafa80 100644 --- a/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp +++ b/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp @@ -27,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, u8(OutputReportID::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) diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp index e930a2b547..b7dab2fefe 100644 --- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp +++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp @@ -71,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] == u8(OutputReportID::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; @@ -99,10 +99,10 @@ void Wiimote::DisableDataReporting() // This accomplishes very little: OutputReportMode rpt = {}; - rpt.mode = InputReportID::REPORT_CORE; + rpt.mode = InputReportID::ReportCore; rpt.continuous = 0; rpt.rumble = 0; - QueueReport(u8(OutputReportID::REPORT_MODE), &rpt, sizeof(rpt)); + QueueReport(u8(OutputReportID::ReportMode), &rpt, sizeof(rpt)); } void Wiimote::EnableDataReporting(u8 mode) @@ -112,7 +112,7 @@ void Wiimote::EnableDataReporting(u8 mode) OutputReportMode rpt = {}; rpt.mode = InputReportID(mode); rpt.continuous = 1; - QueueReport(u8(OutputReportID::REPORT_MODE), &rpt, sizeof(rpt)); + QueueReport(u8(OutputReportID::ReportMode), &rpt, sizeof(rpt)); } void Wiimote::SetChannel(u16 channel) @@ -177,7 +177,7 @@ 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] == u8(OutputReportID::LEDS)) + if (rpt[1] == u8(OutputReportID::LED)) { auto& leds_rpt = *reinterpret_cast(&rpt[2]); if (0 == leds_rpt.leds) @@ -186,12 +186,12 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const leds_rpt.leds = 0xf; } } - else if (rpt[1] == u8(OutputReportID::SPEAKER_DATA) && + 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] = u8(OutputReportID::RUMBLE); + rpt[1] = u8(OutputReportID::Rumble); // Keep only the rumble bit. rpt[2] &= 0x1; rpt.resize(3); @@ -255,23 +255,11 @@ bool Wiimote::IsBalanceBoard() 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, - u8(OutputReportID::WRITE_DATA), - 0x04, - 0xa4, - 0x00, - 0xf0, - 0x01, - 0x55}; - static const u8 init_extension_rpt2[MAX_PAYLOAD] = {WR_SET_REPORT | BT_OUTPUT, - u8(OutputReportID::WRITE_DATA), - 0x04, - 0xa4, - 0x00, - 0xfb, - 0x01, - 0x00}; - static const u8 status_report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::REQUEST_STATUS), + static const u8 init_extension_rpt1[MAX_PAYLOAD] = { + 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, 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))) @@ -290,7 +278,7 @@ bool Wiimote::IsBalanceBoard() switch (InputReportID(buf[1])) { - case InputReportID::STATUS: + case InputReportID::Status: { const auto* status = reinterpret_cast(&buf[2]); // A Balance Board has a Balance Board extension. @@ -298,7 +286,7 @@ bool Wiimote::IsBalanceBoard() return false; // Read two bytes from 0xa400fe to identify the extension. static const u8 identify_ext_rpt[] = {WR_SET_REPORT | BT_OUTPUT, - u8(OutputReportID::READ_DATA), + u8(OutputReportID::ReadData), 0x04, 0xa4, 0x00, @@ -308,7 +296,7 @@ bool Wiimote::IsBalanceBoard() ret = IOWrite(identify_ext_rpt, sizeof(identify_ext_rpt)); break; } - case InputReportID::READ_DATA_REPLY: + case InputReportID::ReadDataReply: { const auto* reply = reinterpret_cast(&buf[2]); if (Common::swap16(reply->address) != 0x00fe) @@ -320,10 +308,10 @@ bool Wiimote::IsBalanceBoard() // A Balance Board ext can be identified by checking for 0x0402. return reply->data[0] == 0x04 && reply->data[1] == 0x02; } - case InputReportID::ACK: + case InputReportID::Ack: { const auto* ack = reinterpret_cast(&buf[2]); - if (ack->rpt_id == OutputReportID::READ_DATA && ack->error_code != ErrorCode::SUCCESS) + 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; @@ -338,7 +326,7 @@ bool Wiimote::IsBalanceBoard() static bool IsDataReport(const Report& rpt) { - return rpt.size() >= 2 && rpt[1] >= u8(InputReportID::REPORT_CORE); + return rpt.size() >= 2 && rpt[1] >= u8(InputReportID::ReportCore); } // Returns the next report that should be sent @@ -387,30 +375,17 @@ void Wiimote::Update() bool Wiimote::CheckForButtonPress() { - const Report& rpt = ProcessReadQueue(); + Report& rpt = ProcessReadQueue(); if (rpt.size() >= 4) { - switch (InputReportID(rpt[1])) + const auto mode = InputReportID(rpt[1]); + if (DataReportBuilder::IsValidMode(mode)) { - case InputReportID::REPORT_CORE: - case InputReportID::REPORT_CORE_ACCEL: - case InputReportID::REPORT_CORE_EXT8: - case InputReportID::REPORT_CORE_ACCEL_IR12: - case InputReportID::REPORT_CORE_EXT19: - case InputReportID::REPORT_CORE_ACCEL_EXT16: - case InputReportID::REPORT_CORE_IR10_EXT9: - case InputReportID::REPORT_CORE_ACCEL_IR10_EXT6: - case InputReportID::REPORT_INTERLEAVE1: - case InputReportID::REPORT_INTERLEAVE2: - // check any button without checking accelerometer data - // TODO: use the structs! - 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; @@ -426,19 +401,19 @@ bool Wiimote::PrepareOnThread() { // core buttons, no continuous reporting // TODO: use the structs.. - u8 static const mode_report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::REPORT_MODE), 0, - u8(InputReportID::REPORT_CORE)}; + 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, u8(OutputReportID::LEDS), 0}; + 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, u8(OutputReportID::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, - u8(OutputReportID::REQUEST_STATUS), 0}; + u8(OutputReportID::RequestStatus), 0}; // TODO: check for sane response? return (IOWrite(mode_report, sizeof(mode_report)) && IOWrite(led_report, sizeof(led_report)) && @@ -473,7 +448,7 @@ void Wiimote::EmuResume() OutputReportMode rpt = {}; rpt.mode = wm->m_reporting_mode; rpt.continuous = 1; - QueueReport(u8(OutputReportID::REPORT_MODE), &rpt, sizeof(rpt)); + QueueReport(u8(OutputReportID::ReportMode), &rpt, sizeof(rpt)); NOTICE_LOG(WIIMOTE, "Resuming Wiimote data reporting."); @@ -485,9 +460,9 @@ void Wiimote::EmuPause() m_last_input_report.clear(); OutputReportMode rpt = {}; - rpt.mode = InputReportID::REPORT_CORE; + rpt.mode = InputReportID::ReportCore; rpt.continuous = 0; - QueueReport(u8(OutputReportID::REPORT_MODE), &rpt, sizeof(rpt)); + QueueReport(u8(OutputReportID::ReportMode), &rpt, sizeof(rpt)); NOTICE_LOG(WIIMOTE, "Pausing Wiimote data reporting."); diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h index 15c955986f..01e1001f57 100644 --- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h +++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h @@ -25,7 +25,7 @@ namespace WiimoteReal { using WiimoteCommon::MAX_PAYLOAD; -typedef std::vector Report; +using Report = std::vector; constexpr u32 WIIMOTE_DEFAULT_TIMEOUT = 1000; diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index ae12975895..93cc595eb6 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -51,6 +51,7 @@ #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" @@ -706,7 +707,7 @@ static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt, } // Nunchuk - if (rpt.HasExt() && ext == 1) + if (rpt.HasExt() && ext == ExtensionNumber::NUNCHUK) { const u8* const extData = rpt.GetExtDataPtr(); @@ -728,7 +729,7 @@ static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt, } // Classic controller - if (rpt.HasExt() && ext == 2) + if (rpt.HasExt() && ext == ExtensionNumber::CLASSIC) { const u8* const extData = rpt.GetExtDataPtr(); diff --git a/Source/Core/Core/Movie.h b/Source/Core/Core/Movie.h index aac76fdd84..02acfa6661 100644 --- a/Source/Core/Core/Movie.h +++ b/Source/Core/Core/Movie.h @@ -10,14 +10,22 @@ #include #include "Common/CommonTypes.h" -#include "Core/HW/WiimoteCommon/DataReport.h" -#include "Core/HW/WiimoteEmu/Encryption.h" struct BootParameters; struct GCPadStatus; class PointerWrap; +namespace WiimoteCommon +{ +class DataReportBuilder; +} + +namespace WiimoteEmu +{ +class EncryptionKey; +} + // Per-(video )Movie actions namespace Movie diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 0acee31f3a..9daa698166 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -53,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" @@ -2191,11 +2191,10 @@ void SetupWiimotes() { if (wiimote_map[i] > 0) { - static_cast( - static_cast( - Wiimote::GetConfig()->GetController(static_cast(i))) - ->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Extension)) - ->switch_extension = netplay_settings.m_WiimoteExtension[i]; + static_cast( + static_cast(Wiimote::GetConfig()->GetController(int(i))) + ->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments)) + ->SetSelectedAttachment(netplay_settings.m_WiimoteExtension[i]); } } } diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 9a96346f0f..c12bafb5b2 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -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( - static_cast( - Wiimote::GetConfig()->GetController(static_cast(i))) - ->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Extension)) - ->switch_extension; + const int extension = + static_cast( + static_cast(Wiimote::GetConfig()->GetController(int(i))) + ->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments)) + ->GetSelectedAttachment(); spac << extension; } diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h index 746bc523ab..81e76eccfe 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h @@ -32,6 +32,6 @@ public: private: std::vector> m_attachments; - std::atomic m_selected_attachment; + std::atomic m_selected_attachment = {}; }; } // namespace ControllerEmu