summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2019-01-01 08:32:39 -0600
committerJordan Woyak <jordan.woyak@gmail.com>2019-02-02 19:40:31 -0600
commit0d1fbe7bbca8e42449ee37600519509cb47516b9 (patch)
tree28c80ff2fa153a50e9e7ed9bee91ed85a9692289 /Source/Core
parentb1f350ab1c225996bf5eee169acd1f53d8936234 (diff)
WiimoteEmu: Major renaming and cleanup.
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Common/BitUtils.h10
-rw-r--r--Source/Core/Core/CMakeLists.txt18
-rw-r--r--Source/Core/Core/Core.vcxproj37
-rw-r--r--Source/Core/Core/Core.vcxproj.filters131
-rw-r--r--Source/Core/Core/HW/WiimoteCommon/DataReport.cpp425
-rw-r--r--Source/Core/Core/HW/WiimoteCommon/DataReport.h115
-rw-r--r--Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h70
-rw-r--r--Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h48
-rw-r--r--Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h545
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.cpp79
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.h41
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Camera.cpp202
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Camera.h104
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp262
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Dynamics.h68
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp538
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Encryption.cpp24
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Encryption.h21
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp (renamed from Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.cpp)85
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Classic.h (renamed from Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.h)72
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp (renamed from Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.cpp)33
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Drums.h (renamed from Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.h)44
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp129
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h112
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp (renamed from Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.cpp)24
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.h (renamed from Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.h)44
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp (renamed from Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.cpp)101
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.h (renamed from Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.h)59
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp (renamed from Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.cpp)22
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.h (renamed from Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.h)50
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp29
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h43
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp56
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/I2CBus.h76
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp376
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/MotionPlus.h131
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Speaker.cpp75
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/Speaker.h67
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp1230
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h814
-rw-r--r--Source/Core/Core/HW/WiimoteReal/IOWin.cpp51
-rw-r--r--Source/Core/Core/HW/WiimoteReal/IOdarwin.mm2
-rw-r--r--Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp7
-rw-r--r--Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp137
-rw-r--r--Source/Core/Core/HW/WiimoteReal/WiimoteReal.h33
-rw-r--r--Source/Core/Core/Movie.cpp98
-rw-r--r--Source/Core/Core/Movie.h24
-rw-r--r--Source/Core/Core/NetPlayClient.cpp3
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp5
-rw-r--r--Source/Core/DolphinQt/Config/Mapping/WiimoteEmuGeneral.cpp21
-rw-r--r--Source/Core/DolphinQt/MainWindow.cpp6
-rw-r--r--Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp117
-rw-r--r--Source/Core/DolphinQt/TAS/WiiTASInputWindow.h12
-rw-r--r--Source/Core/InputCommon/CMakeLists.txt2
-rw-r--r--Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp33
-rw-r--r--Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h37
-rw-r--r--Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.cpp21
-rw-r--r--Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h2
-rw-r--r--Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.cpp16
-rw-r--r--Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.h31
-rw-r--r--Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.h3
-rw-r--r--Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp14
-rw-r--r--Source/Core/InputCommon/InputCommon.vcxproj6
-rw-r--r--Source/Core/InputCommon/InputCommon.vcxproj.filters14
64 files changed, 4030 insertions, 3075 deletions
diff --git a/Source/Core/Common/BitUtils.h b/Source/Core/Common/BitUtils.h
index c975b5c475..62b9604fab 100644
--- a/Source/Core/Common/BitUtils.h
+++ b/Source/Core/Common/BitUtils.h
@@ -199,4 +199,14 @@ inline To BitCast(const From& source) noexcept
std::memcpy(&storage, &source, sizeof(storage));
return reinterpret_cast<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
diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index ccf1f69e64..433aeb9d15 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -133,16 +133,22 @@ add_library(core
HW/VideoInterface.cpp
HW/WII_IPC.cpp
HW/Wiimote.cpp
+ HW/WiimoteCommon/DataReport.cpp
HW/WiimoteEmu/WiimoteEmu.cpp
- HW/WiimoteEmu/Attachment/Classic.cpp
- HW/WiimoteEmu/Attachment/Attachment.cpp
- HW/WiimoteEmu/Attachment/Nunchuk.cpp
- HW/WiimoteEmu/Attachment/Drums.cpp
- HW/WiimoteEmu/Attachment/Guitar.cpp
- HW/WiimoteEmu/Attachment/Turntable.cpp
+ HW/WiimoteEmu/Camera.cpp
+ HW/WiimoteEmu/Dynamics.cpp
HW/WiimoteEmu/EmuSubroutines.cpp
HW/WiimoteEmu/Encryption.cpp
+ HW/WiimoteEmu/ExtensionPort.cpp
+ HW/WiimoteEmu/I2CBus.cpp
+ HW/WiimoteEmu/MotionPlus.cpp
HW/WiimoteEmu/Speaker.cpp
+ HW/WiimoteEmu/Extension/Classic.cpp
+ HW/WiimoteEmu/Extension/Extension.cpp
+ HW/WiimoteEmu/Extension/Nunchuk.cpp
+ HW/WiimoteEmu/Extension/Drums.cpp
+ HW/WiimoteEmu/Extension/Guitar.cpp
+ HW/WiimoteEmu/Extension/Turntable.cpp
HW/WiimoteReal/WiimoteReal.cpp
HW/WiiSave.cpp
IOS/Device.cpp
diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj
index b3054e54bc..837fbfdd15 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -171,14 +171,20 @@
<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" />
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index 5c5242c852..114b9a61ac 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -79,9 +79,6 @@
<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" />
diff --git a/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp b/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp
new file mode 100644
index 0000000000..47b01d2a65
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp
@@ -0,0 +1,425 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <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
diff --git a/Source/Core/Core/HW/WiimoteCommon/DataReport.h b/Source/Core/Core/HW/WiimoteCommon/DataReport.h
new file mode 100644
index 0000000000..f48981677b
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteCommon/DataReport.h
@@ -0,0 +1,115 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <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
diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h
index a72776dbcd..7b0e5dc30d 100644
--- a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h
+++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h
@@ -6,58 +6,76 @@
#include "Common/CommonTypes.h"
-// Wiimote internal codes
-
-// Communication channels
-enum WiimoteChannel
+namespace WiimoteCommon
{
- WC_OUTPUT = 0x11,
- WC_INPUT = 0x13
-};
+constexpr u8 MAX_PAYLOAD = 23;
-// 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 InputReportID : u8
{
-#ifdef _WIN32
- WR_SET_REPORT = 0x50
-#else
- WR_SET_REPORT = 0xA0
-#endif
+ 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,
};
-enum WiimoteBT
+enum class OutputReportID : u8
{
- BT_INPUT = 0x01,
- BT_OUTPUT = 0x02
+ 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,
};
-// LED bit masks
-enum WiimoteLED
+enum class LED : u8
{
- LED_NONE = 0x00,
+ NONE = 0x00,
LED_1 = 0x10,
LED_2 = 0x20,
LED_3 = 0x40,
LED_4 = 0x80
};
-enum class WiimoteAddressSpace : u8
+enum class AddressSpace : u8
{
+ // FYI: The EEPROM address space is offset 0x0070 on i2c slave 0x50.
+ // However attempting to access this device directly results in an error.
EEPROM = 0x00,
// 0x01 is never used but it does function on a real wiimote:
I2C_BUS_ALT = 0x01,
I2C_BUS = 0x02,
};
-enum class WiimoteErrorCode : u8
+enum class ErrorCode : u8
{
SUCCESS = 0,
INVALID_SPACE = 6,
NACK = 7,
INVALID_ADDRESS = 8,
+
+ // Not a real value:
+ DO_NOT_SEND_ACK = 0xff,
};
-constexpr u8 MAX_PAYLOAD = 23;
-constexpr u32 WIIMOTE_DEFAULT_TIMEOUT = 1000;
+} // namespace WiimoteCommon
diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h
index a467d9017b..ccd05a9303 100644
--- a/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h
+++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h
@@ -5,55 +5,71 @@
#pragma once
#include "Common/CommonTypes.h"
+#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
+#include "Core/HW/WiimoteCommon/WiimoteReport.h"
+
+namespace WiimoteCommon
+{
+// Source: HID_010_SPC_PFL/1.0 (official HID specification)
+
+constexpr u8 HID_TYPE_HANDSHAKE = 0;
+constexpr u8 HID_TYPE_SET_REPORT = 5;
+constexpr u8 HID_TYPE_DATA = 0xA;
+
+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)
-// Source: HID_010_SPC_PFL/1.0 (official HID specification)
-
-struct hid_packet
+struct HIDPacket
{
+ static constexpr int HEADER_SIZE = 1;
+
u8 param : 4;
u8 type : 4;
+
u8 data[0];
};
template <typename T>
-struct TypedHidPacket
+struct TypedHIDInputData
{
+ TypedHIDInputData(InputReportID _rpt_id)
+ : param(HID_PARAM_INPUT), type(HID_TYPE_DATA), report_id(_rpt_id)
+ {
+ }
+
u8 param : 4;
u8 type : 4;
- // TODO: auto set this from the T-type:
- u8 report_id;
+ 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); }
- u32 GetSize() const
+
+ constexpr 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;
-
-constexpr u8 HID_HANDSHAKE_SUCCESS = 0;
-
-constexpr u8 HID_PARAM_INPUT = 1;
-constexpr u8 HID_PARAM_OUTPUT = 2;
-
#pragma pack(pop)
#ifdef _MSC_VER
#pragma warning(pop)
#endif
+
+} // namespace WiimoteCommon
diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h
index 2d727ac105..18b0293804 100644
--- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h
+++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h
@@ -7,371 +7,23 @@
#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"
-{
- u16 hex;
-
- struct
- {
- u8 left : 1;
- u8 right : 1;
- u8 down : 1;
- u8 up : 1;
- u8 plus : 1;
- // For most input reports this is the 2 LSbs of accel.x:
- // For interleaved reports this is alternating bits of accel.z:
- u8 acc_bits : 2;
- u8 unknown : 1;
-
- u8 two : 1;
- u8 one : 1;
- u8 b : 1;
- u8 a : 1;
- u8 minus : 1;
- // For most input reports this is bits of accel.y/z:
- // For interleaved reports this is alternating bits of accel.z:
- u8 acc_bits2 : 2;
- u8 home : 1;
- };
-};
-static_assert(sizeof(wm_buttons) == 2, "Wrong size");
-
-struct wm_accel
-{
- 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
+struct OutputReportGeneric
{
- 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;
+ OutputReportID rpt_id;
- u8 extension_connected : 1;
- u8 roll_slow : 1;
- u8 roll2 : 6;
+ static constexpr int HEADER_SIZE = sizeof(OutputReportID);
- 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];
@@ -379,54 +31,57 @@ struct wm_report
{
// 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");
+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 wm_leds
+struct OutputReportEnableFeature
{
u8 rumble : 1;
- u8 : 3;
+ // 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(wm_leds) == 1, "Wrong size");
+static_assert(sizeof(OutputReportLeds) == 1, "Wrong size");
-struct wm_report_mode
+struct OutputReportMode
{
u8 rumble : 1;
- u8 : 1;
+ u8 ack : 1;
u8 continuous : 1;
u8 : 5;
- u8 mode;
+ InputReportID mode;
};
-static_assert(sizeof(wm_report_mode) == 2, "Wrong size");
+static_assert(sizeof(OutputReportMode) == 2, "Wrong size");
-struct wm_request_status
+struct OutputReportRequestStatus
{
u8 rumble : 1;
u8 : 7;
};
-static_assert(sizeof(wm_request_status) == 1, "Wrong size");
+static_assert(sizeof(OutputReportRequestStatus) == 1, "Wrong size");
-struct wm_status_report
-{
- wm_buttons buttons;
- u8 battery_low : 1;
- u8 extension : 1;
- u8 speaker : 1;
- u8 ir : 1;
- u8 leds : 4;
- u8 padding2[2];
- u8 battery;
-};
-static_assert(sizeof(wm_status_report) == 6, "Wrong size");
-
-struct wm_write_data
+struct OutputReportWriteData
{
u8 rumble : 1;
u8 space : 2;
@@ -440,17 +95,9 @@ struct wm_write_data
u8 size;
u8 data[16];
};
-static_assert(sizeof(wm_write_data) == 21, "Wrong size");
-
-struct wm_acknowledge
-{
- wm_buttons buttons;
- u8 reportID;
- u8 errorID;
-};
-static_assert(sizeof(wm_acknowledge) == 4, "Wrong size");
+static_assert(sizeof(OutputReportWriteData) == 21, "Wrong size");
-struct wm_read_data
+struct OutputReportReadData
{
u8 rumble : 1;
u8 space : 2;
@@ -463,82 +110,82 @@ struct wm_read_data
u8 address[2];
u8 size[2];
};
-static_assert(sizeof(wm_read_data) == 6, "Wrong size");
+static_assert(sizeof(OutputReportReadData) == 6, "Wrong size");
-struct wm_read_data_reply
+struct OutputReportSpeakerData
{
- wm_buttons buttons;
- u8 error : 4;
- u8 size_minus_one : 4;
- // big endian:
- u16 address;
- u8 data[16];
+ u8 unknown : 3;
+ u8 length : 5;
+ u8 data[20];
};
-static_assert(sizeof(wm_read_data_reply) == 21, "Wrong size");
-
-// Data reports
+static_assert(sizeof(OutputReportSpeakerData) == 21, "Wrong size");
-struct wm_report_core
+// FYI: Also contains LSB of accel data:
+union ButtonData
{
- wm_buttons c;
-};
-static_assert(sizeof(wm_report_core) == 2, "Wrong size");
+ static constexpr u16 BUTTON_MASK = ~0x6060;
-struct wm_report_core_accel
-{
- wm_buttons c;
- wm_accel a;
-};
-static_assert(sizeof(wm_report_core_accel) == 5, "Wrong size");
+ u16 hex;
-struct wm_report_core_ext8
-{
- wm_buttons c;
- u8 ext[8];
-};
-static_assert(sizeof(wm_report_core_ext8) == 10, "Wrong size");
+ struct
+ {
+ u8 left : 1;
+ u8 right : 1;
+ u8 down : 1;
+ u8 up : 1;
+ u8 plus : 1;
+ // For most input reports this is the 2 LSbs of accel.x:
+ // For interleaved reports this is alternating bits of accel.z:
+ u8 acc_bits : 2;
+ u8 unknown : 1;
-struct wm_report_core_accel_ir12
-{
- wm_buttons c;
- wm_accel a;
- wm_ir_extended ir[4];
+ u8 two : 1;
+ u8 one : 1;
+ u8 b : 1;
+ u8 a : 1;
+ u8 minus : 1;
+ // For most input reports this is bits of accel.y/z:
+ // For interleaved reports this is alternating bits of accel.z:
+ u8 acc_bits2 : 2;
+ u8 home : 1;
+ };
};
-static_assert(sizeof(wm_report_core_accel_ir12) == 17, "Wrong size");
+static_assert(sizeof(ButtonData) == 2, "Wrong size");
-struct wm_report_core_accel_ext16
+struct InputReportStatus
{
- 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];
+ ButtonData buttons;
+ u8 battery_low : 1;
+ u8 extension : 1;
+ u8 speaker : 1;
+ u8 ir : 1;
+ u8 leds : 4;
+ u8 padding2[2];
+ u8 battery;
};
-static_assert(sizeof(wm_report_core_accel_ext16) == 21, "Wrong size");
+static_assert(sizeof(InputReportStatus) == 6, "Wrong size");
-struct wm_report_core_accel_ir10_ext6
+struct InputReportAck
{
- 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?
+ ButtonData buttons;
+ OutputReportID rpt_id;
+ ErrorCode error_code;
};
-static_assert(sizeof(wm_report_core_accel_ir10_ext6) == 21, "Wrong size");
+static_assert(sizeof(InputReportAck) == 4, "Wrong size");
-struct wm_report_ext21
+struct InputReportReadDataReply
{
- u8 ext[21];
+ ButtonData buttons;
+ u8 error : 4;
+ u8 size_minus_one : 4;
+ // big endian:
+ u16 address;
+ u8 data[16];
};
-static_assert(sizeof(wm_report_ext21) == 21, "Wrong size");
+static_assert(sizeof(InputReportReadDataReply) == 21, "Wrong size");
+
+} // namespace WiimoteCommon
-struct wm_speaker_data
-{
- u8 unknown : 3;
- u8 length : 5;
- u8 data[20];
-};
-static_assert(sizeof(wm_speaker_data) == 21, "Wrong size");
#pragma pack(pop)
#ifdef _MSC_VER
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.cpp b/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.cpp
deleted file mode 100644
index a9185125b1..0000000000
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2010 Dolphin Emulator Project
-// Licensed under GPLv2+
-// Refer to the license.txt file included.
-
-#include "Core/HW/WiimoteEmu/Attachment/Attachment.h"
-
-#include <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
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.h b/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.h
deleted file mode 100644
index b1bae12086..0000000000
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Attachment.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2010 Dolphin Emulator Project
-// Licensed under GPLv2+
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <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
diff --git a/Source/Core/Core/HW/WiimoteEmu/Camera.cpp b/Source/Core/Core/HW/WiimoteEmu/Camera.cpp
new file mode 100644
index 0000000000..0a8d20c67e
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/Camera.cpp
@@ -0,0 +1,202 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Core/HW/WiimoteEmu/Camera.h"
+
+#include "Common/ChunkFile.h"
+#include "Core/HW/WiimoteCommon/WiimoteReport.h"
+#include "Core/HW/WiimoteEmu/MatrixMath.h"
+
+namespace WiimoteEmu
+{
+void CameraLogic::Reset()
+{
+ reg_data = {};
+}
+
+void CameraLogic::DoState(PointerWrap& p)
+{
+ p.Do(reg_data);
+}
+
+int CameraLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
+{
+ if (I2C_ADDR != slave_addr)
+ return 0;
+
+ return RawRead(&reg_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(&reg_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
diff --git a/Source/Core/Core/HW/WiimoteEmu/Camera.h b/Source/Core/Core/HW/WiimoteEmu/Camera.h
new file mode 100644
index 0000000000..fdcc48858b
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/Camera.h
@@ -0,0 +1,104 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "Common/ChunkFile.h"
+#include "Common/CommonTypes.h"
+#include "Core/HW/WiimoteEmu/Dynamics.h"
+#include "Core/HW/WiimoteEmu/I2CBus.h"
+#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
+
+namespace WiimoteEmu
+{
+// Four bytes for two objects. Filled with 0xFF if empty
+struct IRBasic
+{
+ u8 x1;
+ u8 y1;
+ u8 x2hi : 2;
+ u8 y2hi : 2;
+ u8 x1hi : 2;
+ u8 y1hi : 2;
+ u8 x2;
+ u8 y2;
+};
+static_assert(sizeof(IRBasic) == 5, "Wrong size");
+
+// Three bytes for one object
+struct IRExtended
+{
+ u8 x;
+ u8 y;
+ u8 size : 4;
+ u8 xhi : 2;
+ u8 yhi : 2;
+};
+static_assert(sizeof(IRExtended) == 3, "Wrong size");
+
+// Nine bytes for one object
+// first 3 bytes are the same as extended
+struct IRFull : IRExtended
+{
+ u8 xmin : 7;
+ u8 : 1;
+ u8 ymin : 7;
+ u8 : 1;
+ u8 xmax : 7;
+ u8 : 1;
+ u8 ymax : 7;
+ u8 : 1;
+ u8 zero;
+ u8 intensity;
+};
+static_assert(sizeof(IRFull) == 9, "Wrong size");
+
+class CameraLogic : public I2CSlave
+{
+public:
+ enum : u8
+ {
+ IR_MODE_BASIC = 1,
+ IR_MODE_EXTENDED = 3,
+ IR_MODE_FULL = 5,
+ };
+
+private:
+ // TODO: some of this memory is write-only and should return error 7.
+#pragma pack(push, 1)
+ struct Register
+ {
+ // Contains sensitivity and other unknown data
+ // TODO: Do the IR and Camera enabling reports write to the i2c bus?
+ // TODO: Does disabling the camera peripheral reset the mode or sensitivity?
+ // TODO: Break out this "data" array into some known members
+ u8 data[0x33];
+ u8 mode;
+ u8 unk[3];
+ // addr: 0x37
+ u8 camera_data[36];
+ u8 unk2[165];
+ };
+#pragma pack(pop)
+
+ static_assert(0x100 == sizeof(Register));
+
+public:
+ static constexpr u8 I2C_ADDR = 0x58;
+
+ // The real wiimote reads camera data from the i2c bus at offset 0x37:
+ static const u8 REPORT_DATA_OFFSET = offsetof(Register, camera_data);
+
+ void Reset();
+ void DoState(PointerWrap& p);
+ void Update(const ControllerEmu::Cursor::StateData& cursor, const NormalizedAccelData& accel,
+ bool sensor_bar_on_top);
+
+private:
+ Register reg_data;
+
+ int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
+ int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;
+};
+} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp b/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp
new file mode 100644
index 0000000000..47214ea46b
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp
@@ -0,0 +1,262 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Core/HW/WiimoteEmu/Dynamics.h"
+
+#include <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
diff --git a/Source/Core/Core/HW/WiimoteEmu/Dynamics.h b/Source/Core/Core/HW/WiimoteEmu/Dynamics.h
new file mode 100644
index 0000000000..116c03d912
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/Dynamics.h
@@ -0,0 +1,68 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <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
diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp
index 25f0726ded..cf71ba7b50 100644
--- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp
@@ -2,24 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-/* HID reports access guide. */
-
-/* 0x10 - 0x1a Output EmuMain.cpp: HidOutputReport()
- 0x10 - 0x14: General
- 0x15: Status report request from the Wii
- 0x16 and 0x17: Write and read memory or registers
- 0x19 and 0x1a: General
- 0x20 - 0x22 Input EmuMain.cpp: HidOutputReport() to the destination
- 0x15 leads to a 0x20 Input report
- 0x17 leads to a 0x21 Input report
- 0x10 - 0x1a leads to a 0x22 Input report
- 0x30 - 0x3f Input This file: Update() */
-
#include <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;
-/* 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.
+ SendAck(OutputReportID::REPORT_MODE, ErrorCode::SUCCESS);
+}
- The call browser:
+// 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)
+{
+ if (size < sizeof(T))
+ {
+ ERROR_LOG(WIIMOTE, "InvokeHandler: report: 0x%02x invalid size: %d", int(rpt.rpt_id), size);
+ }
- 1. Wiimote_InterruptChannel > InterruptChannel > HidOutputReport
- 2. Wiimote_ControlChannel > ControlChannel > HidOutputReport
+ (this->*handler)(*reinterpret_cast<const T*>(rpt.data));
+}
- 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)
+// 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)
{
- DEBUG_LOG(WIIMOTE, "HidOutputReport (page: %i, cid: 0x%02x, wm: 0x%02x)", m_index,
- m_reporting_channel, sr->wm);
+ 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?
+
+ u16 address = Common::swap16(wd.address);
- INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x @ 0x%02x (%d)", wd->space,
- wd->slave_address, address, wd->size);
+ 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)
+ 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);
-
- 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);
+ // 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_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
+ExtensionNumber Wiimote::GetActiveExtensionNumber() const
+{
+ return m_active_extension;
}
-// load real Wiimote state
void Wiimote::RealState()
{
using namespace WiimoteReal;
@@ -500,7 +534,7 @@ void Wiimote::RealState()
if (g_wiimotes[m_index])
{
g_wiimotes[m_index]->SetChannel(m_reporting_channel);
- g_wiimotes[m_index]->EnableDataReporting(m_reporting_mode);
}
}
+
} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/Encryption.cpp b/Source/Core/Core/HW/WiimoteEmu/Encryption.cpp
index 8e19716108..9d6a68225d 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Encryption.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/Encryption.cpp
@@ -241,8 +241,10 @@ static void GenerateTables(const u8* const rand, const u8* const key, const u8 i
sb[7] = sboxes[idx][rand[2]] ^ sboxes[(idx + 1) % 8][rand[6]];
}
-/* Generate key from the 0x40-0x4c data in g_RegExt */
-void WiimoteGenerateKey(wiimote_key* const key, const u8* const keydata)
+namespace WiimoteEmu
+{
+// Generate key from the 0x40-0x4c data in g_RegExt
+void EncryptionKey::Generate(const u8* const keydata)
{
u8 rand[10];
u8 skey[6];
@@ -262,22 +264,24 @@ void WiimoteGenerateKey(wiimote_key* const key, const u8* const keydata)
}
// default case is idx = 7 which is valid (homebrew uses it for the 0x17 case)
- GenerateTables(rand, skey, idx, key->ft, key->sb);
+ GenerateTables(rand, skey, idx, ft, sb);
// for homebrew, ft and sb are all 0x97 which is equivalent to 0x17
}
-// TODO: is there a reason these can only handle a length of 255?
-/* Encrypt data */
-void WiimoteEncrypt(const wiimote_key* const key, u8* const data, int addr, const u8 len)
+// Question: Is there a reason these can only handle a length of 255?
+// Answer: The i2c address space is only 8-bits so it really doesn't need to.
+// Also, only 21 bytes are ever encrypted at most (6 in any normal game).
+void EncryptionKey::Encrypt(u8* const data, int addr, const u8 len) const
{
for (int i = 0; i < len; ++i, ++addr)
- data[i] = (data[i] - key->ft[addr % 8]) ^ key->sb[addr % 8];
+ data[i] = (data[i] - ft[addr % 8]) ^ sb[addr % 8];
}
-/* Decrypt data */
-void WiimoteDecrypt(const wiimote_key* const key, u8* const data, int addr, const u8 len)
+void EncryptionKey::Decrypt(u8* const data, int addr, const u8 len) const
{
for (int i = 0; i < len; ++i, ++addr)
- data[i] = (data[i] ^ key->sb[addr % 8]) + key->ft[addr % 8];
+ data[i] = (data[i] ^ sb[addr % 8]) + ft[addr % 8];
}
+
+} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/Encryption.h b/Source/Core/Core/HW/WiimoteEmu/Encryption.h
index 5fcf6ff4f4..adac34427d 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Encryption.h
+++ b/Source/Core/Core/HW/WiimoteEmu/Encryption.h
@@ -8,14 +8,19 @@
#include "Common/CommonTypes.h"
-// The key structure to use with WiimoteGenerateKey()
-struct wiimote_key
+namespace WiimoteEmu
{
- u8 ft[8];
- u8 sb[8];
-};
+class EncryptionKey
+{
+public:
+ void Generate(const u8* keydata);
-void 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 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 WiimoteGenerateKey(wiimote_key* const key, const u8* const keydata);
+} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp
index 1cabaf60c4..aa6b7e9ccd 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp
@@ -2,7 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-#include "Core/HW/WiimoteEmu/Attachment/Classic.h"
+#include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include <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)
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.h
index 357ad9a1cc..ed144d83db 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Classic.h
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Classic.h
@@ -4,7 +4,8 @@
#pragma once
-#include "Core/HW/WiimoteEmu/Attachment/Attachment.h"
+#include "Core/HW/WiimoteCommon/WiimoteReport.h"
+#include "Core/HW/WiimoteEmu/Extension/Extension.h"
namespace ControllerEmu
{
@@ -16,15 +17,74 @@ class MixedTriggers;
namespace WiimoteEmu
{
-enum class ClassicGroup;
-struct ExtensionReg;
+enum class ClassicGroup
+{
+ Buttons,
+ Triggers,
+ DPad,
+ LeftStick,
+ RightStick
+};
-class Classic : public Attachment
+class Classic : public EncryptedExtension
{
public:
- explicit Classic(ExtensionReg& reg);
- void GetState(u8* const data) override;
+ union ButtonFormat
+ {
+ u16 hex;
+
+ struct
+ {
+ u8 : 1;
+ u8 rt : 1; // right trigger
+ u8 plus : 1;
+ u8 home : 1;
+ u8 minus : 1;
+ u8 lt : 1; // left trigger
+ u8 dpad_down : 1;
+ u8 dpad_right : 1;
+
+ u8 dpad_up : 1;
+ u8 dpad_left : 1;
+ u8 zr : 1;
+ u8 x : 1;
+ u8 a : 1;
+ u8 y : 1;
+ u8 b : 1;
+ u8 zl : 1; // left z button
+ };
+ };
+ static_assert(sizeof(ButtonFormat) == 2, "Wrong size");
+
+ struct DataFormat
+ {
+ // lx/ly/lz; left joystick
+ // rx/ry/rz; right joystick
+ // lt; left trigger
+ // rt; left trigger
+
+ u8 lx : 6; // byte 0
+ u8 rx3 : 2;
+
+ u8 ly : 6; // byte 1
+ u8 rx2 : 2;
+
+ u8 ry : 5;
+ u8 lt2 : 2;
+ u8 rx1 : 1;
+
+ u8 rt : 5;
+ u8 lt1 : 3;
+
+ ButtonFormat bt; // byte 4, 5
+ };
+ static_assert(sizeof(DataFormat) == 6, "Wrong size");
+
+ Classic();
+
+ void Update() override;
bool IsButtonPressed() const override;
+ void Reset() override;
ControllerEmu::ControlGroup* GetGroup(ClassicGroup group);
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp
index b32b767b01..976038711c 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp
@@ -2,7 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-#include "Core/HW/WiimoteEmu/Attachment/Drums.h"
+#include "Core/HW/WiimoteEmu/Extension/Drums.h"
#include <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)
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.h
index ea36869cb5..d76a7cc555 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Drums.h
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Drums.h
@@ -4,26 +4,54 @@
#pragma once
-#include "Core/HW/WiimoteEmu/Attachment/Attachment.h"
+#include "Core/HW/WiimoteEmu/Extension/Extension.h"
namespace ControllerEmu
{
class AnalogStick;
class Buttons;
class ControlGroup;
-}
+} // namespace ControllerEmu
namespace WiimoteEmu
{
-enum class DrumsGroup;
-struct ExtensionReg;
+enum class DrumsGroup
+{
+ Buttons,
+ Pads,
+ Stick
+};
-class Drums : public Attachment
+// TODO: Do the drums ever use encryption?
+class Drums : public EncryptedExtension
{
public:
- explicit Drums(ExtensionReg& reg);
- void GetState(u8* const data) override;
+ struct DataFormat
+ {
+ u8 sx : 6;
+ u8 pad1 : 2; // always 0
+
+ u8 sy : 6;
+ u8 pad2 : 2; // always 0
+
+ u8 pad3 : 1; // unknown
+ u8 which : 5;
+ u8 none : 1;
+ u8 hhp : 1;
+
+ u8 pad4 : 1; // unknown
+ u8 velocity : 4; // unknown
+ u8 softness : 3;
+
+ u16 bt; // buttons
+ };
+ static_assert(sizeof(DataFormat) == 6, "Wrong size");
+
+ Drums();
+
+ void Update() override;
bool IsButtonPressed() const override;
+ void Reset() override;
ControllerEmu::ControlGroup* GetGroup(DrumsGroup group);
@@ -51,4 +79,4 @@ private:
ControllerEmu::Buttons* m_pads;
ControllerEmu::AnalogStick* m_stick;
};
-}
+} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp
new file mode 100644
index 0000000000..d6208b2935
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp
@@ -0,0 +1,129 @@
+// Copyright 2010 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Core/HW/WiimoteEmu/Extension/Extension.h"
+
+#include <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
diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h
new file mode 100644
index 0000000000..b32b1367bd
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h
@@ -0,0 +1,112 @@
+// Copyright 2010 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "Core/HW/WiimoteEmu/Extension/Extension.h"
+
+#include <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
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp
index 8b3d447376..b3d5ab286b 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp
@@ -2,7 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-#include "Core/HW/WiimoteEmu/Attachment/Guitar.h"
+#include "Core/HW/WiimoteEmu/Extension/Guitar.h"
#include <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)
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.h
index 8964221511..0c34c80955 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Guitar.h
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.h
@@ -4,7 +4,7 @@
#pragma once
-#include "Core/HW/WiimoteEmu/Attachment/Attachment.h"
+#include "Core/HW/WiimoteEmu/Extension/Extension.h"
namespace ControllerEmu
{
@@ -13,19 +13,47 @@ class Buttons;
class ControlGroup;
class Triggers;
class Slider;
-}
+} // namespace ControllerEmu
namespace WiimoteEmu
{
-enum class GuitarGroup;
-struct ExtensionReg;
+enum class GuitarGroup
+{
+ Buttons,
+ Frets,
+ Strum,
+ Whammy,
+ Stick,
+ SliderBar
+};
-class Guitar : public Attachment
+// TODO: Does the guitar ever use encryption?
+class Guitar : public EncryptedExtension
{
public:
- explicit Guitar(ExtensionReg& reg);
- void GetState(u8* const data) override;
+ struct DataFormat
+ {
+ u8 sx : 6;
+ u8 pad1 : 2; // 1 on gh3, 0 on ghwt
+
+ u8 sy : 6;
+ u8 pad2 : 2; // 1 on gh3, 0 on ghwt
+
+ u8 sb : 5; // not used in gh3
+ u8 pad3 : 3; // always 0
+
+ u8 whammy : 5;
+ u8 pad4 : 3; // always 0
+
+ u16 bt; // buttons
+ };
+ static_assert(sizeof(DataFormat) == 6, "Wrong size");
+
+ Guitar();
+
+ void Update() override;
bool IsButtonPressed() const override;
+ void Reset() override;
ControllerEmu::ControlGroup* GetGroup(GuitarGroup group);
@@ -57,4 +85,4 @@ private:
ControllerEmu::AnalogStick* m_stick;
ControllerEmu::Slider* m_slider_bar;
};
-}
+} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp
index accc64bc74..91dd8bf176 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp
@@ -2,7 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-#include "Core/HW/WiimoteEmu/Attachment/Nunchuk.h"
+#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
#include <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,42 +68,12 @@ 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;
-
- // Build calibration data:
- m_calibration = {{
- // Accel Zero X,Y,Z:
- ACCEL_ZERO_G,
- ACCEL_ZERO_G,
- ACCEL_ZERO_G,
- // Possibly LSBs of zero values:
- 0x00,
- // Accel 1G X,Y,Z:
- ACCEL_ONE_G,
- ACCEL_ONE_G,
- ACCEL_ONE_G,
- // Possibly LSBs of 1G values:
- 0x00,
- // Stick X max,min,center:
- STICK_CENTER + STICK_RADIUS,
- STICK_CENTER - STICK_RADIUS,
- STICK_CENTER,
- // Stick Y max,min,center:
- STICK_CENTER + STICK_RADIUS,
- STICK_CENTER - STICK_RADIUS,
- STICK_CENTER,
- // 2 checksum bytes calculated below:
- 0x00,
- 0x00,
- }};
-
- UpdateCalibrationDataChecksum(m_calibration);
}
-void Nunchuk::GetState(u8* const data)
+void Nunchuk::Update()
{
- wm_nc nc_data = {};
+ auto& nc_data = *reinterpret_cast<DataFormat*>(&m_reg.controller_data);
+ nc_data = {};
// stick
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState();
@@ -126,7 +96,7 @@ void Nunchuk::GetState(u8* const data)
++nc_data.jy;
}
- AccelData accel;
+ NormalizedAccelData accel;
// tilt
EmulateTilt(&accel, m_tilt);
@@ -150,23 +120,15 @@ void Nunchuk::GetState(u8* const 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;
+ // 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);
- std::memcpy(data, &nc_data, sizeof(wm_nc));
+ 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
@@ -176,6 +138,41 @@ bool Nunchuk::IsButtonPressed() const
return buttons != 0;
}
+void Nunchuk::Reset()
+{
+ m_reg = {};
+ m_reg.identifier = nunchuk_id;
+
+ // Build calibration data:
+ m_reg.calibration = {{
+ // Accel Zero X,Y,Z:
+ ACCEL_ZERO_G,
+ ACCEL_ZERO_G,
+ ACCEL_ZERO_G,
+ // Possibly LSBs of zero values:
+ 0x00,
+ // Accel 1G X,Y,Z:
+ ACCEL_ONE_G,
+ ACCEL_ONE_G,
+ ACCEL_ONE_G,
+ // Possibly LSBs of 1G values:
+ 0x00,
+ // Stick X max,min,center:
+ STICK_CENTER + STICK_RADIUS,
+ STICK_CENTER - STICK_RADIUS,
+ STICK_CENTER,
+ // Stick Y max,min,center:
+ STICK_CENTER + STICK_RADIUS,
+ STICK_CENTER - STICK_RADIUS,
+ STICK_CENTER,
+ // 2 checksum bytes calculated below:
+ 0x00,
+ 0x00,
+ }};
+
+ UpdateCalibrationDataChecksum(m_reg.calibration, CALIBRATION_CHECKSUM_BYTES);
+}
+
ControllerEmu::ControlGroup* Nunchuk::GetGroup(NunchukGroup group)
{
switch (group)
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.h
index 9b36cac1b2..22c66f5263 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.h
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.h
@@ -5,7 +5,9 @@
#pragma once
#include <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;
+
+ 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");
- void GetState(u8* const data) override;
+ union DataFormat
+ {
+ struct
+ {
+ // joystick x, y
+ u8 jx;
+ u8 jy;
+
+ // accelerometer
+ u8 ax;
+ u8 ay;
+ u8 az;
+
+ // buttons + accelerometer LSBs
+ ButtonFormat bt;
+ };
+ };
+ static_assert(sizeof(DataFormat) == 6, "Wrong size");
+
+ Nunchuk();
+
+ void Update() override;
bool IsButtonPressed() const override;
+ void Reset() override;
ControllerEmu::ControlGroup* GetGroup(NunchukGroup group);
@@ -41,7 +86,6 @@ public:
{
ACCEL_ZERO_G = 0x80,
ACCEL_ONE_G = 0xB3,
- ACCEL_RANGE = (ACCEL_ONE_G - ACCEL_ZERO_G),
};
enum
@@ -55,6 +99,7 @@ public:
private:
ControllerEmu::Tilt* m_tilt;
+
ControllerEmu::Force* m_swing;
ControllerEmu::Force* m_swing_slow;
ControllerEmu::Force* m_swing_fast;
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp
index 76eef2ab2a..2871a73339 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp
@@ -2,7 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-#include "Core/HW/WiimoteEmu/Attachment/Turntable.h"
+#include "Core/HW/WiimoteEmu/Extension/Turntable.h"
#include <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)
diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.h
index 8d2a0b0988..158f3cdd73 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Turntable.h
+++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.h
@@ -4,7 +4,7 @@
#pragma once
-#include "Core/HW/WiimoteEmu/Attachment/Attachment.h"
+#include "Core/HW/WiimoteEmu/Extension/Extension.h"
namespace ControllerEmu
{
@@ -13,19 +13,53 @@ class Buttons;
class ControlGroup;
class Slider;
class Triggers;
-}
+} // namespace ControllerEmu
namespace WiimoteEmu
{
-enum class TurntableGroup;
-struct ExtensionReg;
+enum class TurntableGroup
+{
+ Buttons,
+ Stick,
+ EffectDial,
+ LeftTable,
+ RightTable,
+ Crossfade
+};
-class Turntable : public Attachment
+// TODO: Does the turntable ever use encryption?
+class Turntable : public EncryptedExtension
{
public:
- explicit Turntable(ExtensionReg& reg);
- void GetState(u8* const data) override;
+ struct DataFormat
+ {
+ u8 sx : 6;
+ u8 rtable3 : 2;
+
+ u8 sy : 6;
+ u8 rtable2 : 2;
+
+ u8 rtable4 : 1;
+ u8 slider : 4;
+ u8 dial2 : 2;
+ u8 rtable1 : 1;
+
+ u8 ltable1 : 5;
+ u8 dial1 : 3;
+
+ union
+ {
+ u16 ltable2 : 1;
+ u16 bt; // buttons
+ };
+ };
+ static_assert(sizeof(DataFormat) == 6, "Wrong size");
+
+ Turntable();
+
+ void Update() override;
bool IsButtonPressed() const override;
+ void Reset() override;
ControllerEmu::ControlGroup* GetGroup(TurntableGroup group);
@@ -59,4 +93,4 @@ private:
ControllerEmu::Slider* m_right_table;
ControllerEmu::Slider* m_crossfade;
};
-}
+} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp
new file mode 100644
index 0000000000..8a9c6de766
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp
@@ -0,0 +1,29 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Core/HW/WiimoteEmu/ExtensionPort.h"
+
+#include "Common/ChunkFile.h"
+
+namespace WiimoteEmu
+{
+ExtensionPort::ExtensionPort(I2CBus* i2c_bus) : m_i2c_bus(*i2c_bus)
+{
+}
+
+bool ExtensionPort::IsDeviceConnected() const
+{
+ return m_extension->ReadDeviceDetectPin();
+}
+
+void ExtensionPort::AttachExtension(Extension* ext)
+{
+ m_i2c_bus.RemoveSlave(m_extension);
+
+ m_extension = ext;
+ m_i2c_bus.AddSlave(m_extension);
+ ;
+}
+
+} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h
new file mode 100644
index 0000000000..13dcc0025d
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h
@@ -0,0 +1,43 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "Common/ChunkFile.h"
+#include "Core/HW/WiimoteEmu/Extension/Extension.h"
+#include "Core/HW/WiimoteEmu/I2CBus.h"
+
+namespace WiimoteEmu
+{
+enum ExtensionNumber : u8
+{
+ NONE,
+
+ NUNCHUK,
+ CLASSIC,
+ GUITAR,
+ DRUMS,
+ TURNTABLE,
+};
+
+// FYI: An extension must be attached.
+// Attach "None" for no extension.
+class ExtensionPort
+{
+public:
+ // The real wiimote reads extension data from i2c slave 0x52 addres 0x00:
+ static constexpr u8 REPORT_I2C_SLAVE = 0x52;
+ static constexpr u8 REPORT_I2C_ADDR = 0x00;
+
+ ExtensionPort(I2CBus* i2c_bus);
+
+ bool IsDeviceConnected() const;
+ void AttachExtension(Extension* dev);
+
+private:
+ Extension* m_extension = nullptr;
+ I2CBus& m_i2c_bus;
+};
+
+} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp b/Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp
new file mode 100644
index 0000000000..ee5c3b4325
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp
@@ -0,0 +1,56 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Core/HW/WiimoteEmu/I2CBus.h"
+
+#include <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
diff --git a/Source/Core/Core/HW/WiimoteEmu/I2CBus.h b/Source/Core/Core/HW/WiimoteEmu/I2CBus.h
new file mode 100644
index 0000000000..f20719af32
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/I2CBus.h
@@ -0,0 +1,76 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <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
diff --git a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp
new file mode 100644
index 0000000000..6734c32e53
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.cpp
@@ -0,0 +1,376 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Core/HW/WiimoteEmu/MotionPlus.h"
+
+#include "Common/BitUtils.h"
+#include "Common/ChunkFile.h"
+#include "Common/Logging/Log.h"
+#include "Common/MsgHandler.h"
+
+namespace WiimoteEmu
+{
+MotionPlus::MotionPlus() : Extension("MotionPlus")
+{
+}
+
+void MotionPlus::Reset()
+{
+ reg_data = {};
+
+ constexpr std::array<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(&reg_data, addr, count, data_out);
+ else
+ return 0;
+ }
+ else
+ {
+ if (INACTIVE_DEVICE_ADDR == slave_addr)
+ return RawRead(&reg_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(&reg_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(&reg_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
diff --git a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h
new file mode 100644
index 0000000000..cf1b1bfa7e
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h
@@ -0,0 +1,131 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <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
diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp
index 4f99a92ee7..eda359c9d4 100644
--- a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
+#include "Core/HW/WiimoteEmu/Speaker.h"
+
#include <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(&reg_data, addr, count, data_out);
+}
+
+int SpeakerLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
+{
+ if (I2C_ADDR != slave_addr)
+ return 0;
+
+ if (0x00 == addr)
+ {
+ ERROR_LOG(WIIMOTE, "Writing of speaker data to address 0x00 is unimplemented!");
+ return count;
+ }
+ else
+ {
+ // TODO: Does writing immediately change the decoder config even when active
+ // or does a write to 0x08 activate the new configuration or something?
+ return RawWrite(&reg_data, addr, count, data_in);
+ }
+}
+
} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.h b/Source/Core/Core/HW/WiimoteEmu/Speaker.h
new file mode 100644
index 0000000000..80b5e217d0
--- /dev/null
+++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.h
@@ -0,0 +1,67 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "Common/ChunkFile.h"
+#include "Common/CommonTypes.h"
+#include "Core/HW/WiimoteEmu/I2CBus.h"
+
+namespace WiimoteEmu
+{
+struct ADPCMState
+{
+ s32 predictor, step;
+};
+
+class SpeakerLogic : public I2CSlave
+{
+public:
+ static const u8 I2C_ADDR = 0x51;
+
+ void Reset();
+ void DoState(PointerWrap& p);
+
+ void SpeakerData(const u8* data, int length, int speaker_pan);
+
+private:
+ // TODO: enum class
+ static const u8 DATA_FORMAT_ADPCM = 0x00;
+ static const u8 DATA_FORMAT_PCM = 0x40;
+
+ // TODO: It seems reading address 0x00 should always return 0xff.
+#pragma pack(push, 1)
+ struct Register
+ {
+ // Speaker reports result in a write of samples to addr 0x00 (which also plays sound)
+ u8 speaker_data;
+ u8 unk_1;
+ u8 format;
+ // seems to always play at 6khz no matter what this is set to?
+ // or maybe it only applies to pcm input
+ // Little-endian:
+ u16 sample_rate;
+ u8 volume;
+ u8 unk_5;
+ u8 unk_6;
+ // Reading this byte on real hardware seems to return 0x09:
+ u8 unk_7;
+ u8 unk_8;
+ u8 unknown[0xf6];
+ };
+#pragma pack(pop)
+
+ static_assert(0x100 == sizeof(Register));
+
+ int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
+ int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;
+
+ Register reg_data;
+
+ // TODO: What actions reset this state?
+ // Is this actually in the register somewhere?
+ ADPCMState adpcm_state;
+};
+
+} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp
index 39c22d3271..a7ea01b3dd 100644
--- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp
@@ -5,18 +5,14 @@
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include <algorithm>
-#include <array>
#include <cassert>
-#include <cmath>
-#include <cstddef>
-#include <cstring>
-#include <mutex>
-#include <numeric>
+#include <memory>
#include "Common/BitUtils.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
+#include "Common/Logging/Log.h"
#include "Common/MathUtil.h"
#include "Common/MsgHandler.h"
@@ -24,25 +20,24 @@
#include "Core/Config/WiimoteInputSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
-#include "Core/HW/Wiimote.h"
+#include "Core/Movie.h"
+#include "Core/NetPlayClient.h"
+
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
-#include "Core/HW/WiimoteEmu/Attachment/Classic.h"
-#include "Core/HW/WiimoteEmu/Attachment/Drums.h"
-#include "Core/HW/WiimoteEmu/Attachment/Guitar.h"
-#include "Core/HW/WiimoteEmu/Attachment/Nunchuk.h"
-#include "Core/HW/WiimoteEmu/Attachment/Turntable.h"
-#include "Core/HW/WiimoteEmu/MatrixMath.h"
+#include "Core/HW/WiimoteEmu/Extension/Classic.h"
+#include "Core/HW/WiimoteEmu/Extension/Drums.h"
+#include "Core/HW/WiimoteEmu/Extension/Guitar.h"
+#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
+#include "Core/HW/WiimoteEmu/Extension/Turntable.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
-#include "Core/Movie.h"
-#include "Core/NetPlayClient.h"
#include "InputCommon/ControllerEmu/Control/Input.h"
#include "InputCommon/ControllerEmu/Control/Output.h"
+#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
-#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
#include "InputCommon/ControllerEmu/ControlGroup/Force.h"
#include "InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.h"
#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h"
@@ -51,246 +46,7 @@
namespace WiimoteEmu
{
-constexpr int SHAKE_FREQ = 6;
-// Frame count of one up/down shake
-// < 9 no shake detection in "Wario Land: Shake It"
-constexpr int SHAKE_STEP_MAX = ::Wiimote::UPDATE_FREQ / SHAKE_FREQ;
-
-// clang-format off
-static const u8 eeprom_data_0[] = {
- // IR, maybe more
- // assuming last 2 bytes are checksum
- // messing up the checksum on purpose
- 0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, 0x00,
- 0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, 0x00,
- // Accelerometer
- // Important: checksum is required for tilt games
- ACCEL_ZERO_G, ACCEL_ZERO_G, ACCEL_ZERO_G, 0, ACCEL_ONE_G, ACCEL_ONE_G, ACCEL_ONE_G, 0, 0, 0xA3,
- ACCEL_ZERO_G, ACCEL_ZERO_G, ACCEL_ZERO_G, 0, ACCEL_ONE_G, ACCEL_ONE_G, ACCEL_ONE_G, 0, 0, 0xA3,
-};
-// clang-format on
-
-static const u8 motion_plus_id[] = {0x00, 0x00, 0xA6, 0x20, 0x00, 0x05};
-
-static const u8 eeprom_data_16D0[] = {0x00, 0x00, 0x00, 0xFF, 0x11, 0xEE, 0x00, 0x00,
- 0x33, 0xCC, 0x44, 0xBB, 0x00, 0x00, 0x66, 0x99,
- 0x77, 0x88, 0x00, 0x00, 0x2B, 0x01, 0xE8, 0x13};
-
-// Counts are how many bytes of each feature are in a particular report
-static const ReportFeatures reporting_mode_features[] = {
- // 0x30: Core Buttons
- {2, 0, 0, 0, 4},
- // 0x31: Core Buttons and Accelerometer
- {2, 3, 0, 0, 7},
- // 0x32: Core Buttons with 8 Extension bytes
- {2, 0, 0, 8, 12},
- // 0x33: Core Buttons and Accelerometer with 12 IR bytes
- {2, 3, 12, 0, 19},
- // 0x34: Core Buttons with 19 Extension bytes
- {2, 0, 0, 19, 23},
- // 0x35: Core Buttons and Accelerometer with 16 Extension Bytes
- {2, 3, 0, 16, 23},
- // 0x36: Core Buttons with 10 IR bytes and 9 Extension Bytes
- {2, 0, 10, 9, 23},
- // 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes
- {2, 3, 10, 6, 23},
- // 0x38 - 0x3c: Nothing
- {0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0},
- // 0x3d: 21 Extension Bytes
- {0, 0, 0, 21, 23},
- // 0x3e - 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes
- {2, 1, 0, 18, 23},
- {2, 1, 0, 18, 23},
-};
-
-// Used for extension calibration data:
-void UpdateCalibrationDataChecksum(std::array<u8, 0x10>& data)
-{
- // Last two bytes are the sum of the previous bytes plus 0x55 and 0xaa (0x55 + 0x55)
- const u8 checksum1 = std::accumulate(std::begin(data), std::end(data) - 2, u8(0x55));
- const u8 checksum2 = checksum1 + 0x55;
-
- data[0xe] = checksum1;
- data[0xf] = checksum2;
-}
-
-void EmulateShake(AccelData* const accel, ControllerEmu::Buttons* const buttons_group,
- const double intensity, u8* const shake_step)
-{
- // shake is a bitfield of X,Y,Z shake button states
- static const unsigned int btns[] = {0x01, 0x02, 0x04};
- unsigned int shake = 0;
- buttons_group->GetState(&shake, btns);
-
- for (int i = 0; i != 3; ++i)
- {
- if (shake & (1 << i))
- {
- (&(accel->x))[i] += std::sin(MathUtil::TAU * shake_step[i] / SHAKE_STEP_MAX) * intensity;
- shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX;
- }
- else
- shake_step[i] = 0;
- }
-}
-
-void EmulateDynamicShake(AccelData* const accel, DynamicData& dynamic_data,
- ControllerEmu::Buttons* const buttons_group,
- const DynamicConfiguration& config, u8* const shake_step)
-{
- // shake is a bitfield of X,Y,Z shake button states
- static const unsigned int btns[] = {0x01, 0x02, 0x04};
- unsigned int shake = 0;
- buttons_group->GetState(&shake, btns);
-
- for (int i = 0; i != 3; ++i)
- {
- if ((shake & (1 << i)) && dynamic_data.executing_frames_left[i] == 0)
- {
- dynamic_data.timing[i]++;
- }
- else if (dynamic_data.executing_frames_left[i] > 0)
- {
- (&(accel->x))[i] +=
- std::sin(MathUtil::TAU * shake_step[i] / SHAKE_STEP_MAX) * dynamic_data.intensity[i];
- shake_step[i] = (shake_step[i] + 1) % SHAKE_STEP_MAX;
- dynamic_data.executing_frames_left[i]--;
- }
- else if (shake == 0 && dynamic_data.timing[i] > 0)
- {
- if (dynamic_data.timing[i] > config.frames_needed_for_high_intensity)
- {
- dynamic_data.intensity[i] = config.high_intensity;
- }
- else if (dynamic_data.timing[i] < config.frames_needed_for_low_intensity)
- {
- dynamic_data.intensity[i] = config.low_intensity;
- }
- else
- {
- dynamic_data.intensity[i] = config.med_intensity;
- }
- dynamic_data.timing[i] = 0;
- dynamic_data.executing_frames_left[i] = config.frames_to_execute;
- }
- else
- {
- shake_step[i] = 0;
- }
- }
-}
-
-void EmulateTilt(AccelData* const accel, ControllerEmu::Tilt* const tilt_group, const bool sideways,
- const bool upright)
-{
- // 180 degrees
- const ControllerEmu::Tilt::StateData state = tilt_group->GetState();
- const ControlState roll = state.x * MathUtil::PI;
- const ControlState pitch = state.y * MathUtil::PI;
-
- // Some notes that no one will understand but me :p
- // left, forward, up
- // lr/ left == negative for all orientations
- // ud/ up == negative for upright longways
- // fb/ forward == positive for (sideways flat)
-
- // Determine which axis is which direction
- const u32 ud = upright ? (sideways ? 0 : 1) : 2;
- const u32 lr = sideways;
- const u32 fb = upright ? 2 : (sideways ? 0 : 1);
-
- // Sign fix
- std::array<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(AccelData* const accel, ControllerEmu::Force* const swing_group,
- const double intensity, const bool sideways, const bool upright)
-{
- const ControllerEmu::Force::StateData swing = swing_group->GetState();
-
- // Determine which axis is which direction
- const std::array<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(AccelData* const accel, DynamicData& dynamic_data,
- ControllerEmu::Force* const swing_group,
- const DynamicConfiguration& config, const bool sideways,
- const bool upright)
-{
- const ControllerEmu::Force::StateData swing = swing_group->GetState();
-
- // Determine which axis is which direction
- const std::array<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;
- }
- }
-}
+using namespace WiimoteCommon;
static const u16 button_bitmasks[] = {
Wiimote::BUTTON_A, Wiimote::BUTTON_B, Wiimote::BUTTON_ONE, Wiimote::BUTTON_TWO,
@@ -298,6 +54,7 @@ static const u16 button_bitmasks[] = {
static const u16 dpad_bitmasks[] = {Wiimote::PAD_UP, Wiimote::PAD_DOWN, Wiimote::PAD_LEFT,
Wiimote::PAD_RIGHT};
+
static const u16 dpad_sideways_bitmasks[] = {Wiimote::PAD_RIGHT, Wiimote::PAD_LEFT, Wiimote::PAD_UP,
Wiimote::PAD_DOWN};
@@ -307,93 +64,84 @@ static const char* const named_buttons[] = {
void Wiimote::Reset()
{
+ // TODO: This value should be re-read if SYSCONF gets changed.
+ m_sensor_bar_on_top = Config::Get(Config::SYSCONF_SENSOR_BAR_POSITION) != 0;
+
+ SetRumble(false);
+
// Wiimote starts in non-continuous CORE mode:
- m_reporting_mode = RT_REPORT_CORE;
m_reporting_channel = 0;
- m_reporting_auto = false;
+ m_reporting_mode = InputReportID::REPORT_CORE;
+ m_reporting_continuous = false;
- m_rumble_on = false;
m_speaker_mute = false;
- // TODO: Can probably just set this to the desired extension right away?
- m_extension->active_extension = 0;
-
- // eeprom
- memset(m_eeprom, 0, sizeof(m_eeprom));
- // calibration data
- memcpy(m_eeprom, eeprom_data_0, sizeof(eeprom_data_0));
- // dunno what this is for, copied from old plugin
- memcpy(m_eeprom + 0x16D0, eeprom_data_16D0, sizeof(eeprom_data_16D0));
+ // EEPROM
+ m_eeprom = {};
- // set up the register
+ // IR calibration and maybe more unknown purposes:
+ // The meaning of this data needs more investigation.
+ // Last byte is a checksum.
+ std::array<u8, 11> ir_calibration = {
+ 0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, 0x74, 0x00,
+ };
+ // Purposely not updating checksum because using this data results in a weird IR offset..
+ // UpdateCalibrationDataChecksum(ir_calibration, 1);
+ m_eeprom.ir_calibration_1 = ir_calibration;
+ m_eeprom.ir_calibration_2 = ir_calibration;
+
+ // Accel calibration:
+ // Last byte is a checksum.
+ std::array<u8, 10> accel_calibration = {
+ ACCEL_ZERO_G, ACCEL_ZERO_G, ACCEL_ZERO_G, 0, ACCEL_ONE_G, ACCEL_ONE_G, ACCEL_ONE_G, 0, 0, 0,
+ };
+ UpdateCalibrationDataChecksum(accel_calibration, 1);
+ m_eeprom.accel_calibration_1 = accel_calibration;
+ m_eeprom.accel_calibration_2 = accel_calibration;
- // TODO: kill/move these
- memset(&m_speaker_logic.reg_data, 0, sizeof(m_speaker_logic.reg_data));
- memset(&m_camera_logic.reg_data, 0, sizeof(m_camera_logic.reg_data));
- memset(&m_ext_logic.reg_data, 0, sizeof(m_ext_logic.reg_data));
+ // Data of unknown purpose:
+ constexpr std::array<u8, 24> EEPROM_DATA_16D0 = {0x00, 0x00, 0x00, 0xFF, 0x11, 0xEE, 0x00, 0x00,
+ 0x33, 0xCC, 0x44, 0xBB, 0x00, 0x00, 0x66, 0x99,
+ 0x77, 0x88, 0x00, 0x00, 0x2B, 0x01, 0xE8, 0x13};
+ m_eeprom.unk_2 = EEPROM_DATA_16D0;
- memset(&m_motion_plus_logic.reg_data, 0x00, sizeof(m_motion_plus_logic.reg_data));
- memcpy(&m_motion_plus_logic.reg_data.ext_identifier, motion_plus_id, sizeof(motion_plus_id));
+ m_read_request = {};
- // TODO: determine meaning of calibration data:
- static const u8 mplus_cdata[32] = {
- 0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, 0xcf, 0xf0, 0x31,
- 0xad, 0xc8, 0x0b, 0x5e, 0x39, 0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51,
- 0x33, 0x60, 0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d,
- };
+ // Initialize i2c bus:
+ m_i2c_bus.Reset();
+ m_i2c_bus.AddSlave(&m_speaker_logic);
+ m_i2c_bus.AddSlave(&m_camera_logic);
- std::copy(std::begin(mplus_cdata), std::end(mplus_cdata),
- m_motion_plus_logic.reg_data.calibration_data);
+ // FYI: AttachExtension also connects devices to the i2c bus
+ // TODO: Only attach M+ when enabled in settings.
+ m_extension_port.AttachExtension(&m_motion_plus);
- // TODO: determine the meaning behind this:
- static const u8 mp_cert[64] = {
- 0x99, 0x1a, 0x07, 0x1b, 0x97, 0xf1, 0x11, 0x78, 0x0c, 0x42, 0x2b, 0x68, 0xdf,
- 0x44, 0x38, 0x0d, 0x2b, 0x7e, 0xd6, 0x84, 0x84, 0x58, 0x65, 0xc9, 0xf2, 0x95,
- 0xd9, 0xaf, 0xb6, 0xc4, 0x87, 0xd5, 0x18, 0xdb, 0x67, 0x3a, 0xc0, 0x71, 0xec,
- 0x3e, 0xf4, 0xe6, 0x7e, 0x35, 0xa3, 0x29, 0xf8, 0x1f, 0xc5, 0x7c, 0x3d, 0xb9,
- 0x56, 0x22, 0x95, 0x98, 0x8f, 0xfb, 0x66, 0x3e, 0x9a, 0xdd, 0xeb, 0x7e,
- };
+ m_active_extension = ExtensionNumber::NONE;
+ m_motion_plus.AttachExtension(GetActiveExtension());
+ // Switch to the desired extension (if any).
+ HandleExtensionSwap();
- std::copy(std::begin(mp_cert), std::end(mp_cert), m_motion_plus_logic.reg_data.cert_data);
+ // Reset sub-devices:
+ m_speaker_logic.Reset();
+ m_camera_logic.Reset();
+ m_motion_plus.Reset();
+ GetActiveExtension()->Reset();
- // status
- memset(&m_status, 0, sizeof(m_status));
+ m_status = {};
+ // TODO: This will suppress a status report on connect when an extension is already attached.
+ // I am not 100% sure if this is proper.
+ m_status.extension = m_extension_port.IsDeviceConnected();
+ // Dynamics:
m_shake_step = {};
m_shake_soft_step = {};
m_shake_hard_step = {};
m_swing_dynamic_data = {};
m_shake_dynamic_data = {};
-
- m_read_request.size = 0;
-
- // Yamaha ADPCM state initialize
- m_speaker_logic.adpcm_state.predictor = 0;
- m_speaker_logic.adpcm_state.step = 127;
-
- // Initialize i2c bus
- m_i2c_bus.Reset();
- // Address 0x51
- m_i2c_bus.AddSlave(&m_speaker_logic);
- // Address 0x58
- m_i2c_bus.AddSlave(&m_camera_logic);
-
- // TODO: only add to bus when enabled
- // This also adds the motion plus to the i2c bus
- // Address 0x53 (or 0x52 when activated)
- m_extension_port.SetAttachment(&m_motion_plus_logic);
-
- // TODO: add directly to wiimote bus when mplus is disabled
- // TODO: only add to bus when connected:
- // Address 0x52 (when motion plus is not activated)
- // Connected to motion plus i2c_bus (with passthrough by default)
- // m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic);
}
-Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1)
+Wiimote::Wiimote(const unsigned int index) : m_index(index)
{
- // ---- set up all the controls ----
-
// buttons
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons")));
for (const char* named_button : named_buttons)
@@ -444,14 +192,13 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1
new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z"));
// extension
- groups.emplace_back(m_extension = new ControllerEmu::Extension(_trans("Extension")));
- m_ext_logic.extension = m_extension;
- m_extension->attachments.emplace_back(new WiimoteEmu::None(m_ext_logic.reg_data));
- m_extension->attachments.emplace_back(new WiimoteEmu::Nunchuk(m_ext_logic.reg_data));
- m_extension->attachments.emplace_back(new WiimoteEmu::Classic(m_ext_logic.reg_data));
- m_extension->attachments.emplace_back(new WiimoteEmu::Guitar(m_ext_logic.reg_data));
- m_extension->attachments.emplace_back(new WiimoteEmu::Drums(m_ext_logic.reg_data));
- m_extension->attachments.emplace_back(new WiimoteEmu::Turntable(m_ext_logic.reg_data));
+ groups.emplace_back(m_attachments = new ControllerEmu::Attachments(_trans("Extension")));
+ m_attachments->AddAttachment(std::make_unique<WiimoteEmu::None>());
+ m_attachments->AddAttachment(std::make_unique<WiimoteEmu::Nunchuk>());
+ m_attachments->AddAttachment(std::make_unique<WiimoteEmu::Classic>());
+ m_attachments->AddAttachment(std::make_unique<WiimoteEmu::Guitar>());
+ m_attachments->AddAttachment(std::make_unique<WiimoteEmu::Drums>());
+ m_attachments->AddAttachment(std::make_unique<WiimoteEmu::Turntable>());
// rumble
groups.emplace_back(m_rumble = new ControllerEmu::ControlGroup(_trans("Rumble")));
@@ -493,16 +240,12 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1
m_hotkeys->AddInput(_trans("Sideways Hold"), false);
m_hotkeys->AddInput(_trans("Upright Hold"), false);
- // TODO: This value should probably be re-read if SYSCONF gets changed
- m_sensor_bar_on_top = Config::Get(Config::SYSCONF_SENSOR_BAR_POSITION) != 0;
-
- // --- reset eeprom/register/values to default ---
Reset();
}
std::string Wiimote::GetName() const
{
- return std::string("Wiimote") + char('1' + m_index);
+ return StringFromFormat("Wiimote%d", 1 + m_index);
}
ControllerEmu::ControlGroup* Wiimote::GetWiimoteGroup(WiimoteGroup group)
@@ -523,8 +266,8 @@ ControllerEmu::ControlGroup* Wiimote::GetWiimoteGroup(WiimoteGroup group)
return m_swing;
case WiimoteGroup::Rumble:
return m_rumble;
- case WiimoteGroup::Extension:
- return m_extension;
+ case WiimoteGroup::Attachments:
+ return m_attachments;
case WiimoteGroup::Options:
return m_options;
case WiimoteGroup::Hotkeys:
@@ -537,510 +280,220 @@ ControllerEmu::ControlGroup* Wiimote::GetWiimoteGroup(WiimoteGroup group)
ControllerEmu::ControlGroup* Wiimote::GetNunchukGroup(NunchukGroup group)
{
- return static_cast<Nunchuk*>(m_extension->attachments[EXT_NUNCHUK].get())->GetGroup(group);
+ return static_cast<Nunchuk*>(m_attachments->GetAttachmentList()[ExtensionNumber::NUNCHUK].get())
+ ->GetGroup(group);
}
ControllerEmu::ControlGroup* Wiimote::GetClassicGroup(ClassicGroup group)
{
- return static_cast<Classic*>(m_extension->attachments[EXT_CLASSIC].get())->GetGroup(group);
+ return static_cast<Classic*>(m_attachments->GetAttachmentList()[ExtensionNumber::CLASSIC].get())
+ ->GetGroup(group);
}
ControllerEmu::ControlGroup* Wiimote::GetGuitarGroup(GuitarGroup group)
{
- return static_cast<Guitar*>(m_extension->attachments[EXT_GUITAR].get())->GetGroup(group);
+ return static_cast<Guitar*>(m_attachments->GetAttachmentList()[ExtensionNumber::GUITAR].get())
+ ->GetGroup(group);
}
ControllerEmu::ControlGroup* Wiimote::GetDrumsGroup(DrumsGroup group)
{
- return static_cast<Drums*>(m_extension->attachments[EXT_DRUMS].get())->GetGroup(group);
+ return static_cast<Drums*>(m_attachments->GetAttachmentList()[ExtensionNumber::DRUMS].get())
+ ->GetGroup(group);
}
ControllerEmu::ControlGroup* Wiimote::GetTurntableGroup(TurntableGroup group)
{
- return static_cast<Turntable*>(m_extension->attachments[EXT_TURNTABLE].get())->GetGroup(group);
+ return static_cast<Turntable*>(
+ m_attachments->GetAttachmentList()[ExtensionNumber::TURNTABLE].get())
+ ->GetGroup(group);
}
-bool Wiimote::Step()
+bool Wiimote::ProcessExtensionPortEvent()
{
- m_motor->control_ref->State(m_rumble_on);
+ // WiiBrew: Following a connection or disconnection event on the Extension Port,
+ // data reporting is disabled and the Data Reporting Mode must be reset before new data can
+ // arrive.
+ if (m_extension_port.IsDeviceConnected() == m_status.extension)
+ return false;
- // when a movie is active, this button status update is disabled (moved), because movies only
- // record data reports.
- if (!Core::WantsDeterminism())
- {
- UpdateButtonsStatus();
- }
+ // FYI: This happens even during a read request which continues after the status report is sent.
+ m_reporting_mode = InputReportID::REPORT_DISABLED;
- // If an extension change is requested in the GUI it will first be disconnected here.
- // causing IsDeviceConnected() to return false below:
- HandleExtensionSwap();
+ DEBUG_LOG(WIIMOTE, "Sending status report due to extension status change.");
- // Check if a status report needs to be sent. This happens when extensions are switched.
- // ..even during read requests which continue after the status report is sent.
- if (m_status.extension != m_extension_port.IsDeviceConnected())
- {
- // WiiBrew: Following a connection or disconnection event on the Extension Port,
- // data reporting is disabled and the Data Reporting Mode must be reset before new data can
- // arrive.
- m_reporting_mode = RT_REPORT_DISABLED;
-
- INFO_LOG(WIIMOTE, "Sending status report due to extension status change.");
-
- RequestStatus();
-
- return true;
- }
+ HandleRequestStatus(OutputReportRequestStatus{});
- if (ProcessReadDataRequest())
- {
- // Read requests suppress normal input reports
- // Don't send any other reports
- return true;
- }
-
- return false;
+ return true;
}
+// Update buttons in status struct from user input.
void Wiimote::UpdateButtonsStatus()
{
- // update buttons in status struct
m_status.buttons.hex = 0;
- const bool sideways_modifier_toggle = m_hotkeys->getSettingsModifier()[0];
- const bool sideways_modifier_switch = m_hotkeys->getSettingsModifier()[2];
- const bool is_sideways =
- m_sideways_setting->GetValue() ^ sideways_modifier_toggle ^ sideways_modifier_switch;
- m_buttons->GetState(&m_status.buttons.hex, button_bitmasks);
- m_dpad->GetState(&m_status.buttons.hex, is_sideways ? dpad_sideways_bitmasks : dpad_bitmasks);
-}
-
-void Wiimote::GetButtonData(u8* const data)
-{
- // when a movie is active, the button update happens here instead of Wiimote::Step, to avoid
- // potential desync issues.
- if (Core::WantsDeterminism())
- {
- UpdateButtonsStatus();
- }
-
- reinterpret_cast<wm_buttons*>(data)->hex |= m_status.buttons.hex;
-}
-
-// Produces 10-bit precision values.
-void Wiimote::GetAccelData(s16* x, s16* y, s16* z)
-{
- const bool sideways_modifier_toggle = m_hotkeys->getSettingsModifier()[0];
- const bool upright_modifier_toggle = m_hotkeys->getSettingsModifier()[1];
- const bool sideways_modifier_switch = m_hotkeys->getSettingsModifier()[2];
- const bool upright_modifier_switch = m_hotkeys->getSettingsModifier()[3];
- const bool is_sideways =
- m_sideways_setting->GetValue() ^ sideways_modifier_toggle ^ sideways_modifier_switch;
- const bool is_upright =
- m_upright_setting->GetValue() ^ upright_modifier_toggle ^ upright_modifier_switch;
-
- EmulateTilt(&m_accel, m_tilt, is_sideways, is_upright);
-
- DynamicConfiguration swing_config;
- swing_config.low_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_SLOW);
- swing_config.med_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_MEDIUM);
- swing_config.high_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_FAST);
- swing_config.frames_needed_for_high_intensity =
- Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_FAST);
- swing_config.frames_needed_for_low_intensity =
- Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_SLOW);
- swing_config.frames_to_execute = Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_LENGTH);
-
- EmulateSwing(&m_accel, m_swing, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_MEDIUM),
- is_sideways, is_upright);
- EmulateSwing(&m_accel, m_swing_slow, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_SLOW),
- is_sideways, is_upright);
- EmulateSwing(&m_accel, m_swing_fast, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_FAST),
- is_sideways, is_upright);
- EmulateDynamicSwing(&m_accel, m_swing_dynamic_data, m_swing_dynamic, swing_config, is_sideways,
- is_upright);
-
- DynamicConfiguration shake_config;
- shake_config.low_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT);
- shake_config.med_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM);
- shake_config.high_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD);
- shake_config.frames_needed_for_high_intensity =
- Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_HARD);
- shake_config.frames_needed_for_low_intensity =
- Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_SOFT);
- shake_config.frames_to_execute = Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_LENGTH);
-
- EmulateShake(&m_accel, m_shake, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM),
- m_shake_step.data());
- EmulateShake(&m_accel, m_shake_soft, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT),
- m_shake_soft_step.data());
- EmulateShake(&m_accel, m_shake_hard, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD),
- m_shake_hard_step.data());
- EmulateDynamicShake(&m_accel, m_shake_dynamic_data, m_shake_dynamic, shake_config,
- m_shake_dynamic_step.data());
-
- // We now use 2 bits more precision, so multiply by 4 before converting to int
- *x = (s16)(4 * (m_accel.x * ACCEL_RANGE + ACCEL_ZERO_G));
- *y = (s16)(4 * (m_accel.y * ACCEL_RANGE + ACCEL_ZERO_G));
- *z = (s16)(4 * (m_accel.z * ACCEL_RANGE + ACCEL_ZERO_G));
-
- *x = MathUtil::Clamp<s16>(*x, 0, 0x3ff);
- *y = MathUtil::Clamp<s16>(*y, 0, 0x3ff);
- *z = MathUtil::Clamp<s16>(*z, 0, 0x3ff);
-}
-
-inline void LowPassFilter(double& var, double newval, double period)
-{
- static const double CUTOFF_FREQUENCY = 5.0;
- double RC = 1.0 / CUTOFF_FREQUENCY;
- double alpha = period / (period + RC);
- var = newval * alpha + var * (1.0 - alpha);
+ m_buttons->GetState(&m_status.buttons.hex, button_bitmasks);
+ m_dpad->GetState(&m_status.buttons.hex, IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks);
}
-void Wiimote::UpdateIRData(bool use_accel)
+void Wiimote::Update()
{
- u16 x[4], y[4];
- memset(x, 0xFF, sizeof(x));
-
- double nsin, ncos;
-
- if (use_accel)
- {
- double ax = m_accel.x;
- double az = m_accel.z;
- const double len = sqrt(ax * ax + az * az);
+ // Check if connected.
+ if (0 == m_reporting_channel)
+ return;
- if (len)
- {
- ax /= len;
- az /= len; // normalizing the vector
- nsin = ax;
- ncos = az;
- }
- else
- {
- nsin = 0;
- ncos = 1;
- }
- }
- else
+ // Update buttons in the status struct which is sent in 99% of input reports.
+ // FYI: Movies only sync button updates in data reports.
+ if (!Core::WantsDeterminism())
{
- // TODO m_tilt stuff
- nsin = 0;
- ncos = 1;
+ const auto lock = GetStateLock();
+ UpdateButtonsStatus();
}
- LowPassFilter(ir_sin, nsin, 1.0 / 60);
- LowPassFilter(ir_cos, ncos, 1.0 / 60);
-
- static constexpr int camWidth = 1024;
- static constexpr int camHeight = 768;
- static constexpr double bndup = -0.315447;
- static constexpr double bnddown = 0.85;
- static constexpr double bndleft = 0.78820266;
- static constexpr double bndright = -0.78820266;
- static constexpr double dist1 = 100.0 / camWidth; // this seems the optimal distance for zelda
- static constexpr double dist2 = 1.2 * dist1;
-
- const ControllerEmu::Cursor::StateData cursor_state = m_ir->GetState(true);
+ // If a new extension is requested in the GUI the change will happen here.
+ HandleExtensionSwap();
- std::array<Vertex, 4> v;
- for (auto& vtx : v)
+ // Returns true if a report was sent.
+ if (ProcessExtensionPortEvent())
{
- vtx.x = cursor_state.x * (bndright - bndleft) / 2 + (bndleft + bndright) / 2;
-
- if (m_sensor_bar_on_top)
- vtx.y = cursor_state.y * (bndup - bnddown) / 2 + (bndup + bnddown) / 2;
- else
- vtx.y = cursor_state.y * (bndup - bnddown) / 2 - (bndup + bnddown) / 2;
-
- vtx.z = 0;
+ // Extension port event occured.
+ // Don't send any other reports.
+ return;
}
- v[0].x -= (cursor_state.z * 0.5 + 1) * dist1;
- v[1].x += (cursor_state.z * 0.5 + 1) * dist1;
- v[2].x -= (cursor_state.z * 0.5 + 1) * dist2;
- v[3].x += (cursor_state.z * 0.5 + 1) * dist2;
-
-#define printmatrix(m) \
- PanicAlert("%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", m[0][0], m[0][1], m[0][2], \
- m[0][3], m[1][0], m[1][1], m[1][2], m[1][3], m[2][0], m[2][1], m[2][2], m[2][3], \
- m[3][0], m[3][1], m[3][2], m[3][3])
- Matrix rot, tot;
- static Matrix scale;
- MatrixScale(scale, 1, camWidth / camHeight, 1);
- MatrixRotationByZ(rot, ir_sin, ir_cos);
- MatrixMultiply(tot, scale, rot);
-
- for (std::size_t i = 0; i < v.size(); i++)
+ if (ProcessReadDataRequest())
{
- 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)));
+ // Read requests suppress normal input reports
+ // Don't send any other reports
+ return;
}
- // IR data is read from offset 0x37 on real hardware
- auto& data = m_camera_logic.reg_data.camera_data;
- // A maximum of 36 bytes:
- std::fill(std::begin(data), std::end(data), 0xff);
-
- // Fill report with valid data when full handshake was done
- // TODO: kill magic number:
- if (m_camera_logic.reg_data.data[0x30])
- {
- // ir mode
- switch (m_camera_logic.reg_data.mode)
- {
- // basic
- case 1:
- {
- wm_ir_basic* const irdata = reinterpret_cast<wm_ir_basic*>(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;
- }
- // extended
- case 3:
- {
- wm_ir_extended* const irdata = reinterpret_cast<wm_ir_extended*>(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;
- }
- // full
- case 5:
- {
- wm_ir_full* const irdata = reinterpret_cast<wm_ir_full*>(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 is fairly normal, 0xff data is sent in this case:
- // WARN_LOG(WIIMOTE, "Game is requesting IR data before setting IR mode.");
- break;
- }
- }
+ SendDataReport();
}
-void Wiimote::Update()
+void Wiimote::SendDataReport()
{
- // no channel == not connected i guess
- if (0 == m_reporting_channel)
- return;
-
- // returns true if a report was sent
- {
- const auto lock = GetStateLock();
- if (Step())
- return;
- }
-
- u8 data[MAX_PAYLOAD] = {};
-
Movie::SetPolledDevice();
- if (RT_REPORT_DISABLED == m_reporting_mode)
+ if (InputReportID::REPORT_DISABLED == m_reporting_mode)
{
// The wiimote is in this disabled after an extension change.
// Input reports are not sent, even on button change.
return;
}
- if (RT_REPORT_CORE == m_reporting_mode && !m_reporting_auto)
+ if (InputReportID::REPORT_CORE == m_reporting_mode && !m_reporting_continuous)
{
- // TODO: we only need to send a report if the data changed when reporting_auto is disabled
- // probably only sensible to check this with RT_REPORT_CORE
- // and just always send a report with the other modes
- // return;
+ // TODO: we only need to send a report if the data changed when m_reporting_continuous is
+ // disabled. It's probably only sensible to check this with REPORT_CORE
}
- const ReportFeatures& rptf = reporting_mode_features[m_reporting_mode - RT_REPORT_CORE];
- s8 rptf_size = rptf.total_size;
+ DataReportBuilder rpt_builder(m_reporting_mode);
+
if (Movie::IsPlayingInput() &&
- Movie::PlayWiimote(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key))
+ Movie::PlayWiimote(m_index, rpt_builder, m_active_extension, GetExtensionEncryptionKey()))
{
- if (rptf.core_size)
- m_status.buttons = *reinterpret_cast<wm_buttons*>(data + rptf.GetCoreOffset());
+ // Update buttons in status struct from movie:
+ rpt_builder.GetCoreData(&m_status.buttons);
}
else
{
- data[0] = HID_TYPE_DATA << 4 | HID_PARAM_INPUT;
- data[1] = m_reporting_mode;
-
const auto lock = GetStateLock();
- // hotkey/settings modifier
- m_hotkeys->GetState(); // data is later accessed in UpdateButtonsStatus and GetAccelData
-
- u8* feature_ptr = data + rptf.GetCoreOffset();
+ // Hotkey / settings modifier
+ // Data is later accessed in IsSideways and IsUpright
+ m_hotkeys->GetState();
- // core buttons
- if (rptf.core_size)
- {
- GetButtonData(feature_ptr);
- feature_ptr += rptf.core_size;
- }
+ // CORE
- // acceleration
- if (rptf.accel_size)
+ if (rpt_builder.HasCore())
{
- s16 x, y, z;
- GetAccelData(&x, &y, &z);
-
- auto& core = *reinterpret_cast<wm_buttons*>(data + rptf.GetCoreOffset());
-
- // Special cases for the interleaved reports:
- // only 8 bits of precision.
- if (RT_REPORT_INTERLEAVE1 == m_reporting_mode)
+ if (Core::WantsDeterminism())
{
- *feature_ptr = (x >> 2) & 0xff;
- core.acc_bits = (z >> 6) & 0b11;
- core.acc_bits2 = (z >> 8) & 0b11;
- }
- if (RT_REPORT_INTERLEAVE2 == m_reporting_mode)
- {
- *feature_ptr = (y >> 2) & 0xff;
- core.acc_bits = (z >> 2) & 0b11;
- core.acc_bits2 = (z >> 4) & 0b11;
- }
- else
- {
- auto& accel = *reinterpret_cast<wm_accel*>(feature_ptr);
- accel.x = (x >> 2) & 0xFF;
- accel.y = (y >> 2) & 0xFF;
- accel.z = (z >> 2) & 0xFF;
-
- // Set the LSBs
- core.acc_bits = x & 0b11;
- core.acc_bits2 = (y >> 1) & 0x1;
- core.acc_bits2 |= ((z >> 1) & 0x1) << 1;
+ // When running non-deterministically we've already updated buttons in Update()
+ UpdateButtonsStatus();
}
- feature_ptr += rptf.accel_size;
+ rpt_builder.SetCoreData(m_status.buttons);
}
- // IR Camera
- // TODO: kill use_accel param, I think it exists for TAS reasons..
- if (m_status.ir)
- UpdateIRData(rptf.accel_size != 0);
+ // ACCEL
+
+ // FYI: This data is also used to tilt the IR dots.
+ NormalizedAccelData norm_accel = {};
+ GetAccelData(&norm_accel);
- if (rptf.ir_size)
+ if (rpt_builder.HasAccel())
{
- if (!m_status.ir)
- {
- // TODO: Does a real wiimote send 0xFFs in this case?
- WARN_LOG(WIIMOTE, "Game is reading IR data without enabling IR logic first.");
- }
+ // Calibration values are 8-bit but we want 10-bit precision, so << 2.
+ DataReportBuilder::AccelData accel =
+ DenormalizeAccelData(norm_accel, ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
+ rpt_builder.SetAccelData(accel);
+ }
- // The real wiimote reads camera data from the i2c bus at offset 0x37:
- u8 camera_data_offset = offsetof(IRCameraLogic::RegData, camera_data);
+ // IR
- if (RT_REPORT_INTERLEAVE2 == m_reporting_mode)
+ if (rpt_builder.HasIR())
+ {
+ const auto cursor = m_ir->GetState(true);
+ m_camera_logic.Update(cursor, norm_accel, m_sensor_bar_on_top);
+
+ if (!m_status.ir)
{
- // Interleave2 reads the 2nd half of the "FULL" IR data
- camera_data_offset += sizeof(wm_ir_full) * 2;
+ // TODO: What does a real wiimote send in this case? 0xFFs ?
+ // I'm assuming it still reads from the bus?
+ DEBUG_LOG(WIIMOTE, "Game is reading IR data without enabling IR logic first.");
}
- m_i2c_bus.BusRead(IRCameraLogic::DEVICE_ADDR, camera_data_offset, rptf.ir_size, feature_ptr);
+ // The real wiimote reads camera data from the i2c bus starting at offset 0x37:
+ const u8 camera_data_offset =
+ CameraLogic::REPORT_DATA_OFFSET + rpt_builder.GetIRDataFormatOffset();
- feature_ptr += rptf.ir_size;
+ m_i2c_bus.BusRead(CameraLogic::I2C_ADDR, camera_data_offset, rpt_builder.GetIRDataSize(),
+ rpt_builder.GetIRDataPtr());
}
- // extension / motion-plus
- if (rptf.ext_size)
+ // EXT
+
+ if (rpt_builder.HasExt())
{
- // Update extension first as motion-plus will read from it.
- m_ext_logic.Update();
- m_motion_plus_logic.Update();
+ // Update extension first as motion-plus may read from it.
+ GetActiveExtension()->Update();
+ m_motion_plus.Update();
+
+ u8* ext_data = rpt_builder.GetExtDataPtr();
+ const u8 ext_size = rpt_builder.GetExtDataSize();
- const u8 slave_addr = ExtensionLogic::DEVICE_ADDR;
- if (rptf.ext_size != m_i2c_bus.BusRead(slave_addr, 0x00, rptf.ext_size, feature_ptr))
+ if (ext_size != m_i2c_bus.BusRead(ExtensionPort::REPORT_I2C_SLAVE,
+ ExtensionPort::REPORT_I2C_ADDR, ext_size, ext_data))
{
// Real wiimote seems to fill with 0xff on failed bus read
- std::fill_n(feature_ptr, rptf.ext_size, 0xff);
+ std::fill_n(ext_data, ext_size, 0xff);
}
-
- feature_ptr += rptf.ext_size;
}
- if (feature_ptr != data + rptf_size)
- {
- PanicAlert("Wiimote input report is the wrong size!");
- }
-
- Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension,
- m_ext_logic.ext_key);
+ Movie::CallWiiInputManip(rpt_builder, m_index, m_active_extension, GetExtensionEncryptionKey());
}
if (NetPlay::IsNetPlayRunning())
{
- NetPlay_GetWiimoteData(m_index, data, rptf.total_size, m_reporting_mode);
- if (rptf.core_size)
- m_status.buttons = *reinterpret_cast<wm_buttons*>(data + rptf.core_size);
+ NetPlay_GetWiimoteData(m_index, rpt_builder.GetDataPtr(), rpt_builder.GetDataSize(),
+ u8(m_reporting_mode));
+
+ // TODO: clean up how m_status.buttons is updated.
+ rpt_builder.GetCoreData(&m_status.buttons);
}
- Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension,
- m_ext_logic.ext_key);
+ Movie::CheckWiimoteStatus(m_index, rpt_builder, m_active_extension, GetExtensionEncryptionKey());
- // send data report
- if (rptf_size)
- {
- Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, rptf_size);
- }
+ // Send the report:
+ CallbackInterruptChannel(rpt_builder.GetDataPtr(), rpt_builder.GetDataSize());
// The interleaved reporting modes toggle back and forth:
- if (RT_REPORT_INTERLEAVE1 == m_reporting_mode)
- m_reporting_mode = RT_REPORT_INTERLEAVE2;
- else if (RT_REPORT_INTERLEAVE2 == m_reporting_mode)
- m_reporting_mode = RT_REPORT_INTERLEAVE1;
+ if (InputReportID::REPORT_INTERLEAVE1 == m_reporting_mode)
+ m_reporting_mode = InputReportID::REPORT_INTERLEAVE2;
+ else if (InputReportID::REPORT_INTERLEAVE2 == m_reporting_mode)
+ m_reporting_mode = InputReportID::REPORT_INTERLEAVE1;
}
void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size)
@@ -1048,27 +501,41 @@ void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size)
// Check for custom communication
if (99 == channel_id)
{
- // Wii Remote disconnected
- // reset eeprom/register/reporting mode
+ // Wii Remote disconnected.
Reset();
+
+ return;
+ }
+
+ if (!size)
+ {
+ ERROR_LOG(WIIMOTE, "ControlChannel: zero sized data");
return;
}
m_reporting_channel = channel_id;
- const hid_packet* hidp = reinterpret_cast<const hid_packet*>(data);
+ // FYI: ControlChannel is piped through WiimoteEmu before WiimoteReal just so we can sync the
+ // channel on state load. This is ugly.
+ if (WIIMOTE_SRC_REAL == g_wiimote_sources[m_index])
+ {
+ WiimoteReal::ControlChannel(m_index, channel_id, data, size);
+ return;
+ }
+
+ const auto& hidp = *reinterpret_cast<const HIDPacket*>(data);
DEBUG_LOG(WIIMOTE, "Emu ControlChannel (page: %i, type: 0x%02x, param: 0x%02x)", m_index,
- hidp->type, hidp->param);
+ hidp.type, hidp.param);
- switch (hidp->type)
+ switch (hidp.type)
{
case HID_TYPE_HANDSHAKE:
- PanicAlert("HID_TYPE_HANDSHAKE - %s", (hidp->param == HID_PARAM_INPUT) ? "INPUT" : "OUPUT");
+ PanicAlert("HID_TYPE_HANDSHAKE - %s", (hidp.param == HID_PARAM_INPUT) ? "INPUT" : "OUPUT");
break;
case HID_TYPE_SET_REPORT:
- if (HID_PARAM_INPUT == hidp->param)
+ if (HID_PARAM_INPUT == hidp.param)
{
PanicAlert("HID_TYPE_SET_REPORT - INPUT");
}
@@ -1076,46 +543,61 @@ void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size)
{
// AyuanX: My experiment shows Control Channel is never used
// shuffle2: but lwbt uses this, so we'll do what we must :)
- HidOutputReport(reinterpret_cast<const wm_report*>(hidp->data));
+ HIDOutputReport(hidp.data, size - hidp.HEADER_SIZE);
+ // TODO: Should this be above the previous?
u8 handshake = HID_HANDSHAKE_SUCCESS;
- Core::Callback_WiimoteInterruptChannel(m_index, channel_id, &handshake, 1);
+ CallbackInterruptChannel(&handshake, sizeof(handshake));
}
break;
case HID_TYPE_DATA:
- PanicAlert("HID_TYPE_DATA - %s", (hidp->param == HID_PARAM_INPUT) ? "INPUT" : "OUTPUT");
+ PanicAlert("HID_TYPE_DATA - %s", (hidp.param == HID_PARAM_INPUT) ? "INPUT" : "OUTPUT");
break;
default:
- PanicAlert("HidControlChannel: Unknown type %x and param %x", hidp->type, hidp->param);
+ PanicAlert("HidControlChannel: Unknown type %x and param %x", hidp.type, hidp.param);
break;
}
}
void Wiimote::InterruptChannel(const u16 channel_id, const void* data, u32 size)
{
+ if (!size)
+ {
+ ERROR_LOG(WIIMOTE, "InterruptChannel: zero sized data");
+ return;
+ }
+
m_reporting_channel = channel_id;
- const hid_packet* hidp = reinterpret_cast<const hid_packet*>(data);
+ // FYI: InterruptChannel is piped through WiimoteEmu before WiimoteReal just so we can sync the
+ // channel on state load. This is ugly.
+ if (WIIMOTE_SRC_REAL == g_wiimote_sources[m_index])
+ {
+ WiimoteReal::InterruptChannel(m_index, channel_id, data, size);
+ return;
+ }
+
+ const auto& hidp = *reinterpret_cast<const HIDPacket*>(data);
- switch (hidp->type)
+ switch (hidp.type)
{
case HID_TYPE_DATA:
- switch (hidp->param)
+ switch (hidp.param)
{
case HID_PARAM_OUTPUT:
- HidOutputReport(reinterpret_cast<const wm_report*>(hidp->data));
+ HIDOutputReport(hidp.data, size - hidp.HEADER_SIZE);
break;
default:
- PanicAlert("HidInput: HID_TYPE_DATA - param 0x%02x", hidp->param);
+ PanicAlert("HidInput: HID_TYPE_DATA - param 0x%02x", hidp.param);
break;
}
break;
default:
- PanicAlert("HidInput: Unknown type 0x%02x and param 0x%02x", hidp->type, hidp->param);
+ PanicAlert("HidInput: Unknown type 0x%02x and param 0x%02x", hidp.type, hidp.param);
break;
}
}
@@ -1127,7 +609,7 @@ bool Wiimote::CheckForButtonPress()
m_buttons->GetState(&buttons, button_bitmasks);
m_dpad->GetState(&buttons, dpad_bitmasks);
- return (buttons != 0 || m_extension->IsButtonPressed());
+ return (buttons != 0 || GetActiveExtension()->IsButtonPressed());
}
void Wiimote::LoadDefaults(const ControllerInterface& ciface)
@@ -1185,228 +667,42 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface)
m_dpad->SetControlExpression(3, "Right"); // Right
#endif
- // ugly stuff
- // enable nunchuk
- m_extension->switch_extension = 1;
-
- // set nunchuk defaults
- m_extension->attachments[1]->LoadDefaults(ciface);
+ // Enable Nunchuk:
+ constexpr ExtensionNumber DEFAULT_EXT = ExtensionNumber::NUNCHUK;
+ m_attachments->SetSelectedAttachment(DEFAULT_EXT);
+ m_attachments->GetAttachmentList()[DEFAULT_EXT]->LoadDefaults(ciface);
}
-int Wiimote::CurrentExtension() const
+Extension* Wiimote::GetActiveExtension() const
{
- return m_extension->active_extension;
+ return static_cast<Extension*>(m_attachments->GetAttachmentList()[m_active_extension].get());
}
-bool Wiimote::ExtensionLogic::ReadDeviceDetectPin()
+EncryptionKey Wiimote::GetExtensionEncryptionKey() const
{
- return extension->active_extension != 0;
+ if (ExtensionNumber::NONE == GetActiveExtensionNumber())
+ return {};
+
+ return static_cast<EncryptedExtension*>(GetActiveExtension())->ext_key;
}
-void Wiimote::ExtensionLogic::Update()
+bool Wiimote::IsSideways() const
{
- // Update controller data from user input:
- // Write data to addr 0x00 of extension register
- extension->GetState(reg_data.controller_data);
+ const bool sideways_modifier_toggle = m_hotkeys->getSettingsModifier()[0];
+ const bool sideways_modifier_switch = m_hotkeys->getSettingsModifier()[2];
+ return m_sideways_setting->GetValue() ^ sideways_modifier_toggle ^ sideways_modifier_switch;
}
-// TODO: move this to Common if it doesn't exist already?
-template <typename T>
-void SetBit(T& value, u32 bit_number, bool bit_value)
+bool Wiimote::IsUpright() const
{
- if (bit_value)
- value |= (1 << bit_number);
- else
- value &= ~(1 << bit_number);
+ const bool upright_modifier_toggle = m_hotkeys->getSettingsModifier()[1];
+ const bool upright_modifier_switch = m_hotkeys->getSettingsModifier()[3];
+ return m_upright_setting->GetValue() ^ upright_modifier_toggle ^ upright_modifier_switch;
}
-void Wiimote::MotionPlusLogic::Update()
+void Wiimote::SetRumble(bool on)
{
- if (!IsActive())
- {
- return;
- }
-
- auto& data = reg_data.controller_data;
- auto& mplus_data = *reinterpret_cast<wm_motionplus_data*>(data);
-
- if (0x0 == reg_data.cert_ready)
- {
- // Without sending this nonsense, inputs are unresponsive.. even regular buttons
- // Device still operates when changing the data slightly so its not any sort of encrpytion
- // It even works when removing the is_mp_data bit in the last byte
- // My M+ non-inside gives: 61,46,45,aa,0,2 or b6,46,45,9a,0,2
- // static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc | 0x01, 0x02};
- static const u8 init_data[6] = {0x81, 0x46, 0x46, 0xb6, 0x01, 0x02};
- std::copy(std::begin(init_data), std::end(init_data), data);
- reg_data.cert_ready = 0x2;
- return;
- }
-
- if (0x2 == reg_data.cert_ready)
- {
- static const u8 init_data[6] = {0x7f, 0xcf, 0xdf, 0x8b, 0x4f, 0x82};
- std::copy(std::begin(init_data), std::end(init_data), data);
- reg_data.cert_ready = 0x8;
- return;
- }
-
- if (0x8 == reg_data.cert_ready)
- {
- // A real wiimote takes about 2 seconds to reach this state:
- reg_data.cert_ready = 0xe;
- }
-
- if (0x18 == reg_data.cert_ready)
- {
- // TODO: determine the meaning of this
- const u8 mp_cert2[64] = {
- 0xa5, 0x84, 0x1f, 0xd6, 0xbd, 0xdc, 0x7a, 0x4c, 0xf3, 0xc0, 0x24, 0xe0, 0x92,
- 0xef, 0x19, 0x28, 0x65, 0xe0, 0x62, 0x7c, 0x9b, 0x41, 0x6f, 0x12, 0xc3, 0xac,
- 0x78, 0xe4, 0xfc, 0x6b, 0x7b, 0x0a, 0xb4, 0x50, 0xd6, 0xf2, 0x45, 0xf7, 0x93,
- 0x04, 0xaf, 0xf2, 0xb7, 0x26, 0x94, 0xee, 0xad, 0x92, 0x05, 0x6d, 0xe5, 0xc6,
- 0xd6, 0x36, 0xdc, 0xa5, 0x69, 0x0f, 0xc8, 0x99, 0xf2, 0x1c, 0x4e, 0x0d,
- };
-
- std::copy(std::begin(mp_cert2), std::end(mp_cert2), reg_data.cert_data);
-
- if (0x01 != reg_data.cert_enable)
- {
- PanicAlert("M+ Failure! Game requested cert2 with value other than 0x01. M+ will disconnect "
- "shortly unfortunately. Reconnect wiimote and hope for the best.");
- }
-
- // A real wiimote takes about 2 seconds to reach this state:
- reg_data.cert_ready = 0x1a;
- INFO_LOG(WIIMOTE, "M+ cert 2 ready!");
- }
-
- // TODO: make sure a motion plus report is sent first after init
-
- // On real mplus:
- // For some reason the first read seems to have garbage data
- // is_mp_data and extension_connected are set, but the data is junk
- // it does seem to have some sort of pattern though, byte 5 is always 2
- // something like: d5, b0, 4e, 6e, fc, 2
- // When a passthrough mode is set:
- // the second read is valid mplus data, which then triggers a read from the extension
- // the third read is finally extension data
- // If an extension is not attached the data is always mplus data
- // even when passthrough is enabled
-
- // Real M+ seems to only ever read 6 bytes from the extension.
- // Data after 6 bytes seems to be zero-filled.
- // After reading, the real M+ uses that data for the next frame.
- // But we are going to use it for the current frame instead.
- const int ext_amt = 6;
- // Always read from 0x52 @ 0x00:
- const u8 ext_slave = ACTIVE_DEVICE_ADDR;
- const u8 ext_addr = 0x00;
-
- // Try to alternate between M+ and EXT data:
- mplus_data.is_mp_data ^= true;
-
- // hax!!!
- static const u8 hacky_mp_data[6] = {0x1d, 0x91, 0x49, 0x87, 0x73, 0x7a};
- static const u8 hacky_nc_data[6] = {0x79, 0x7f, 0x4b, 0x83, 0x8b, 0xec};
- auto& hacky_ptr = mplus_data.is_mp_data ? hacky_mp_data : hacky_nc_data;
- std::copy(std::begin(hacky_ptr), std::end(hacky_ptr), data);
- return;
-
- // If the last frame had M+ data try to send some non-M+ data:
- if (!mplus_data.is_mp_data)
- {
- switch (GetPassthroughMode())
- {
- case PassthroughMode::DISABLED:
- {
- // Passthrough disabled, always send M+ data:
- mplus_data.is_mp_data = true;
- break;
- }
- case PassthroughMode::NUNCHUK:
- {
- if (ext_amt == i2c_bus.BusRead(ext_slave, ext_addr, ext_amt, data))
- {
- // Passthrough data modifications via wiibrew.org
- // Data passing through drops the least significant bit of the three accelerometer values
- // Bit 7 of byte 5 is moved to bit 6 of byte 5, overwriting it
- SetBit(data[5], 6, Common::ExtractBit(data[5], 7));
- // Bit 0 of byte 4 is moved to bit 7 of byte 5
- SetBit(data[5], 7, Common::ExtractBit(data[4], 0));
- // Bit 3 of byte 5 is moved to bit 4 of byte 5, overwriting it
- SetBit(data[5], 4, Common::ExtractBit(data[5], 3));
- // Bit 1 of byte 5 is moved to bit 3 of byte 5
- SetBit(data[5], 3, Common::ExtractBit(data[5], 1));
- // Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it
- SetBit(data[5], 2, Common::ExtractBit(data[5], 0));
-
- // Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below.
- mplus_data.is_mp_data = false;
- }
- else
- {
- // Read failed (extension unplugged), Send M+ data instead
- mplus_data.is_mp_data = true;
- }
- break;
- }
- case PassthroughMode::CLASSIC:
- {
- if (ext_amt == i2c_bus.BusRead(ext_slave, ext_addr, ext_amt, data))
- {
- // Passthrough data modifications via wiibrew.org
- // Data passing through drops the least significant bit of the axes of the left (or only)
- // joystick Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] Bits 0 and 1
- // of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before
- SetBit(data[0], 0, Common::ExtractBit(data[5], 0));
- SetBit(data[1], 0, Common::ExtractBit(data[5], 1));
-
- // Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below.
- mplus_data.is_mp_data = false;
- }
- else
- {
- // Read failed (extension unplugged), Send M+ data instead
- mplus_data.is_mp_data = true;
- }
- break;
- }
- default:
- PanicAlert("MotionPlus unknown passthrough-mode %d", (int)GetPassthroughMode());
- break;
- }
- }
-
- // If the above logic determined this should be M+ data, update it here
- if (mplus_data.is_mp_data)
- {
- // Wiibrew: "While the Wiimote is still, the values will be about 0x1F7F (8,063)"
- // high-velocity range should be about +/- 1500 or 1600 dps
- // low-velocity range should be about +/- 400 dps
- // Wiibrew implies it shoould be +/- 595 and 2700
-
- u16 yaw_value = 0x2000;
- u16 roll_value = 0x2000;
- u16 pitch_value = 0x2000;
-
- mplus_data.yaw_slow = 1;
- mplus_data.roll_slow = 1;
- mplus_data.pitch_slow = 1;
-
- // Bits 0-7
- mplus_data.yaw1 = yaw_value & 0xff;
- mplus_data.roll1 = roll_value & 0xff;
- mplus_data.pitch1 = pitch_value & 0xff;
-
- // Bits 8-13
- mplus_data.yaw2 = yaw_value >> 8;
- mplus_data.roll2 = roll_value >> 8;
- mplus_data.pitch2 = pitch_value >> 8;
- }
-
- mplus_data.extension_connected = extension_port.IsDeviceConnected();
- mplus_data.zero = 0;
+ m_motor->control_ref->State(on);
}
} // namespace WiimoteEmu
diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h
index ebb1c97397..5d6293f47f 100644
--- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h
+++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h
@@ -4,25 +4,25 @@
#pragma once
-#include <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
-{
- EXT_NONE,
-
- EXT_NUNCHUK,
- EXT_CLASSIC,
- EXT_GUITAR,
- EXT_DRUMS,
- EXT_TURNTABLE
-};
-
-enum class NunchukGroup
-{
- Buttons,
- Stick,
- Tilt,
- Swing,
- Shake
-};
-
-enum class ClassicGroup
-{
- Buttons,
- Triggers,
- DPad,
- LeftStick,
- RightStick
-};
-
-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;
+enum class NunchukGroup;
+enum class ClassicGroup;
+enum class GuitarGroup;
+enum class DrumsGroup;
+enum class TurntableGroup;
- int frames_to_execute; // How many frames should we execute the action for?
-};
-
-struct ADPCMState
+template <typename T>
+void UpdateCalibrationDataChecksum(T& data, int cksum_bytes)
{
- s32 predictor, step;
-};
+ constexpr u8 CALIBRATION_MAGIC_NUMBER = 0x55;
-struct ExtensionReg
-{
- // 16 bytes of possible extension data
- u8 controller_data[0x10];
-
- u8 unknown2[0x10];
-
- // address 0x20
- u8 calibration[0x10];
- u8 unknown3[0x10];
+ static_assert(std::is_same<decltype(data[0]), u8&>::value, "Only sane for containers of u8!");
- // 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)
+ auto cksum_start = std::end(data) - cksum_bytes;
-static_assert(0x100 == sizeof(ExtensionReg));
+ // 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);
-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;
-
- ExtensionPort m_extension_port{m_i2c_bus};
+ // Used only for error generation:
+ static constexpr u8 EEPROM_I2C_ADDR = 0x50;
- struct IRCameraLogic : public I2CSlave
- {
- // TODO: some of this memory is write-only and should return error 7.
+ // static constexpr int EEPROM_SIZE = 16 * 1024;
+ // This is the region exposed over bluetooth:
+ static constexpr int EEPROM_FREE_SIZE = 0x1700;
- 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;
-
- static_assert(0x100 == sizeof(reg_data));
-
- static const u8 DEVICE_ADDR = 0x58;
-
- int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override
- {
- if (DEVICE_ADDR != slave_addr)
- return 0;
-
- return RawRead(&reg_data, addr, count, data_out);
- }
+ void UpdateButtonsStatus();
- int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override
- {
- if (DEVICE_ADDR != slave_addr)
- return 0;
+ void GetAccelData(NormalizedAccelData* accel);
- return RawWrite(&reg_data, addr, count, data_in);
- }
+ void HIDOutputReport(const void* data, u32 size);
- } m_camera_logic;
+ 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&);
- class ExtensionLogic : public ExtensionAttachment
- {
- public:
- ExtensionReg reg_data;
- wiimote_key ext_key;
+ template <typename T, typename H>
+ void InvokeHandler(H&& handler, const WiimoteCommon::OutputReportGeneric& rpt, u32 size);
- ControllerEmu::Extension* extension;
-
- static const u8 DEVICE_ADDR = 0x52;
+ void HandleExtensionSwap();
+ bool ProcessExtensionPortEvent();
+ void SendDataReport();
+ bool ProcessReadDataRequest();
- void Update();
+ void RealState();
- 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(&reg_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;
+ void SetRumble(bool on);
- auto const result = RawWrite(&reg_data, addr, count, data_in);
+ void CallbackInterruptChannel(const u8* data, u32 size);
+ void SendAck(WiimoteCommon::OutputReportID rpt_id, WiimoteCommon::ErrorCode err);
- 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);
- }
+ bool IsSideways() const;
+ bool IsUpright() const;
- return result;
- }
+ Extension* GetActiveExtension() const;
- bool ReadDeviceDetectPin() override;
+ bool NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size, u8 reporting_mode);
- } m_ext_logic;
+ // TODO: Kill this nonsensical function used for TAS:
+ EncryptionKey GetExtensionEncryptionKey() const;
- struct SpeakerLogic : public I2CSlave
+ struct ReadRequest
{
- // 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(&reg_data, addr, count, data_out);
- }
+ WiimoteCommon::AddressSpace space;
+ u8 slave_address;
+ u16 address;
+ u16 size;
+ };
- 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(&reg_data, addr, count, data_in);
- }
- }
-
- } m_speaker_logic;
-
- struct MotionPlusLogic : public ExtensionAttachment
+ // This is just the usable 0x1700 bytes:
+ union UsableEEPROMData
{
- 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
+ struct
{
- u8 controller_data[21];
- u8 unknown_0x15[11];
-
- // address 0x20
- u8 calibration_data[0x20];
-
- u8 unknown_0x40[0x10];
-
- // address 0x50
- u8 cert_data[0x40];
+ // addr: 0x0000
+ std::array<u8, 11> ir_calibration_1;
+ std::array<u8, 11> ir_calibration_2;
- u8 unknown_0x90[0x60];
+ std::array<u8, 10> accel_calibration_1;
+ std::array<u8, 10> accel_calibration_2;
- // address 0xF0
- u8 initialized;
+ // addr: 0x002A
+ std::array<u8, 0x0FA0> user_data;
- // address 0xF1
- u8 cert_enable;
+ // addr: 0x0FCA
+ std::array<u8, 0x02f0> mii_data_1;
+ std::array<u8, 0x02f0> mii_data_2;
- // Conduit 2 writes 1 byte to 0xf2 on calibration screen
- u8 unknown_0xf2[5];
+ // addr: 0x15AA
+ std::array<u8, 0x0126> unk_1;
- // 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,
+ // addr: 0x16D0
+ std::array<u8, 24> unk_2;
+ std::array<u8, 24> unk_3;
};
- 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
+ std::array<u8, EEPROM_FREE_SIZE> data;
+ };
- // TODO: if an extension is attached after activation, it also does this.
+ static_assert(EEPROM_FREE_SIZE == sizeof(UsableEEPROMData));
- 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(&reg_data, addr, count, data_out);
- else
- return 0;
- }
- else
- {
- if (INACTIVE_DEVICE_ADDR == slave_addr)
- return RawRead(&reg_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(&reg_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(&reg_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);
- bool ProcessReadDataRequest();
- bool NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size, u8 reporting_mode);
-
- // control groups
+ // 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;
+
+ I2CBus m_i2c_bus;
- // Wiimote accel data
- // TODO: can this member be eliminated?
- AccelData m_accel;
+ 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
diff --git a/Source/Core/Core/HW/WiimoteReal/IOWin.cpp b/Source/Core/Core/HW/WiimoteReal/IOWin.cpp
index 244fe07f69..a5f9b569ad 100644
--- a/Source/Core/Core/HW/WiimoteReal/IOWin.cpp
+++ b/Source/Core/Core/HW/WiimoteReal/IOWin.cpp
@@ -28,6 +28,8 @@
#include "Common/ScopeGuard.h"
#include "Common/Thread.h"
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
+#include "Core/HW/WiimoteCommon/WiimoteReport.h"
+#include "Core/HW/WiimoteCommon/DataReport.h"
#include "Core/HW/WiimoteReal/IOWin.h"
// Create func_t function pointer type and declare a nullptr-initialized static variable of that
@@ -436,6 +438,8 @@ int ReadFromHandle(HANDLE& dev_handle, u8* buf)
bool IsWiimote(const std::basic_string<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
diff --git a/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm b/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm
index 2b5df642dd..5436692076 100644
--- a/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm
+++ b/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm
@@ -298,7 +298,7 @@ void WiimoteDarwin::DisablePowerAssertionInternal()
return;
}
- if (length > MAX_PAYLOAD)
+ if (length > WiimoteCommon::MAX_PAYLOAD)
{
WARN_LOG(WIIMOTE, "Dropping packet for Wiimote %i, too large", wm->GetIndex() + 1);
return;
diff --git a/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp b/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp
index 34e7b0c9f8..2d5a89672c 100644
--- a/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp
+++ b/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp
@@ -10,6 +10,9 @@
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
#include "Core/HW/WiimoteReal/IOhidapi.h"
+using namespace WiimoteCommon;
+using namespace WiimoteReal;
+
static bool IsDeviceUsable(const std::string& device_path)
{
hid_device* handle = hid_open_path(device_path.c_str());
@@ -24,7 +27,7 @@ static bool IsDeviceUsable(const std::string& device_path)
// Some third-party adapters (DolphinBar) always expose all four Wii Remotes as HIDs
// even when they are not connected, which causes an endless error loop when we try to use them.
// Try to write a report to the device to see if this Wii Remote is really usable.
- static const u8 report[] = {WR_SET_REPORT | BT_OUTPUT, RT_REQUEST_STATUS, 0};
+ static const u8 report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::REQUEST_STATUS), 0};
const int result = hid_write(handle, report, sizeof(report));
// The DolphinBar uses EPIPE to signal the absence of a Wii Remote connected to this HID.
if (result == -1 && errno != EPIPE)
@@ -145,4 +148,4 @@ int WiimoteHidapi::IOWrite(const u8* buf, size_t len)
}
return (result == 0) ? 1 : result;
}
-}; // WiimoteReal
+}; // namespace WiimoteReal
diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp
index 51f4969fb6..e930a2b547 100644
--- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp
+++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp
@@ -34,6 +34,8 @@ unsigned int g_wiimote_sources[MAX_BBMOTES];
namespace WiimoteReal
{
+using namespace WiimoteCommon;
+
static void TryToConnectBalanceBoard(Wiimote*);
static void TryToConnectWiimote(Wiimote*);
static void HandleWiimoteDisconnect(int index);
@@ -69,7 +71,7 @@ void Wiimote::WriteReport(Report rpt)
bool const new_rumble_state = (rpt[2] & 0x1) != 0;
// If this is a rumble report and the rumble state didn't change, ignore.
- if (rpt[1] == RT_RUMBLE && new_rumble_state == m_rumble_state)
+ if (rpt[1] == u8(OutputReportID::RUMBLE) && new_rumble_state == m_rumble_state)
return;
m_rumble_state = new_rumble_state;
@@ -95,22 +97,22 @@ void Wiimote::DisableDataReporting()
{
m_last_input_report.clear();
- // This probably accomplishes nothing.
- wm_report_mode rpt = {};
- rpt.mode = RT_REPORT_CORE;
+ // This accomplishes very little:
+ OutputReportMode rpt = {};
+ rpt.mode = InputReportID::REPORT_CORE;
rpt.continuous = 0;
rpt.rumble = 0;
- QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt));
+ QueueReport(u8(OutputReportID::REPORT_MODE), &rpt, sizeof(rpt));
}
void Wiimote::EnableDataReporting(u8 mode)
{
m_last_input_report.clear();
- wm_report_mode rpt = {};
- rpt.mode = mode;
+ OutputReportMode rpt = {};
+ rpt.mode = InputReportID(mode);
rpt.continuous = 1;
- QueueReport(RT_REPORT_MODE, &rpt, sizeof(rpt));
+ QueueReport(u8(OutputReportID::REPORT_MODE), &rpt, sizeof(rpt));
}
void Wiimote::SetChannel(u16 channel)
@@ -139,11 +141,11 @@ void Wiimote::ControlChannel(const u16 channel, const void* const data, const u3
else
{
InterruptChannel(channel, data, size);
- const hid_packet* const hidp = reinterpret_cast<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.");
diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h
index d1b4c65086..15c955986f 100644
--- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h
+++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h
@@ -23,6 +23,37 @@ class PointerWrap;
namespace WiimoteReal
{
+using WiimoteCommon::MAX_PAYLOAD;
+
+typedef std::vector<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
diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp
index ceb1cf5a01..ae12975895 100644
--- a/Source/Core/Core/Movie.cpp
+++ b/Source/Core/Core/Movie.cpp
@@ -45,8 +45,13 @@
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/Wiimote.h"
+#include "Core/HW/WiimoteCommon/DataReport.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
-#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
+
+#include "Core/HW/WiimoteEmu/Encryption.h"
+#include "Core/HW/WiimoteEmu/Extension/Classic.h"
+#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
+
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
#include "Core/NetPlayProto.h"
@@ -64,6 +69,9 @@
namespace Movie
{
+using namespace WiimoteCommon;
+using namespace WiimoteEmu;
+
static bool s_bReadOnly = true;
static u32 s_rerecords = 0;
static PlayMode s_playMode = MODE_NONE;
@@ -640,23 +648,17 @@ static void SetInputDisplayString(ControllerState padState, int controllerID)
}
// NOTE: CPU Thread
-static void SetWiiInputDisplayString(int remoteID, const u8* const data,
- const WiimoteEmu::ReportFeatures& rptf, int ext,
- const wiimote_key key)
+static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt, int ext,
+ const EncryptionKey& key)
{
int controllerID = remoteID + 4;
std::string display_str = StringFromFormat("R%d:", remoteID + 1);
- const u8* const coreData = rptf.core_size ? (data + rptf.GetCoreOffset()) : nullptr;
- const u8* const accelData = rptf.accel_size ? (data + rptf.GetAccelOffset()) : nullptr;
- const u8* const irData = rptf.ir_size ? (data + rptf.GetIROffset()) : nullptr;
- const u8* const extData = rptf.ext_size ? (data + rptf.GetExtOffset()) : nullptr;
-
- if (coreData)
+ if (rpt.HasCore())
{
- wm_buttons buttons;
- std::memcpy(&buttons, coreData, sizeof(buttons));
+ ButtonData buttons;
+ rpt.GetCoreData(&buttons);
if (buttons.left)
display_str += " LEFT";
@@ -680,31 +682,37 @@ static void SetWiiInputDisplayString(int remoteID, const u8* const data,
display_str += " 2";
if (buttons.home)
display_str += " HOME";
+ }
- // A few bits of accelData are actually inside the coreData struct.
- if (accelData)
- {
- wm_accel dt;
- std::memcpy(&dt, accelData, sizeof(dt));
+ if (rpt.HasAccel())
+ {
+ DataReportBuilder::AccelData accel_data;
+ rpt.GetAccelData(&accel_data);
- display_str +=
- StringFromFormat(" ACC:%d,%d,%d (LSB not shown)", dt.x << 2, dt.y << 2, dt.z << 2);
- }
+ // FYI: This will only print partial data for interleaved reports.
+
+ display_str += StringFromFormat(" ACC:%d,%d,%d", accel_data.x, accel_data.y, accel_data.z);
}
- if (irData)
+ if (rpt.HasIR())
{
+ const u8* const irData = rpt.GetIRDataPtr();
+
+ // TODO: This does not handle the different IR formats.
+
u16 x = irData[0] | ((irData[2] >> 4 & 0x3) << 8);
u16 y = irData[1] | ((irData[2] >> 6 & 0x3) << 8);
display_str += StringFromFormat(" IR:%d,%d", x, y);
}
// Nunchuk
- if (extData && ext == 1)
+ if (rpt.HasExt() && ext == 1)
{
- wm_nc nunchuk;
- memcpy(&nunchuk, extData, sizeof(wm_nc));
- WiimoteDecrypt(&key, (u8*)&nunchuk, 0, sizeof(wm_nc));
+ const u8* const extData = rpt.GetExtDataPtr();
+
+ Nunchuk::DataFormat nunchuk;
+ memcpy(&nunchuk, extData, sizeof(nunchuk));
+ key.Decrypt((u8*)&nunchuk, 0, sizeof(nunchuk));
nunchuk.bt.hex = nunchuk.bt.hex ^ 0x3;
std::string accel = StringFromFormat(
@@ -720,20 +728,22 @@ static void SetWiiInputDisplayString(int remoteID, const u8* const data,
}
// Classic controller
- if (extData && ext == 2)
+ if (rpt.HasExt() && ext == 2)
{
- wm_classic_extension cc;
- memcpy(&cc, extData, sizeof(wm_classic_extension));
- WiimoteDecrypt(&key, (u8*)&cc, 0, sizeof(wm_classic_extension));
+ const u8* const extData = rpt.GetExtDataPtr();
+
+ Classic::DataFormat cc;
+ memcpy(&cc, extData, sizeof(cc));
+ key.Decrypt((u8*)&cc, 0, sizeof(cc));
cc.bt.hex = cc.bt.hex ^ 0xFFFF;
- if (cc.bt.regular_data.dpad_left)
+ if (cc.bt.dpad_left)
display_str += " LEFT";
if (cc.bt.dpad_right)
display_str += " RIGHT";
if (cc.bt.dpad_down)
display_str += " DOWN";
- if (cc.bt.regular_data.dpad_up)
+ if (cc.bt.dpad_up)
display_str += " UP";
if (cc.bt.a)
display_str += " A";
@@ -756,7 +766,7 @@ static void SetWiiInputDisplayString(int remoteID, const u8* const data,
display_str += Analog1DToString(cc.lt1 | (cc.lt2 << 3), " L", 31);
display_str += Analog1DToString(cc.rt, " R", 31);
- display_str += Analog2DToString(cc.regular_data.lx, cc.regular_data.ly, " ANA", 63);
+ display_str += Analog2DToString(cc.lx, cc.ly, " ANA", 63);
display_str += Analog2DToString(cc.rx1 | (cc.rx2 << 1) | (cc.rx3 << 3), cc.ry, " R-ANA", 31);
}
@@ -814,13 +824,13 @@ void RecordInput(const GCPadStatus* PadStatus, int controllerID)
}
// NOTE: CPU Thread
-void CheckWiimoteStatus(int wiimote, const u8* data, const WiimoteEmu::ReportFeatures& rptf,
- int ext, const wiimote_key key)
+void CheckWiimoteStatus(int wiimote, const DataReportBuilder& rpt, int ext,
+ const EncryptionKey& key)
{
- SetWiiInputDisplayString(wiimote, data, rptf, ext, key);
+ SetWiiInputDisplayString(wiimote, rpt, ext, key);
if (IsRecordingInput())
- RecordWiimote(wiimote, data, rptf.total_size);
+ RecordWiimote(wiimote, rpt.GetDataPtr(), rpt.GetDataSize());
}
void RecordWiimote(int wiimote, const u8* data, u8 size)
@@ -1189,8 +1199,8 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
}
// NOTE: CPU Thread
-bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf, int ext,
- const wiimote_key key)
+bool PlayWiimote(int wiimote, WiimoteCommon::DataReportBuilder& rpt, int ext,
+ const EncryptionKey& key)
{
if (!IsPlayingInput() || !IsUsingWiimote(wiimote) || s_temp_input.empty())
return false;
@@ -1203,9 +1213,8 @@ bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf,
return false;
}
- u8 size = rptf.total_size;
-
- u8 sizeInMovie = s_temp_input[s_currentByte];
+ const u8 size = rpt.GetDataSize();
+ const u8 sizeInMovie = s_temp_input[s_currentByte];
if (size != sizeInMovie)
{
@@ -1229,7 +1238,7 @@ bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf,
return false;
}
- memcpy(data, &s_temp_input[s_currentByte], size);
+ memcpy(rpt.GetDataPtr(), &s_temp_input[s_currentByte], size);
s_currentByte += size;
s_currentInputCount++;
@@ -1348,11 +1357,10 @@ void CallGCInputManip(GCPadStatus* PadStatus, int controllerID)
s_gc_manip_func(PadStatus, controllerID);
}
// NOTE: CPU Thread
-void CallWiiInputManip(u8* data, WiimoteEmu::ReportFeatures rptf, int controllerID, int ext,
- const wiimote_key key)
+void CallWiiInputManip(DataReportBuilder& rpt, int controllerID, int ext, const EncryptionKey& key)
{
if (s_wii_manip_func)
- s_wii_manip_func(data, rptf, controllerID, ext, key);
+ s_wii_manip_func(rpt, controllerID, ext, key);
}
// NOTE: GPU Thread
diff --git a/Source/Core/Core/Movie.h b/Source/Core/Core/Movie.h
index b4e88cc47d..aac76fdd84 100644
--- a/Source/Core/Core/Movie.h
+++ b/Source/Core/Core/Movie.h
@@ -10,17 +10,13 @@
#include <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
diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp
index e81a885267..0acee31f3a 100644
--- a/Source/Core/Core/NetPlayClient.cpp
+++ b/Source/Core/Core/NetPlayClient.cpp
@@ -43,6 +43,7 @@
#include "Core/HW/Sram.h"
#include "Core/HW/WiiSave.h"
#include "Core/HW/WiiSaveStructs.h"
+#include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Core/IOS/FS/FileSystem.h"
@@ -61,6 +62,8 @@
namespace NetPlay
{
+using namespace WiimoteCommon;
+
static std::mutex crit_netplay_client;
static NetPlayClient* netplay_client = nullptr;
static std::unique_ptr<IOS::HLE::FS::FileSystem> s_wii_sync_fs;
diff --git a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp
index 6110fac43b..9c808eae0a 100644
--- a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp
+++ b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp
@@ -9,6 +9,11 @@
#include <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"
diff --git a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuGeneral.cpp b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuGeneral.cpp
index 11a7533589..3c34319790 100644
--- a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuGeneral.cpp
+++ b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuGeneral.cpp
@@ -16,7 +16,7 @@
#include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuExtension.h"
-#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
+#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/Setting/BooleanSetting.h"
#include "InputCommon/InputConfig.h"
@@ -40,12 +40,13 @@ void WiimoteEmuGeneral::CreateMainLayout()
m_main_layout->addWidget(CreateGroupBox(
tr("Hotkeys"), Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Hotkeys)));
- auto* extension_group = Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Extension);
+ auto* extension_group =
+ Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::Attachments);
auto* extension = CreateGroupBox(tr("Extension"), extension_group);
- auto* ce_extension = static_cast<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()
diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp
index 5759f0e70a..14230bf94d 100644
--- a/Source/Core/DolphinQt/MainWindow.cpp
+++ b/Source/Core/DolphinQt/MainWindow.cpp
@@ -350,9 +350,9 @@ void MainWindow::CreateComponents()
m_gc_tas_input_windows[controller_id]->GetValues(pad_status);
});
- Movie::SetWiiInputManip([this](u8* input_data, WiimoteEmu::ReportFeatures rptf, int controller_id,
- int ext, wiimote_key key) {
- m_wii_tas_input_windows[controller_id]->GetValues(input_data, rptf, ext, key);
+ Movie::SetWiiInputManip([this](WiimoteCommon::DataReportBuilder& rpt, int controller_id, int ext,
+ const WiimoteEmu::EncryptionKey& key) {
+ m_wii_tas_input_windows[controller_id]->GetValues(rpt, ext, key);
});
m_jit_widget = new JITWidget(this);
diff --git a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp
index bc7ab87703..f0a837db38 100644
--- a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp
+++ b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp
@@ -14,10 +14,16 @@
#include "Common/FileUtil.h"
#include "Core/Core.h"
-#include "Core/HW/WiimoteEmu/Attachment/Classic.h"
-#include "Core/HW/WiimoteEmu/Attachment/Nunchuk.h"
+#include "Core/HW/WiimoteCommon/DataReport.h"
#include "Core/HW/WiimoteEmu/Encryption.h"
+#include "Core/HW/WiimoteEmu/Extension/Classic.h"
+#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
+
+#include "Core/HW/WiimoteEmu/Extension/Classic.h"
+#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
+
+#include "Core/HW/WiimoteEmu/Camera.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "DolphinQt/QtUtils/AspectRatioWidget.h"
@@ -27,6 +33,8 @@
#include "InputCommon/InputConfig.h"
+using namespace WiimoteCommon;
+
WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(parent), m_num(num)
{
const QKeySequence ir_x_shortcut_key_sequence = QKeySequence(Qt::ALT + Qt::Key_F);
@@ -255,7 +263,7 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
if (Core::IsRunning())
{
ext = static_cast<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);
-
- // TODO: lsb
- u16 accel_x = (accel.x << 2); // & (buttons.acc_x_lsb & 0b11);
- u16 accel_y = (accel.y << 2); // & ((buttons.acc_y_lsb & 0b1) << 1);
- u16 accel_z = (accel.z << 2); // &((buttons.acc_z_lsb & 0b1) << 1);
-
- GetSpinBoxU16(m_remote_orientation_x_value, accel_x);
- GetSpinBoxU16(m_remote_orientation_y_value, accel_y);
- 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;
+ // FYI: Interleaved reports may behave funky as not all data is always available.
+
+ 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);
+
+ 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));
}
}
diff --git a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h
index a8decedbf6..d3a830b138 100644
--- a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h
+++ b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h
@@ -6,21 +6,27 @@
#include "DolphinQt/TAS/TASInputWindow.h"
+namespace WiimoteCommon
+{
+class DataReportBuilder;
+}
+
namespace WiimoteEmu
{
-struct ReportFeatures;
+class EncryptionKey;
}
+
class QCheckBox;
class QGroupBox;
class QSpinBox;
-struct wiimote_key;
class WiiTASInputWindow : public TASInputWindow
{
Q_OBJECT
public:
explicit WiiTASInputWindow(QWidget* parent, int num);
- void GetValues(u8* input_data, WiimoteEmu::ReportFeatures rptf, int ext, wiimote_key key);
+ void GetValues(WiimoteCommon::DataReportBuilder& rpt, int ext,
+ const WiimoteEmu::EncryptionKey& key);
private:
void UpdateExt(u8 ext);
diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt
index ece9bbbde8..d67d1654b3 100644
--- a/Source/Core/InputCommon/CMakeLists.txt
+++ b/Source/Core/InputCommon/CMakeLists.txt
@@ -7,10 +7,10 @@ add_library(inputcommon
ControllerEmu/Control/Input.cpp
ControllerEmu/Control/Output.cpp
ControllerEmu/ControlGroup/AnalogStick.cpp
+ ControllerEmu/ControlGroup/Attachments.cpp
ControllerEmu/ControlGroup/Buttons.cpp
ControllerEmu/ControlGroup/ControlGroup.cpp
ControllerEmu/ControlGroup/Cursor.cpp
- ControllerEmu/ControlGroup/Extension.cpp
ControllerEmu/ControlGroup/Force.cpp
ControllerEmu/ControlGroup/MixedTriggers.cpp
ControllerEmu/ControlGroup/ModifySettingsButton.cpp
diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp
new file mode 100644
index 0000000000..e104918fe2
--- /dev/null
+++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp
@@ -0,0 +1,33 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
+
+namespace ControllerEmu
+{
+Attachments::Attachments(const std::string& name_) : ControlGroup(name_, GroupType::Attachments)
+{
+}
+
+void Attachments::AddAttachment(std::unique_ptr<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
diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h
new file mode 100644
index 0000000000..746bc523ab
--- /dev/null
+++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h
@@ -0,0 +1,37 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <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
diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.cpp
index 1cc2ac1865..6e8e5cfb70 100644
--- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.cpp
+++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.cpp
@@ -9,7 +9,7 @@
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
-#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
+#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerEmu/Setting/BooleanSetting.h"
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
@@ -67,22 +67,22 @@ void ControlGroup::LoadConfig(IniFile::Section* sec, const std::string& defdev,
}
// extensions
- if (type == GroupType::Extension)
+ if (type == GroupType::Attachments)
{
- Extension* const ext = (Extension*)this;
+ auto* const ext = static_cast<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() + "/");
}
}
diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h
index b771adcdb9..8e9fa3f24a 100644
--- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h
+++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h
@@ -24,7 +24,7 @@ enum class GroupType
MixedTriggers,
Buttons,
Force,
- Extension,
+ Attachments,
Tilt,
Cursor,
Triggers,
diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.cpp
deleted file mode 100644
index eb9dbc8a99..0000000000
--- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 Dolphin Emulator Project
-// Licensed under GPLv2+
-// Refer to the license.txt file included.
-
-#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
-
-#include <string>
-
-#include "InputCommon/ControllerEmu/ControllerEmu.h"
-
-namespace ControllerEmu
-{
-Extension::Extension(const std::string& name_) : ControlGroup(name_, GroupType::Extension)
-{
-}
-} // namespace ControllerEmu
diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.h
deleted file mode 100644
index 366e94f248..0000000000
--- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Extension.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 Dolphin Emulator Project
-// Licensed under GPLv2+
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <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
diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.h
index 03dc07ecbd..7b55d3535f 100644
--- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.h
+++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.h
@@ -19,8 +19,5 @@ public:
explicit Force(const std::string& name);
StateData GetState();
-
-private:
- StateData m_swing{};
};
} // namespace ControllerEmu
diff --git a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp
index 98b9e5828b..e6623ea754 100644
--- a/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp
+++ b/Source/Core/InputCommon/ControllerEmu/ControllerEmu.cpp
@@ -12,8 +12,8 @@
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
+#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
-#include "InputCommon/ControllerEmu/ControlGroup/Extension.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
namespace ControllerEmu
@@ -41,10 +41,10 @@ void EmulatedController::UpdateReferences(const ControllerInterface& devi)
for (auto& control : ctrlGroup->controls)
control->control_ref.get()->UpdateReference(devi, GetDefaultDevice());
- // extension
- if (ctrlGroup->type == GroupType::Extension)
+ // Attachments:
+ if (ctrlGroup->type == GroupType::Attachments)
{
- for (auto& attachment : ((Extension*)ctrlGroup.get())->attachments)
+ for (auto& attachment : static_cast<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);
}
diff --git a/Source/Core/InputCommon/InputCommon.vcxproj b/Source/Core/InputCommon/InputCommon.vcxproj
index 2bef816726..0c8e95d400 100644
--- a/Source/Core/InputCommon/InputCommon.vcxproj
+++ b/Source/Core/InputCommon/InputCommon.vcxproj
@@ -45,7 +45,7 @@
<ClCompile Include="ControllerEmu\ControlGroup\Buttons.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\ControlGroup.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\Cursor.cpp" />
- <ClCompile Include="ControllerEmu\ControlGroup\Extension.cpp" />
+ <ClCompile Include="ControllerEmu\ControlGroup\Attachments.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\Force.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\MixedTriggers.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\ModifySettingsButton.cpp" />
@@ -84,7 +84,7 @@
<ClInclude Include="ControllerEmu\ControlGroup\Buttons.h" />
<ClInclude Include="ControllerEmu\ControlGroup\ControlGroup.h" />
<ClInclude Include="ControllerEmu\ControlGroup\Cursor.h" />
- <ClInclude Include="ControllerEmu\ControlGroup\Extension.h" />
+ <ClInclude Include="ControllerEmu\ControlGroup\Attachments.h" />
<ClInclude Include="ControllerEmu\ControlGroup\Force.h" />
<ClInclude Include="ControllerEmu\ControlGroup\MixedTriggers.h" />
<ClInclude Include="ControllerEmu\ControlGroup\ModifySettingsButton.h" />
@@ -121,4 +121,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/Source/Core/InputCommon/InputCommon.vcxproj.filters b/Source/Core/InputCommon/InputCommon.vcxproj.filters
index 01c5c371b6..ec95436d76 100644
--- a/Source/Core/InputCommon/InputCommon.vcxproj.filters
+++ b/Source/Core/InputCommon/InputCommon.vcxproj.filters
@@ -56,9 +56,6 @@
<ClCompile Include="ControllerEmu\ControlGroup\Cursor.cpp">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClCompile>
- <ClCompile Include="ControllerEmu\ControlGroup\Extension.cpp">
- <Filter>ControllerEmu\ControlGroup</Filter>
- </ClCompile>
<ClCompile Include="ControllerEmu\ControlGroup\Force.cpp">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClCompile>
@@ -114,6 +111,9 @@
<Filter>ControllerInterface</Filter>
</ClCompile>
<ClCompile Include="InputProfile.cpp" />
+ <ClCompile Include="ControllerEmu\ControlGroup\Attachments.cpp">
+ <Filter>ControllerEmu\ControlGroup</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="GCAdapter.h" />
@@ -146,9 +146,6 @@
<ClInclude Include="ControllerEmu\ControlGroup\Cursor.h">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClInclude>
- <ClInclude Include="ControllerEmu\ControlGroup\Extension.h">
- <Filter>ControllerEmu\ControlGroup</Filter>
- </ClInclude>
<ClInclude Include="ControllerEmu\ControlGroup\Force.h">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClInclude>
@@ -210,8 +207,11 @@
<Filter>ControllerInterface</Filter>
</ClInclude>
<ClInclude Include="InputProfile.h" />
+ <ClInclude Include="ControllerEmu\ControlGroup\Attachments.h">
+ <Filter>ControllerEmu\ControlGroup</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
-</Project>
+</Project> \ No newline at end of file