WiimoteEmu: Major renaming and cleanup.
This commit is contained in:
parent
b1f350ab1c
commit
0d1fbe7bbc
|
@ -199,4 +199,14 @@ inline To BitCast(const From& source) noexcept
|
|||
std::memcpy(&storage, &source, sizeof(storage));
|
||||
return reinterpret_cast<To&>(storage);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -171,14 +171,20 @@
|
|||
<ClCompile Include="HW\SystemTimers.cpp" />
|
||||
<ClCompile Include="HW\VideoInterface.cpp" />
|
||||
<ClCompile Include="HW\Wiimote.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Attachment.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Classic.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Drums.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Guitar.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Nunchuk.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Turntable.cpp" />
|
||||
<ClCompile Include="HW\WiimoteCommon\DataReport.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Camera.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Dynamics.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\EmuSubroutines.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Encryption.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\ExtensionPort.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Classic.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Drums.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Extension.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Guitar.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Nunchuk.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Turntable.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\I2CBus.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\MotionPlus.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\Speaker.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\WiimoteEmu.cpp" />
|
||||
<ClCompile Include="HW\WiimoteReal\IOWin.cpp" />
|
||||
|
@ -430,17 +436,24 @@
|
|||
<ClInclude Include="HW\SystemTimers.h" />
|
||||
<ClInclude Include="HW\VideoInterface.h" />
|
||||
<ClInclude Include="HW\Wiimote.h" />
|
||||
<ClInclude Include="HW\WiimoteCommon\DataReport.h" />
|
||||
<ClInclude Include="HW\WiimoteCommon\WiimoteConstants.h" />
|
||||
<ClInclude Include="HW\WiimoteCommon\WiimoteHid.h" />
|
||||
<ClInclude Include="HW\WiimoteCommon\WiimoteReport.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Attachment.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Classic.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Drums.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Guitar.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Nunchuk.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Turntable.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Camera.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Dynamics.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Encryption.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\ExtensionPort.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Classic.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Drums.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Extension.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Guitar.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Nunchuk.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Turntable.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\I2CBus.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\MatrixMath.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\MotionPlus.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\Speaker.h" />
|
||||
<ClInclude Include="HW\WiimoteEmu\WiimoteEmu.h" />
|
||||
<ClInclude Include="HW\WiimoteReal\WiimoteReal.h" />
|
||||
<ClInclude Include="HW\WiimoteReal\WiimoteRealBase.h" />
|
||||
|
|
|
@ -79,9 +79,6 @@
|
|||
<Filter Include="HW %28Flipper/Hollywood%29\Wiimote\Real">
|
||||
<UniqueIdentifier>{bc3e845a-3d01-4713-aa32-f27110838d0c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment">
|
||||
<UniqueIdentifier>{cdbd65da-541f-47d2-8fdc-e99e73e98e69}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="HW %28Flipper/Hollywood%29\EXI - Expansion Interface">
|
||||
<UniqueIdentifier>{d19f1218-0e28-4f24-a4b3-33fac750a899}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
@ -163,9 +160,12 @@
|
|||
<Filter Include="PowerPC\SignatureDB">
|
||||
<UniqueIdentifier>{f0b52c84-49f4-470a-b037-edeea5634b9e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="HW %28Flipper/Hollywood%29\WiimoteCommon">
|
||||
<Filter Include="HW %28Flipper/Hollywood%29\Wiimote\Common">
|
||||
<UniqueIdentifier>{ee6645da-3ad9-4fe7-809f-e4646d0b0ca5}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension">
|
||||
<UniqueIdentifier>{68c09d7e-4f5a-435d-a0d2-7eb7a74d7054}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BootManager.cpp" />
|
||||
|
@ -516,33 +516,12 @@
|
|||
<ClCompile Include="HW\VideoInterface.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\VI - Video Interface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Attachment.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Classic.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Drums.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Guitar.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Nunchuk.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Attachment\Turntable.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\EmuSubroutines.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Encryption.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Speaker.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\WiimoteEmu.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClCompile>
|
||||
|
@ -901,6 +880,45 @@
|
|||
<ClCompile Include="PowerPC\Jit64\RegCache\FPURegCache.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\RegCache\GPRRegCache.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\RegCache\JitRegCache.cpp" />
|
||||
<ClCompile Include="HW\WiimoteEmu\I2CBus.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\MotionPlus.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Camera.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Speaker.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Dynamics.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Classic.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Drums.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Guitar.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Nunchuk.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Turntable.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\Extension\Extension.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteEmu\ExtensionPort.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WiimoteCommon\DataReport.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Common</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BootManager.h" />
|
||||
|
@ -1200,24 +1218,6 @@
|
|||
<ClInclude Include="HW\VideoInterface.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\VI - Video Interface</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Attachment.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Classic.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Drums.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Guitar.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Nunchuk.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Attachment\Turntable.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Attachment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Encryption.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1584,13 +1584,13 @@
|
|||
<Filter>IOS\Network\NCD</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteCommon\WiimoteConstants.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\WiimoteCommon</Filter>
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteCommon\WiimoteHid.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\WiimoteCommon</Filter>
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteCommon\WiimoteReport.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\WiimoteCommon</Filter>
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Config\UISettings.h">
|
||||
<Filter>Config</Filter>
|
||||
|
@ -1598,6 +1598,45 @@
|
|||
<ClInclude Include="Config\WiimoteInputSettings.h">
|
||||
<Filter>Config</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\I2CBus.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\MotionPlus.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Camera.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Speaker.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Dynamics.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Classic.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Drums.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Guitar.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Nunchuk.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Turntable.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\Extension\Extension.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteEmu\ExtensionPort.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WiimoteCommon\DataReport.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
|
|
@ -0,0 +1,425 @@
|
|||
// Copyright 2019 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#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<const CoreData*>(data_ptr);
|
||||
|
||||
// Remove accel LSBs.
|
||||
result->hex &= CoreData::BUTTON_MASK;
|
||||
}
|
||||
|
||||
void SetCoreData(const CoreData& new_core) override
|
||||
{
|
||||
auto& core = *reinterpret_cast<CoreData*>(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<AccelMSB*>(data_ptr + 2);
|
||||
result->x = accel.x << 2;
|
||||
result->y = accel.y << 2;
|
||||
result->z = accel.z << 2;
|
||||
|
||||
// LSBs
|
||||
const auto& core = *reinterpret_cast<CoreData*>(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<AccelMSB*>(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<CoreData*>(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 <u32 Offset, u32 Length>
|
||||
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 <u32 Offset, u32 Length, u32 DataOffset = 0>
|
||||
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<CoreData*>(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<CoreData*>(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<CoreData*>(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<CoreData*>(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<DataReportManipulator> MakeDataReportManipulator(InputReportID rpt_id, u8* data_ptr)
|
||||
{
|
||||
std::unique_ptr<DataReportManipulator> ptr;
|
||||
|
||||
switch (rpt_id)
|
||||
{
|
||||
case InputReportID::REPORT_CORE:
|
||||
// 0x30: Core Buttons
|
||||
ptr = std::make_unique<ReportCore>();
|
||||
break;
|
||||
case InputReportID::REPORT_CORE_ACCEL:
|
||||
// 0x31: Core Buttons and Accelerometer
|
||||
ptr = std::make_unique<ReportCoreAccel>();
|
||||
break;
|
||||
case InputReportID::REPORT_CORE_EXT8:
|
||||
// 0x32: Core Buttons with 8 Extension bytes
|
||||
ptr = std::make_unique<ReportCoreExt8>();
|
||||
break;
|
||||
case InputReportID::REPORT_CORE_ACCEL_IR12:
|
||||
// 0x33: Core Buttons and Accelerometer with 12 IR bytes
|
||||
ptr = std::make_unique<ReportCoreAccelIR12>();
|
||||
break;
|
||||
case InputReportID::REPORT_CORE_EXT19:
|
||||
// 0x34: Core Buttons with 19 Extension bytes
|
||||
ptr = std::make_unique<ReportCoreExt19>();
|
||||
break;
|
||||
case InputReportID::REPORT_CORE_ACCEL_EXT16:
|
||||
// 0x35: Core Buttons and Accelerometer with 16 Extension Bytes
|
||||
ptr = std::make_unique<ReportCoreAccelExt16>();
|
||||
break;
|
||||
case InputReportID::REPORT_CORE_IR10_EXT9:
|
||||
// 0x36: Core Buttons with 10 IR bytes and 9 Extension Bytes
|
||||
ptr = std::make_unique<ReportCoreIR10Ext9>();
|
||||
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<ReportCoreAccelIR10Ext6>();
|
||||
break;
|
||||
case InputReportID::REPORT_EXT21:
|
||||
// 0x3d: 21 Extension Bytes
|
||||
ptr = std::make_unique<ReportExt21>();
|
||||
break;
|
||||
case InputReportID::REPORT_INTERLEAVE1:
|
||||
// 0x3e - 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes
|
||||
ptr = std::make_unique<ReportInterleave1>();
|
||||
break;
|
||||
case InputReportID::REPORT_INTERLEAVE2:
|
||||
ptr = std::make_unique<ReportInterleave2>();
|
||||
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
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright 2019 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#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<DataReportManipulator> MakeDataReportManipulator(InputReportID rpt_id,
|
||||
u8* data_ptr);
|
||||
|
||||
class DataReportBuilder
|
||||
{
|
||||
public:
|
||||
DataReportBuilder(InputReportID rpt_id);
|
||||
|
||||
typedef ButtonData CoreData;
|
||||
typedef DataReportManipulator::AccelData AccelData;
|
||||
|
||||
typedef std::vector<u8> IRData;
|
||||
typedef std::vector<u8> 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<std::array<u8, MAX_DATA_SIZE>> m_data;
|
||||
|
||||
std::unique_ptr<DataReportManipulator> m_manip;
|
||||
};
|
||||
|
||||
} // namespace WiimoteCommon
|
|
@ -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
|
||||
|
|
|
@ -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 <typename T>
|
||||
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<T>::value);
|
||||
|
||||
const u8* GetData() const { return reinterpret_cast<const u8*>(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 <typename T>
|
||||
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<T>::value);
|
||||
|
||||
u8* GetData() { return reinterpret_cast<u8*>(this); }
|
||||
const u8* GetData() const { return reinterpret_cast<const u8*>(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
|
||||
|
|
|
@ -7,54 +7,124 @@
|
|||
#include <vector>
|
||||
|
||||
#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<u8> 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
|
||||
|
|
|
@ -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 <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#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<u8, 6> nothing_id{{0x00, 0x00, 0x00, 0x00, 0x2e, 0x2e}};
|
||||
// The id for a partially inserted extension (currently unused)
|
||||
DOLPHIN_UNUSED constexpr std::array<u8, 6> 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<WiimoteEmu::Attachment*>(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<WiimoteEmu::Attachment*>(attachments[active_extension].get())
|
||||
->IsButtonPressed();
|
||||
if (switch_extension > 0)
|
||||
return static_cast<WiimoteEmu::Attachment*>(attachments[switch_extension].get())
|
||||
->IsButtonPressed();
|
||||
return false;
|
||||
}
|
||||
} // namespace ControllerEmu
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright 2010 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#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<u8, 6> m_id{};
|
||||
std::array<u8, 0x10> m_calibration{};
|
||||
|
||||
private:
|
||||
const char* const m_name;
|
||||
ExtensionReg& m_reg;
|
||||
};
|
||||
|
||||
class None : public Attachment
|
||||
{
|
||||
public:
|
||||
explicit None(ExtensionReg& reg);
|
||||
};
|
||||
} // namespace WiimoteEmu
|
|
@ -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<Vertex, 4> 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<u16>(lround((v[i].x + 1) / 2 * (camWidth - 1)));
|
||||
y[i] = static_cast<u16>(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<IRBasic*>(data);
|
||||
for (unsigned int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (x[i * 2] < 1024 && y[i * 2] < 768)
|
||||
{
|
||||
irdata[i].x1 = static_cast<u8>(x[i * 2]);
|
||||
irdata[i].x1hi = x[i * 2] >> 8;
|
||||
|
||||
irdata[i].y1 = static_cast<u8>(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<u8>(x[i * 2 + 1]);
|
||||
irdata[i].x2hi = x[i * 2 + 1] >> 8;
|
||||
|
||||
irdata[i].y2 = static_cast<u8>(y[i * 2 + 1]);
|
||||
irdata[i].y2hi = y[i * 2 + 1] >> 8;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR_MODE_EXTENDED:
|
||||
{
|
||||
auto* const irdata = reinterpret_cast<IRExtended*>(data);
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
if (x[i] < 1024 && y[i] < 768)
|
||||
{
|
||||
irdata[i].x = static_cast<u8>(x[i]);
|
||||
irdata[i].xhi = x[i] >> 8;
|
||||
|
||||
irdata[i].y = static_cast<u8>(y[i]);
|
||||
irdata[i].yhi = y[i] >> 8;
|
||||
|
||||
irdata[i].size = 10;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR_MODE_FULL:
|
||||
{
|
||||
auto* const irdata = reinterpret_cast<IRFull*>(data);
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
if (x[i] < 1024 && y[i] < 768)
|
||||
{
|
||||
irdata[i].x = static_cast<u8>(x[i]);
|
||||
irdata[i].xhi = x[i] >> 8;
|
||||
|
||||
irdata[i].y = static_cast<u8>(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
|
|
@ -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
|
|
@ -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 <cmath>
|
||||
|
||||
#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<int, 3> 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<int, 3> 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<s8, 3> 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<int, 3> 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<s8, 3> 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<u16>(unclamped_x, 0, 0x3ff);
|
||||
result.y = MathUtil::Clamp<u16>(unclamped_y, 0, 0x3ff);
|
||||
result.z = MathUtil::Clamp<u16>(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
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2019 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#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<int, 3> timing; // Hold length in frames for each axis
|
||||
std::array<double, 3> intensity; // Swing or shake intensity
|
||||
std::array<int, 3> 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
|
|
@ -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 <fstream>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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 <typename T, typename H>
|
||||
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<const T*>(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<const OutputReportGeneric*>(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<OutputReportRumble>(&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<OutputReportLeds>(&Wiimote::HandleReportLeds, rpt, rpt_size);
|
||||
break;
|
||||
|
||||
case RT_REPORT_MODE:
|
||||
ReportMode(reinterpret_cast<const wm_report_mode*>(sr->data));
|
||||
case OutputReportID::REPORT_MODE:
|
||||
InvokeHandler<OutputReportMode>(&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<OutputReportEnableFeature>(&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<OutputReportEnableFeature>(&Wiimote::HandleSpeakerEnable, rpt, rpt_size);
|
||||
break;
|
||||
|
||||
case RT_REQUEST_STATUS:
|
||||
RequestStatus(reinterpret_cast<const wm_request_status*>(sr->data));
|
||||
// No ACK:
|
||||
return;
|
||||
case OutputReportID::REQUEST_STATUS:
|
||||
InvokeHandler<OutputReportRequestStatus>(&Wiimote::HandleRequestStatus, rpt, rpt_size);
|
||||
break;
|
||||
|
||||
case RT_WRITE_DATA:
|
||||
WriteData(reinterpret_cast<const wm_write_data*>(sr->data));
|
||||
// Sends it's own ACK conditionally:
|
||||
return;
|
||||
case OutputReportID::WRITE_DATA:
|
||||
InvokeHandler<OutputReportWriteData>(&Wiimote::HandleWriteData, rpt, rpt_size);
|
||||
break;
|
||||
|
||||
case RT_READ_DATA:
|
||||
ReadData(reinterpret_cast<const wm_read_data*>(sr->data));
|
||||
// No ACK:
|
||||
return;
|
||||
case OutputReportID::READ_DATA:
|
||||
InvokeHandler<OutputReportReadData>(&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<const wm_speaker_data*>(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<OutputReportSpeakerData>(&Wiimote::HandleSpeakerData, rpt, rpt_size);
|
||||
break;
|
||||
|
||||
case RT_SPEAKER_MUTE:
|
||||
m_speaker_mute = sr->enable;
|
||||
case OutputReportID::SPEAKER_MUTE:
|
||||
InvokeHandler<OutputReportEnableFeature>(&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<OutputReportEnableFeature>(&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<wm_acknowledge> 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<InputReportAck> rpt(InputReportID::ACK);
|
||||
auto& ack = rpt.data;
|
||||
|
||||
ack.buttons = m_status.buttons;
|
||||
ack.reportID = report_id;
|
||||
ack.errorID = static_cast<u8>(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<ExtensionNumber>(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<wm_status_report> rpt;
|
||||
rpt.type = HID_TYPE_DATA;
|
||||
rpt.param = HID_PARAM_INPUT;
|
||||
rpt.report_id = RT_STATUS_REPORT;
|
||||
|
||||
// status values
|
||||
TypedHIDInputData<InputReportStatus> 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<WiimoteAddressSpace>(wd->space))
|
||||
switch (static_cast<AddressSpace>(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<WiimoteAddressSpace>(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<AddressSpace>(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<wm_read_data_reply> rpt;
|
||||
rpt.type = HID_TYPE_DATA;
|
||||
rpt.param = HID_PARAM_INPUT;
|
||||
rpt.report_id = RT_READ_DATA_REPLY;
|
||||
TypedHIDInputData<InputReportReadDataReply> 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<u8>(error_code);
|
||||
reply.error = static_cast<u8>(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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <array>
|
||||
#include <cassert>
|
||||
|
@ -71,7 +71,7 @@ constexpr std::array<u16, 4> 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<DataFormat*>(&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<u8>(
|
||||
Classic::LEFT_STICK_CENTER_X + (left_stick_state.x * Classic::LEFT_STICK_RADIUS));
|
||||
classic_data.regular_data.ly = static_cast<u8>(
|
||||
Classic::LEFT_STICK_CENTER_Y + (left_stick_state.y * Classic::LEFT_STICK_RADIUS));
|
||||
classic_data.lx = static_cast<u8>(Classic::LEFT_STICK_CENTER_X +
|
||||
(left_stick_state.x * Classic::LEFT_STICK_RADIUS));
|
||||
classic_data.ly = static_cast<u8>(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)
|
|
@ -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);
|
||||
|
|
@ -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 <array>
|
||||
#include <cassert>
|
||||
|
@ -43,7 +43,7 @@ constexpr std::array<u16, 2> 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<DataFormat&>(m_reg.controller_data);
|
||||
drum_data = {};
|
||||
|
||||
// stick
|
||||
{
|
||||
|
@ -81,9 +77,12 @@ void Drums::GetState(u8* const data)
|
|||
drum_data.sy = static_cast<u8>((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)
|
|
@ -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
|
|
@ -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 <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#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
|
|
@ -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 <array>
|
||||
#include <string>
|
||||
|
||||
#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<u8, 0x10> 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<u8, 6> 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
|
|
@ -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 <array>
|
||||
#include <cassert>
|
||||
|
@ -61,7 +61,7 @@ constexpr std::array<u16, 2> 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<DataFormat&>(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)
|
|
@ -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
|
|
@ -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 <array>
|
||||
#include <cassert>
|
||||
|
@ -30,7 +30,7 @@ constexpr std::array<u8, 2> 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<DataFormat*>(&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<s16>(accel_x, 0, 0x3ff);
|
||||
accel_y = MathUtil::Clamp<s16>(accel_y, 0, 0x3ff);
|
||||
accel_z = MathUtil::Clamp<s16>(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)
|
|
@ -5,7 +5,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#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;
|
|
@ -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 <array>
|
||||
#include <cassert>
|
||||
|
@ -44,7 +44,7 @@ constexpr std::array<const char*, 6> 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<DataFormat&>(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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 <algorithm>
|
||||
|
||||
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
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2019 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#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 <typename T>
|
||||
static int RawRead(T* reg_data, u8 addr, int count, u8* data_out)
|
||||
{
|
||||
static_assert(std::is_pod<T>::value);
|
||||
static_assert(0x100 == sizeof(T));
|
||||
|
||||
// TODO: addr wraps around after 0xff
|
||||
|
||||
u8* src = reinterpret_cast<u8*>(reg_data) + addr;
|
||||
count = std::min(count, int(reinterpret_cast<u8*>(reg_data + 1) - src));
|
||||
|
||||
std::copy_n(src, count, data_out);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static int RawWrite(T* reg_data, u8 addr, int count, const u8* data_in)
|
||||
{
|
||||
static_assert(std::is_pod<T>::value);
|
||||
static_assert(0x100 == sizeof(T));
|
||||
|
||||
// TODO: addr wraps around after 0xff
|
||||
|
||||
u8* dst = reinterpret_cast<u8*>(reg_data) + addr;
|
||||
count = std::min(count, int(reinterpret_cast<u8*>(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<I2CSlave*> m_slaves;
|
||||
};
|
||||
|
||||
} // namespace WiimoteEmu
|
|
@ -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<u8, 6> 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<PassthroughMode>(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<u8, 1> 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<DataFormat*>(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
|
|
@ -0,0 +1,131 @@
|
|||
// Copyright 2019 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
|
||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
struct MotionPlus : public Extension
|
||||
{
|
||||
public:
|
||||
MotionPlus();
|
||||
|
||||
void Update() override;
|
||||
void Reset() override;
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
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
|
|
@ -2,6 +2,8 @@
|
|||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Speaker.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#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<unsigned int>((0xff + (2 * pan)) * speaker_volume_ratio, 0, 0xff);
|
||||
MathUtil::Clamp<unsigned int>((0xff + (2 * speaker_pan)) * speaker_volume_ratio, 0, 0xff);
|
||||
unsigned int right_volume =
|
||||
MathUtil::Clamp<unsigned int>((0xff - (2 * pan)) * speaker_volume_ratio, 0, 0xff);
|
||||
MathUtil::Clamp<unsigned int>((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
|
||||
|
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -4,25 +4,25 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
|
||||
#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 <typename T>
|
||||
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<decltype(data[0]), u8&>::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<int, 3> timing; // Hold length in frames for each axis
|
||||
std::array<double, 3> intensity; // Swing or shake intensity
|
||||
std::array<int, 3> 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<u8, 0x10>& 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 <typename T>
|
||||
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<T>::value);
|
||||
static_assert(0x100 == sizeof(T));
|
||||
|
||||
// TODO: addr wraps around after 0xff
|
||||
|
||||
u8* src = reinterpret_cast<u8*>(reg_data) + addr;
|
||||
count = std::min(count, int(reinterpret_cast<u8*>(reg_data + 1) - src));
|
||||
|
||||
std::copy_n(src, count, data_out);
|
||||
|
||||
return count;
|
||||
*i = checksum;
|
||||
checksum += CALIBRATION_MAGIC_NUMBER;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static int RawWrite(T* reg_data, u8 addr, int count, const u8* data_in)
|
||||
{
|
||||
static_assert(std::is_pod<T>::value);
|
||||
static_assert(0x100 == sizeof(T));
|
||||
|
||||
// TODO: addr wraps around after 0xff
|
||||
|
||||
u8* dst = reinterpret_cast<u8*>(reg_data) + addr;
|
||||
count = std::min(count, int(reinterpret_cast<u8*>(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<I2CSlave*> 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 <typename T, typename H>
|
||||
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<PassthroughMode>(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<u8, 1> 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<u8, 11> ir_calibration_1;
|
||||
std::array<u8, 11> ir_calibration_2;
|
||||
|
||||
std::array<u8, 10> accel_calibration_1;
|
||||
std::array<u8, 10> accel_calibration_2;
|
||||
|
||||
// addr: 0x002A
|
||||
std::array<u8, 0x0FA0> user_data;
|
||||
|
||||
// addr: 0x0FCA
|
||||
std::array<u8, 0x02f0> mii_data_1;
|
||||
std::array<u8, 0x02f0> mii_data_2;
|
||||
|
||||
// addr: 0x15AA
|
||||
std::array<u8, 0x0126> unk_1;
|
||||
|
||||
// addr: 0x16D0
|
||||
std::array<u8, 24> unk_2;
|
||||
std::array<u8, 24> unk_3;
|
||||
};
|
||||
|
||||
std::array<u8, EEPROM_FREE_SIZE> 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<u8, 3> m_shake_step{};
|
||||
std::array<u8, 3> m_shake_soft_step{};
|
||||
std::array<u8, 3> m_shake_hard_step{};
|
||||
std::array<u8, 3> 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
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||
#include "Core/HW/WiimoteCommon/DataReport.h"
|
||||
#include "Core/HW/WiimoteReal/IOWin.h"
|
||||
|
||||
// Create func_t function pointer type and declare a nullptr-initialized static variable of that
|
||||
|
@ -436,6 +438,8 @@ int ReadFromHandle(HANDLE& dev_handle, u8* buf)
|
|||
|
||||
bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMethod& method)
|
||||
{
|
||||
using namespace WiimoteCommon;
|
||||
|
||||
HANDLE dev_handle = CreateFile(device_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED, nullptr);
|
||||
|
@ -445,7 +449,7 @@ bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMethod& meth
|
|||
Common::ScopeGuard handle_guard{[&dev_handle] { CloseHandle(dev_handle); }};
|
||||
|
||||
u8 buf[MAX_PAYLOAD];
|
||||
u8 const req_status_report[] = {WR_SET_REPORT | BT_OUTPUT, RT_REQUEST_STATUS, 0};
|
||||
u8 const req_status_report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::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<TCHAR>& device_path, WinWriteMethod& meth
|
|||
if (rc <= 0)
|
||||
break;
|
||||
|
||||
switch (buf[1])
|
||||
switch (InputReportID(buf[1]))
|
||||
{
|
||||
case RT_STATUS_REPORT:
|
||||
case InputReportID::STATUS:
|
||||
return true;
|
||||
default:
|
||||
WARN_LOG(WIIMOTE, "IsWiimote(): Received unexpected report %02x", buf[1]);
|
||||
|
@ -691,34 +695,25 @@ bool WiimoteWindows::IsConnected() const
|
|||
}
|
||||
|
||||
// See http://wiibrew.org/wiki/Wiimote for the Report IDs and its sizes
|
||||
size_t GetReportSize(u8 report_id)
|
||||
size_t GetReportSize(u8 rid)
|
||||
{
|
||||
using namespace WiimoteCommon;
|
||||
|
||||
const auto report_id = static_cast<InputReportID>(rid);
|
||||
|
||||
switch (report_id)
|
||||
{
|
||||
case RT_STATUS_REPORT:
|
||||
return sizeof(wm_status_report);
|
||||
case RT_READ_DATA_REPLY:
|
||||
return sizeof(wm_read_data_reply);
|
||||
case RT_ACK_DATA:
|
||||
return sizeof(wm_acknowledge);
|
||||
case RT_REPORT_CORE:
|
||||
return sizeof(wm_report_core);
|
||||
case RT_REPORT_CORE_ACCEL:
|
||||
return sizeof(wm_report_core_accel);
|
||||
case RT_REPORT_CORE_EXT8:
|
||||
return sizeof(wm_report_core_ext8);
|
||||
case RT_REPORT_CORE_ACCEL_IR12:
|
||||
return sizeof(wm_report_core_accel_ir12);
|
||||
case RT_REPORT_CORE_EXT19:
|
||||
case RT_REPORT_CORE_ACCEL_EXT16:
|
||||
case RT_REPORT_CORE_IR10_EXT9:
|
||||
case RT_REPORT_CORE_ACCEL_IR10_EXT6:
|
||||
case RT_REPORT_EXT21:
|
||||
case RT_REPORT_INTERLEAVE1:
|
||||
case RT_REPORT_INTERLEAVE2:
|
||||
return sizeof(wm_report_ext21);
|
||||
case InputReportID::STATUS:
|
||||
return sizeof(InputReportStatus);
|
||||
case InputReportID::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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<const hid_packet*>(data);
|
||||
if (hidp->type == HID_TYPE_SET_REPORT)
|
||||
const auto& hidp = *static_cast<const HIDPacket*>(data);
|
||||
if (hidp.type == HID_TYPE_SET_REPORT)
|
||||
{
|
||||
u8 handshake_ok = HID_HANDSHAKE_SUCCESS;
|
||||
Core::Callback_WiimoteInterruptChannel(m_index, channel, &handshake_ok, sizeof(handshake_ok));
|
||||
u8 handshake = HID_HANDSHAKE_SUCCESS;
|
||||
Core::Callback_WiimoteInterruptChannel(m_index, channel, &handshake, sizeof(handshake));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<wm_leds*>(&rpt[2]);
|
||||
auto& leds_rpt = *reinterpret_cast<OutputReportLeds*>(&rpt[2]);
|
||||
if (0 == leds_rpt.leds)
|
||||
{
|
||||
// Turn on ALL of the LEDs.
|
||||
leds_rpt.leds = 0xf;
|
||||
}
|
||||
}
|
||||
else if (rpt[1] == RT_WRITE_SPEAKER_DATA && (!SConfig::GetInstance().m_WiimoteEnableSpeaker ||
|
||||
(!wm->m_status.speaker || wm->m_speaker_mute)))
|
||||
else if (rpt[1] == u8(OutputReportID::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<wm_status_report*>(&buf[2]);
|
||||
const auto* status = reinterpret_cast<InputReportStatus*>(&buf[2]);
|
||||
// A Balance Board has a Balance Board extension.
|
||||
if (!status->extension)
|
||||
return false;
|
||||
// Read two bytes from 0xa400fe to identify the extension.
|
||||
static const u8 identify_ext_rpt[] = {
|
||||
WR_SET_REPORT | BT_OUTPUT, RT_READ_DATA, 0x04, 0xa4, 0x00, 0xfe, 0x02, 0x00};
|
||||
static const u8 identify_ext_rpt[] = {WR_SET_REPORT | BT_OUTPUT,
|
||||
u8(OutputReportID::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<wm_read_data_reply*>(&buf[2]);
|
||||
const auto* reply = reinterpret_cast<InputReportReadDataReply*>(&buf[2]);
|
||||
if (Common::swap16(reply->address) != 0x00fe)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "IsBalanceBoard(): Received unexpected data reply for address %X",
|
||||
|
@ -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<wm_acknowledge*>(&buf[2]);
|
||||
if (ack->reportID == RT_READ_DATA && ack->errorID != 0x00)
|
||||
const auto* ack = reinterpret_cast<InputReportAck*>(&buf[2]);
|
||||
if (ack->rpt_id == OutputReportID::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.");
|
||||
|
||||
|
|
|
@ -23,6 +23,37 @@ class PointerWrap;
|
|||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
using WiimoteCommon::MAX_PAYLOAD;
|
||||
|
||||
typedef std::vector<u8> 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -10,17 +10,13 @@
|
|||
#include <string>
|
||||
|
||||
#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<std::string>* savest
|
|||
void LoadInput(const std::string& movie_path);
|
||||
void ReadHeader();
|
||||
void PlayController(GCPadStatus* PadStatus, int controllerID);
|
||||
bool PlayWiimote(int wiimote, u8* data, const struct WiimoteEmu::ReportFeatures& rptf, int ext,
|
||||
const wiimote_key key);
|
||||
bool PlayWiimote(int wiimote, WiimoteCommon::DataReportBuilder& rpt, int ext,
|
||||
const WiimoteEmu::EncryptionKey& key);
|
||||
void EndPlayInput(bool cont);
|
||||
void SaveRecording(const std::string& filename);
|
||||
void DoState(PointerWrap& p);
|
||||
void Shutdown();
|
||||
void CheckPadStatus(const GCPadStatus* PadStatus, int controllerID);
|
||||
void CheckWiimoteStatus(int wiimote, const u8* data, const struct WiimoteEmu::ReportFeatures& rptf,
|
||||
int ext, const wiimote_key key);
|
||||
void CheckWiimoteStatus(int wiimote, const WiimoteCommon::DataReportBuilder& rpt, int ext,
|
||||
const WiimoteEmu::EncryptionKey& key);
|
||||
|
||||
std::string GetInputDisplay();
|
||||
std::string GetRTCDisplay();
|
||||
|
||||
// Done this way to avoid mixing of core and gui code
|
||||
using GCManipFunction = std::function<void(GCPadStatus*, int)>;
|
||||
using WiiManipFunction =
|
||||
std::function<void(u8*, WiimoteEmu::ReportFeatures, int, int, wiimote_key)>;
|
||||
using WiiManipFunction = std::function<void(WiimoteCommon::DataReportBuilder&, int, int,
|
||||
const WiimoteEmu::EncryptionKey&)>;
|
||||
|
||||
void SetGCInputManip(GCManipFunction);
|
||||
void SetWiiInputManip(WiiManipFunction);
|
||||
void CallGCInputManip(GCPadStatus* PadStatus, int controllerID);
|
||||
void CallWiiInputManip(u8* core, WiimoteEmu::ReportFeatures rptf, int controllerID, int ext,
|
||||
const wiimote_key key);
|
||||
void CallWiiInputManip(WiimoteCommon::DataReportBuilder& rpt, int controllerID, int ext,
|
||||
const WiimoteEmu::EncryptionKey& key);
|
||||
} // namespace Movie
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "Core/HW/Sram.h"
|
||||
#include "Core/HW/WiiSave.h"
|
||||
#include "Core/HW/WiiSaveStructs.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
#include "Core/IOS/FS/FileSystem.h"
|
||||
|
@ -61,6 +62,8 @@
|
|||
|
||||
namespace NetPlay
|
||||
{
|
||||
using namespace WiimoteCommon;
|
||||
|
||||
static std::mutex crit_netplay_client;
|
||||
static NetPlayClient* netplay_client = nullptr;
|
||||
static std::unique_ptr<IOS::HLE::FS::FileSystem> s_wii_sync_fs;
|
||||
|
|
|
@ -9,6 +9,11 @@
|
|||
#include <QLabel>
|
||||
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Drums.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Guitar.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Turntable.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "InputCommon/InputConfig.h"
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "DolphinQt/Config/Mapping/MappingWindow.h"
|
||||
#include "DolphinQt/Config/Mapping/WiimoteEmuExtension.h"
|
||||
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
|
||||
#include "InputCommon/ControllerEmu/Setting/BooleanSetting.h"
|
||||
#include "InputCommon/InputConfig.h"
|
||||
|
||||
|
@ -40,12 +40,13 @@ void WiimoteEmuGeneral::CreateMainLayout()
|
|||
m_main_layout->addWidget(CreateGroupBox(
|
||||
tr("Hotkeys"), Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Hotkeys)));
|
||||
|
||||
auto* extension_group = Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Extension);
|
||||
auto* extension_group =
|
||||
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Attachments);
|
||||
auto* extension = CreateGroupBox(tr("Extension"), extension_group);
|
||||
auto* ce_extension = static_cast<ControllerEmu::Extension*>(extension_group);
|
||||
auto* ce_extension = static_cast<ControllerEmu::Attachments*>(extension_group);
|
||||
m_extension_combo = new QComboBox();
|
||||
|
||||
for (const auto& attachment : ce_extension->attachments)
|
||||
for (const auto& attachment : ce_extension->GetAttachmentList())
|
||||
{
|
||||
// TODO: Figure out how to localize this
|
||||
m_extension_combo->addItem(QString::fromStdString(attachment->GetName()));
|
||||
|
@ -88,18 +89,18 @@ void WiimoteEmuGeneral::OnAttachmentChanged(int extension)
|
|||
|
||||
m_extension_widget->ChangeExtensionType(value_map[value]);
|
||||
|
||||
auto* ce_extension = static_cast<ControllerEmu::Extension*>(
|
||||
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Extension));
|
||||
ce_extension->switch_extension = extension;
|
||||
auto* ce_extension = static_cast<ControllerEmu::Attachments*>(
|
||||
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Attachments));
|
||||
ce_extension->SetSelectedAttachment(extension);
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
void WiimoteEmuGeneral::Update()
|
||||
{
|
||||
auto* ce_extension = static_cast<ControllerEmu::Extension*>(
|
||||
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Extension));
|
||||
auto* ce_extension = static_cast<ControllerEmu::Attachments*>(
|
||||
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Attachments));
|
||||
|
||||
m_extension_combo->setCurrentIndex(ce_extension->switch_extension);
|
||||
m_extension_combo->setCurrentIndex(ce_extension->GetSelectedAttachment());
|
||||
}
|
||||
|
||||
void WiimoteEmuGeneral::LoadSettings()
|
||||
|
|
|
@ -350,9 +350,9 @@ void MainWindow::CreateComponents()
|
|||
m_gc_tas_input_windows[controller_id]->GetValues(pad_status);
|
||||
});
|
||||
|
||||
Movie::SetWiiInputManip([this](u8* input_data, WiimoteEmu::ReportFeatures rptf, int controller_id,
|
||||
int ext, wiimote_key key) {
|
||||
m_wii_tas_input_windows[controller_id]->GetValues(input_data, rptf, ext, key);
|
||||
Movie::SetWiiInputManip([this](WiimoteCommon::DataReportBuilder& rpt, int controller_id, int ext,
|
||||
const WiimoteEmu::EncryptionKey& key) {
|
||||
m_wii_tas_input_windows[controller_id]->GetValues(rpt, ext, key);
|
||||
});
|
||||
|
||||
m_jit_widget = new JITWidget(this);
|
||||
|
|
|
@ -14,10 +14,16 @@
|
|||
#include "Common/FileUtil.h"
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/WiimoteEmu/Attachment/Classic.h"
|
||||
#include "Core/HW/WiimoteEmu/Attachment/Nunchuk.h"
|
||||
#include "Core/HW/WiimoteCommon/DataReport.h"
|
||||
#include "Core/HW/WiimoteEmu/Encryption.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
|
||||
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
|
||||
#include "Core/HW/WiimoteEmu/Camera.h"
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
|
||||
#include "DolphinQt/QtUtils/AspectRatioWidget.h"
|
||||
|
@ -27,6 +33,8 @@
|
|||
|
||||
#include "InputCommon/InputConfig.h"
|
||||
|
||||
using namespace WiimoteCommon;
|
||||
|
||||
WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(parent), m_num(num)
|
||||
{
|
||||
const QKeySequence ir_x_shortcut_key_sequence = QKeySequence(Qt::ALT + Qt::Key_F);
|
||||
|
@ -255,7 +263,7 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
|
|||
if (Core::IsRunning())
|
||||
{
|
||||
ext = static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(num))
|
||||
->CurrentExtension();
|
||||
->GetActiveExtensionNumber();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -318,22 +326,20 @@ void WiiTASInputWindow::UpdateExt(u8 ext)
|
|||
}
|
||||
}
|
||||
|
||||
void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rptf, int ext,
|
||||
wiimote_key key)
|
||||
void WiiTASInputWindow::GetValues(DataReportBuilder& rpt, int ext,
|
||||
const WiimoteEmu::EncryptionKey& key)
|
||||
{
|
||||
if (!isVisible())
|
||||
return;
|
||||
|
||||
UpdateExt(ext);
|
||||
|
||||
u8* const buttons_data = rptf.core_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<wm_buttons*>(buttons_data))->hex;
|
||||
DataReportBuilder::CoreData core;
|
||||
rpt.GetCoreData(&core);
|
||||
|
||||
u16& buttons = core.hex;
|
||||
GetButton<u16>(m_a_button, buttons, WiimoteEmu::Wiimote::BUTTON_A);
|
||||
GetButton<u16>(m_b_button, buttons, WiimoteEmu::Wiimote::BUTTON_B);
|
||||
GetButton<u16>(m_1_button, buttons, WiimoteEmu::Wiimote::BUTTON_ONE);
|
||||
|
@ -345,34 +351,28 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
|
|||
GetButton<u16>(m_up_button, buttons, WiimoteEmu::Wiimote::PAD_UP);
|
||||
GetButton<u16>(m_down_button, buttons, WiimoteEmu::Wiimote::PAD_DOWN);
|
||||
GetButton<u16>(m_right_button, buttons, WiimoteEmu::Wiimote::PAD_RIGHT);
|
||||
|
||||
rpt.SetCoreData(core);
|
||||
}
|
||||
|
||||
if (m_remote_orientation_box->isVisible() && accel_data && buttons_data)
|
||||
if (m_remote_orientation_box->isVisible() && rpt.HasAccel())
|
||||
{
|
||||
wm_accel& accel = *reinterpret_cast<wm_accel*>(accel_data);
|
||||
//wm_buttons& buttons = *reinterpret_cast<wm_buttons*>(buttons_data);
|
||||
// FYI: Interleaved reports may behave funky as not all data is always available.
|
||||
|
||||
// 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<u16, 4> 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<wm_ir_basic*>(ir_data);
|
||||
memset(ir_data, 0xFF, sizeof(WiimoteEmu::IRBasic) * 2);
|
||||
auto* const ir_basic = reinterpret_cast<WiimoteEmu::IRBasic*>(ir_data);
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (x[i * 2] < 1024 && y < 768)
|
||||
|
@ -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<wm_ir_extended*>(ir_data);
|
||||
memset(ir_data, 0xFF, sizeof(WiimoteEmu::IRExtended) * 4);
|
||||
auto* const ir_extended = reinterpret_cast<WiimoteEmu::IRExtended*>(ir_data);
|
||||
for (size_t i = 0; i < x.size(); ++i)
|
||||
{
|
||||
if (x[i] < 1024 && y < 768)
|
||||
|
@ -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<wm_nc*>(ext_data);
|
||||
u8* const ext_data = rpt.GetExtDataPtr();
|
||||
|
||||
auto& nunchuk = *reinterpret_cast<WiimoteEmu::Nunchuk::DataFormat*>(ext_data);
|
||||
|
||||
GetSpinBoxU8(m_nunchuk_stick_x_value, nunchuk.jx);
|
||||
GetSpinBoxU8(m_nunchuk_stick_y_value, nunchuk.jy);
|
||||
|
@ -462,13 +465,15 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
|
|||
GetButton<u8>(m_z_button, nunchuk.bt.hex, WiimoteEmu::Nunchuk::BUTTON_Z);
|
||||
nunchuk.bt.hex ^= 0b11;
|
||||
|
||||
WiimoteEncrypt(&key, reinterpret_cast<u8*>(&nunchuk), 0, sizeof(wm_nc));
|
||||
key.Encrypt(reinterpret_cast<u8*>(&nunchuk), 0, sizeof(nunchuk));
|
||||
}
|
||||
|
||||
if (m_classic_left_stick_box->isVisible())
|
||||
{
|
||||
wm_classic_extension& cc = *reinterpret_cast<wm_classic_extension*>(ext_data);
|
||||
WiimoteDecrypt(&key, reinterpret_cast<u8*>(&cc), 0, sizeof(wm_classic_extension));
|
||||
u8* const ext_data = rpt.GetExtDataPtr();
|
||||
|
||||
auto& cc = *reinterpret_cast<WiimoteEmu::Classic::DataFormat*>(ext_data);
|
||||
key.Decrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
|
||||
|
||||
cc.bt.hex ^= 0xFFFF;
|
||||
GetButton<u16>(m_classic_a_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_A);
|
||||
|
@ -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<u8*>(&cc), 0, sizeof(wm_classic_extension));
|
||||
key.Encrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,21 +6,27 @@
|
|||
|
||||
#include "DolphinQt/TAS/TASInputWindow.h"
|
||||
|
||||
namespace WiimoteCommon
|
||||
{
|
||||
class DataReportBuilder;
|
||||
}
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
struct ReportFeatures;
|
||||
class EncryptionKey;
|
||||
}
|
||||
|
||||
class QCheckBox;
|
||||
class QGroupBox;
|
||||
class QSpinBox;
|
||||
struct wiimote_key;
|
||||
|
||||
class WiiTASInputWindow : public TASInputWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WiiTASInputWindow(QWidget* parent, int num);
|
||||
void GetValues(u8* input_data, WiimoteEmu::ReportFeatures rptf, int ext, wiimote_key key);
|
||||
void GetValues(WiimoteCommon::DataReportBuilder& rpt, int ext,
|
||||
const WiimoteEmu::EncryptionKey& key);
|
||||
|
||||
private:
|
||||
void UpdateExt(u8 ext);
|
||||
|
|
|
@ -7,10 +7,10 @@ add_library(inputcommon
|
|||
ControllerEmu/Control/Input.cpp
|
||||
ControllerEmu/Control/Output.cpp
|
||||
ControllerEmu/ControlGroup/AnalogStick.cpp
|
||||
ControllerEmu/ControlGroup/Attachments.cpp
|
||||
ControllerEmu/ControlGroup/Buttons.cpp
|
||||
ControllerEmu/ControlGroup/ControlGroup.cpp
|
||||
ControllerEmu/ControlGroup/Cursor.cpp
|
||||
ControllerEmu/ControlGroup/Extension.cpp
|
||||
ControllerEmu/ControlGroup/Force.cpp
|
||||
ControllerEmu/ControlGroup/MixedTriggers.cpp
|
||||
ControllerEmu/ControlGroup/ModifySettingsButton.cpp
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
|
||||
|
||||
namespace ControllerEmu
|
||||
{
|
||||
Attachments::Attachments(const std::string& name_) : ControlGroup(name_, GroupType::Attachments)
|
||||
{
|
||||
}
|
||||
|
||||
void Attachments::AddAttachment(std::unique_ptr<EmulatedController> att)
|
||||
{
|
||||
m_attachments.emplace_back(std::move(att));
|
||||
}
|
||||
|
||||
u32 Attachments::GetSelectedAttachment() const
|
||||
{
|
||||
return m_selected_attachment;
|
||||
}
|
||||
|
||||
void Attachments::SetSelectedAttachment(u32 val)
|
||||
{
|
||||
m_selected_attachment = val;
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<EmulatedController>>& Attachments::GetAttachmentList() const
|
||||
{
|
||||
return m_attachments;
|
||||
}
|
||||
|
||||
} // namespace ControllerEmu
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
|
||||
#include "InputCommon/ControllerEmu/ControllerEmu.h"
|
||||
|
||||
namespace ControllerEmu
|
||||
{
|
||||
// A container of the selected and available attachments
|
||||
// for configuration saving/loading purposes
|
||||
class Attachments : public ControlGroup
|
||||
{
|
||||
public:
|
||||
explicit Attachments(const std::string& name);
|
||||
|
||||
void AddAttachment(std::unique_ptr<EmulatedController> att);
|
||||
|
||||
u32 GetSelectedAttachment() const;
|
||||
void SetSelectedAttachment(u32 val);
|
||||
|
||||
const std::vector<std::unique_ptr<EmulatedController>>& GetAttachmentList() const;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<EmulatedController>> m_attachments;
|
||||
|
||||
std::atomic<u32> m_selected_attachment;
|
||||
};
|
||||
} // namespace ControllerEmu
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include "InputCommon/ControlReference/ControlReference.h"
|
||||
#include "InputCommon/ControllerEmu/Control/Control.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
|
||||
#include "InputCommon/ControllerEmu/ControllerEmu.h"
|
||||
#include "InputCommon/ControllerEmu/Setting/BooleanSetting.h"
|
||||
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
|
||||
|
@ -67,22 +67,22 @@ void ControlGroup::LoadConfig(IniFile::Section* sec, const std::string& defdev,
|
|||
}
|
||||
|
||||
// extensions
|
||||
if (type == GroupType::Extension)
|
||||
if (type == GroupType::Attachments)
|
||||
{
|
||||
Extension* const ext = (Extension*)this;
|
||||
auto* const ext = static_cast<Attachments*>(this);
|
||||
|
||||
ext->switch_extension = 0;
|
||||
ext->SetSelectedAttachment(0);
|
||||
u32 n = 0;
|
||||
std::string extname;
|
||||
sec->Get(base + name, &extname, "");
|
||||
|
||||
for (auto& ai : ext->attachments)
|
||||
for (auto& ai : ext->GetAttachmentList())
|
||||
{
|
||||
ai->SetDefaultDevice(defdev);
|
||||
ai->LoadConfig(sec, base + ai->GetName() + "/");
|
||||
|
||||
if (ai->GetName() == extname)
|
||||
ext->switch_extension = n;
|
||||
ext->SetSelectedAttachment(n);
|
||||
|
||||
n++;
|
||||
}
|
||||
|
@ -120,12 +120,13 @@ void ControlGroup::SaveConfig(IniFile::Section* sec, const std::string& defdev,
|
|||
}
|
||||
|
||||
// extensions
|
||||
if (type == GroupType::Extension)
|
||||
if (type == GroupType::Attachments)
|
||||
{
|
||||
Extension* const ext = (Extension*)this;
|
||||
sec->Set(base + name, ext->attachments[ext->switch_extension]->GetName(), "None");
|
||||
auto* const ext = static_cast<Attachments*>(this);
|
||||
sec->Set(base + name, ext->GetAttachmentList()[ext->GetSelectedAttachment()]->GetName(),
|
||||
"None");
|
||||
|
||||
for (auto& ai : ext->attachments)
|
||||
for (auto& ai : ext->GetAttachmentList())
|
||||
ai->SaveConfig(sec, base + ai->GetName() + "/");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ enum class GroupType
|
|||
MixedTriggers,
|
||||
Buttons,
|
||||
Force,
|
||||
Extension,
|
||||
Attachments,
|
||||
Tilt,
|
||||
Cursor,
|
||||
Triggers,
|
||||
|
|
|
@ -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 <string>
|
||||
|
||||
#include "InputCommon/ControllerEmu/ControllerEmu.h"
|
||||
|
||||
namespace ControllerEmu
|
||||
{
|
||||
Extension::Extension(const std::string& name_) : ControlGroup(name_, GroupType::Extension)
|
||||
{
|
||||
}
|
||||
} // namespace ControllerEmu
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<std::unique_ptr<EmulatedController>> attachments;
|
||||
|
||||
int switch_extension = 0;
|
||||
int active_extension = 0;
|
||||
};
|
||||
} // namespace ControllerEmu
|
|
@ -19,8 +19,5 @@ public:
|
|||
explicit Force(const std::string& name);
|
||||
|
||||
StateData GetState();
|
||||
|
||||
private:
|
||||
StateData m_swing{};
|
||||
};
|
||||
} // namespace ControllerEmu
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
|
||||
#include "InputCommon/ControlReference/ControlReference.h"
|
||||
#include "InputCommon/ControllerEmu/Control/Control.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
|
||||
namespace ControllerEmu
|
||||
|
@ -41,10 +41,10 @@ void EmulatedController::UpdateReferences(const ControllerInterface& devi)
|
|||
for (auto& control : ctrlGroup->controls)
|
||||
control->control_ref.get()->UpdateReference(devi, GetDefaultDevice());
|
||||
|
||||
// extension
|
||||
if (ctrlGroup->type == GroupType::Extension)
|
||||
// Attachments:
|
||||
if (ctrlGroup->type == GroupType::Attachments)
|
||||
{
|
||||
for (auto& attachment : ((Extension*)ctrlGroup.get())->attachments)
|
||||
for (auto& attachment : static_cast<Attachments*>(ctrlGroup.get())->GetAttachmentList())
|
||||
attachment->UpdateReferences(devi);
|
||||
}
|
||||
}
|
||||
|
@ -73,10 +73,10 @@ void EmulatedController::SetDefaultDevice(ciface::Core::DeviceQualifier devq)
|
|||
|
||||
for (auto& ctrlGroup : groups)
|
||||
{
|
||||
// extension
|
||||
if (ctrlGroup->type == GroupType::Extension)
|
||||
// Attachments:
|
||||
if (ctrlGroup->type == GroupType::Attachments)
|
||||
{
|
||||
for (auto& ai : ((Extension*)ctrlGroup.get())->attachments)
|
||||
for (auto& ai : static_cast<Attachments*>(ctrlGroup.get())->GetAttachmentList())
|
||||
{
|
||||
ai->SetDefaultDevice(m_default_device);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
<ClCompile Include="ControllerEmu\ControlGroup\Buttons.cpp" />
|
||||
<ClCompile Include="ControllerEmu\ControlGroup\ControlGroup.cpp" />
|
||||
<ClCompile Include="ControllerEmu\ControlGroup\Cursor.cpp" />
|
||||
<ClCompile Include="ControllerEmu\ControlGroup\Extension.cpp" />
|
||||
<ClCompile Include="ControllerEmu\ControlGroup\Attachments.cpp" />
|
||||
<ClCompile Include="ControllerEmu\ControlGroup\Force.cpp" />
|
||||
<ClCompile Include="ControllerEmu\ControlGroup\MixedTriggers.cpp" />
|
||||
<ClCompile Include="ControllerEmu\ControlGroup\ModifySettingsButton.cpp" />
|
||||
|
@ -84,7 +84,7 @@
|
|||
<ClInclude Include="ControllerEmu\ControlGroup\Buttons.h" />
|
||||
<ClInclude Include="ControllerEmu\ControlGroup\ControlGroup.h" />
|
||||
<ClInclude Include="ControllerEmu\ControlGroup\Cursor.h" />
|
||||
<ClInclude Include="ControllerEmu\ControlGroup\Extension.h" />
|
||||
<ClInclude Include="ControllerEmu\ControlGroup\Attachments.h" />
|
||||
<ClInclude Include="ControllerEmu\ControlGroup\Force.h" />
|
||||
<ClInclude Include="ControllerEmu\ControlGroup\MixedTriggers.h" />
|
||||
<ClInclude Include="ControllerEmu\ControlGroup\ModifySettingsButton.h" />
|
||||
|
|
|
@ -56,9 +56,6 @@
|
|||
<ClCompile Include="ControllerEmu\ControlGroup\Cursor.cpp">
|
||||
<Filter>ControllerEmu\ControlGroup</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControllerEmu\ControlGroup\Extension.cpp">
|
||||
<Filter>ControllerEmu\ControlGroup</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControllerEmu\ControlGroup\Force.cpp">
|
||||
<Filter>ControllerEmu\ControlGroup</Filter>
|
||||
</ClCompile>
|
||||
|
@ -114,6 +111,9 @@
|
|||
<Filter>ControllerInterface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InputProfile.cpp" />
|
||||
<ClCompile Include="ControllerEmu\ControlGroup\Attachments.cpp">
|
||||
<Filter>ControllerEmu\ControlGroup</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="GCAdapter.h" />
|
||||
|
@ -146,9 +146,6 @@
|
|||
<ClInclude Include="ControllerEmu\ControlGroup\Cursor.h">
|
||||
<Filter>ControllerEmu\ControlGroup</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ControllerEmu\ControlGroup\Extension.h">
|
||||
<Filter>ControllerEmu\ControlGroup</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ControllerEmu\ControlGroup\Force.h">
|
||||
<Filter>ControllerEmu\ControlGroup</Filter>
|
||||
</ClInclude>
|
||||
|
@ -210,6 +207,9 @@
|
|||
<Filter>ControllerInterface</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="InputProfile.h" />
|
||||
<ClInclude Include="ControllerEmu\ControlGroup\Attachments.h">
|
||||
<Filter>ControllerEmu\ControlGroup</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
|
Loading…
Reference in New Issue