summaryrefslogtreecommitdiff
path: root/src/input_common/udp/protocol.h
diff options
context:
space:
mode:
authorGravatar fearlessTobi2019-08-24 15:57:49 +0200
committerGravatar FearlessTobi2020-01-23 20:55:26 +0100
commitac3690f2057fb93ce18f156ff5ffd720a6d6f60c (patch)
treed0ec80a2537b992146d34f5bf17ba0cc549bd88e /src/input_common/udp/protocol.h
parentMerge pull request #3341 from bunnei/time-posix-myrule (diff)
downloadyuzu-ac3690f2057fb93ce18f156ff5ffd720a6d6f60c.tar.gz
yuzu-ac3690f2057fb93ce18f156ff5ffd720a6d6f60c.tar.xz
yuzu-ac3690f2057fb93ce18f156ff5ffd720a6d6f60c.zip
Input: UDP Client to provide motion and touch controls
An implementation of the cemuhook motion/touch protocol, this adds the ability for users to connect several different devices to citra to send direct motion and touch data to citra. Co-Authored-By: jroweboy <jroweboy@gmail.com>
Diffstat (limited to '')
-rw-r--r--src/input_common/udp/protocol.h249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h
new file mode 100644
index 000000000..d31bbeb89
--- /dev/null
+++ b/src/input_common/udp/protocol.h
@@ -0,0 +1,249 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <optional>
9#include <type_traits>
10#include <vector>
11#include <boost/crc.hpp>
12#include "common/bit_field.h"
13#include "common/swap.h"
14
15namespace InputCommon::CemuhookUDP {
16
17constexpr std::size_t MAX_PACKET_SIZE = 100;
18constexpr u16 PROTOCOL_VERSION = 1001;
19constexpr u32 CLIENT_MAGIC = 0x43555344; // DSUC (but flipped for LE)
20constexpr u32 SERVER_MAGIC = 0x53555344; // DSUS (but flipped for LE)
21
22enum class Type : u32 {
23 Version = 0x00100000,
24 PortInfo = 0x00100001,
25 PadData = 0x00100002,
26};
27
28struct Header {
29 u32_le magic;
30 u16_le protocol_version;
31 u16_le payload_length;
32 u32_le crc;
33 u32_le id;
34 ///> In the protocol, the type of the packet is not part of the header, but its convenient to
35 ///> include in the header so the callee doesn't have to duplicate the type twice when building
36 ///> the data
37 Type type;
38};
39static_assert(sizeof(Header) == 20, "UDP Message Header struct has wrong size");
40static_assert(std::is_trivially_copyable_v<Header>, "UDP Message Header is not trivially copyable");
41
42using MacAddress = std::array<u8, 6>;
43constexpr MacAddress EMPTY_MAC_ADDRESS = {0, 0, 0, 0, 0, 0};
44
45#pragma pack(push, 1)
46template <typename T>
47struct Message {
48 Header header;
49 T data;
50};
51#pragma pack(pop)
52
53template <typename T>
54constexpr Type GetMessageType();
55
56namespace Request {
57
58struct Version {};
59/**
60 * Requests the server to send information about what controllers are plugged into the ports
61 * In citra's case, we only have one controller, so for simplicity's sake, we can just send a
62 * request explicitly for the first controller port and leave it at that. In the future it would be
63 * nice to make this configurable
64 */
65constexpr u32 MAX_PORTS = 4;
66struct PortInfo {
67 u32_le pad_count; ///> Number of ports to request data for
68 std::array<u8, MAX_PORTS> port;
69};
70static_assert(std::is_trivially_copyable_v<PortInfo>,
71 "UDP Request PortInfo is not trivially copyable");
72
73/**
74 * Request the latest pad information from the server. If the server hasn't received this message
75 * from the client in a reasonable time frame, the server will stop sending updates. The default
76 * timeout seems to be 5 seconds.
77 */
78struct PadData {
79 enum class Flags : u8 {
80 AllPorts,
81 Id,
82 Mac,
83 };
84 /// Determines which method will be used as a look up for the controller
85 Flags flags;
86 /// Index of the port of the controller to retrieve data about
87 u8 port_id;
88 /// Mac address of the controller to retrieve data about
89 MacAddress mac;
90};
91static_assert(sizeof(PadData) == 8, "UDP Request PadData struct has wrong size");
92static_assert(std::is_trivially_copyable_v<PadData>,
93 "UDP Request PadData is not trivially copyable");
94
95/**
96 * Creates a message with the proper header data that can be sent to the server.
97 * @param T data Request body to send
98 * @param client_id ID of the udp client (usually not checked on the server)
99 */
100template <typename T>
101Message<T> Create(const T data, const u32 client_id = 0) {
102 boost::crc_32_type crc;
103 Header header{
104 CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(),
105 };
106 Message<T> message{header, data};
107 crc.process_bytes(&message, sizeof(Message<T>));
108 message.header.crc = crc.checksum();
109 return message;
110}
111} // namespace Request
112
113namespace Response {
114
115struct Version {
116 u16_le version;
117};
118static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size");
119static_assert(std::is_trivially_copyable_v<Version>,
120 "UDP Response Version is not trivially copyable");
121
122struct PortInfo {
123 u8 id;
124 u8 state;
125 u8 model;
126 u8 connection_type;
127 MacAddress mac;
128 u8 battery;
129 u8 is_pad_active;
130};
131static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
132static_assert(std::is_trivially_copyable_v<PortInfo>,
133 "UDP Response PortInfo is not trivially copyable");
134
135#pragma pack(push, 1)
136struct PadData {
137 PortInfo info;
138 u32_le packet_counter;
139
140 u16_le digital_button;
141 // The following union isn't trivially copyable but we don't use this input anyway.
142 // union DigitalButton {
143 // u16_le button;
144 // BitField<0, 1, u16> button_1; // Share
145 // BitField<1, 1, u16> button_2; // L3
146 // BitField<2, 1, u16> button_3; // R3
147 // BitField<3, 1, u16> button_4; // Options
148 // BitField<4, 1, u16> button_5; // Up
149 // BitField<5, 1, u16> button_6; // Right
150 // BitField<6, 1, u16> button_7; // Down
151 // BitField<7, 1, u16> button_8; // Left
152 // BitField<8, 1, u16> button_9; // L2
153 // BitField<9, 1, u16> button_10; // R2
154 // BitField<10, 1, u16> button_11; // L1
155 // BitField<11, 1, u16> button_12; // R1
156 // BitField<12, 1, u16> button_13; // Triangle
157 // BitField<13, 1, u16> button_14; // Circle
158 // BitField<14, 1, u16> button_15; // Cross
159 // BitField<15, 1, u16> button_16; // Square
160 // } digital_button;
161
162 u8 home;
163 /// If the device supports a "click" on the touchpad, this will change to 1 when a click happens
164 u8 touch_hard_press;
165 u8 left_stick_x;
166 u8 left_stick_y;
167 u8 right_stick_x;
168 u8 right_stick_y;
169
170 struct AnalogButton {
171 u8 button_8;
172 u8 button_7;
173 u8 button_6;
174 u8 button_5;
175 u8 button_12;
176 u8 button_11;
177 u8 button_10;
178 u8 button_9;
179 u8 button_16;
180 u8 button_15;
181 u8 button_14;
182 u8 button_13;
183 } analog_button;
184
185 struct TouchPad {
186 u8 is_active;
187 u8 id;
188 u16_le x;
189 u16_le y;
190 } touch_1, touch_2;
191
192 u64_le motion_timestamp;
193
194 struct Accelerometer {
195 float x;
196 float y;
197 float z;
198 } accel;
199
200 struct Gyroscope {
201 float pitch;
202 float yaw;
203 float roll;
204 } gyro;
205};
206#pragma pack(pop)
207
208static_assert(sizeof(PadData) == 80, "UDP Response PadData struct has wrong size ");
209static_assert(std::is_trivially_copyable_v<PadData>,
210 "UDP Response PadData is not trivially copyable");
211
212static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE,
213 "UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>");
214
215/**
216 * Create a Response Message from the data
217 * @param data array of bytes sent from the server
218 * @return boost::none if it failed to parse or Type if it succeeded. The client can then safely
219 * copy the data into the appropriate struct for that Type
220 */
221std::optional<Type> Validate(u8* data, std::size_t size);
222
223} // namespace Response
224
225template <>
226constexpr Type GetMessageType<Request::Version>() {
227 return Type::Version;
228}
229template <>
230constexpr Type GetMessageType<Request::PortInfo>() {
231 return Type::PortInfo;
232}
233template <>
234constexpr Type GetMessageType<Request::PadData>() {
235 return Type::PadData;
236}
237template <>
238constexpr Type GetMessageType<Response::Version>() {
239 return Type::Version;
240}
241template <>
242constexpr Type GetMessageType<Response::PortInfo>() {
243 return Type::PortInfo;
244}
245template <>
246constexpr Type GetMessageType<Response::PadData>() {
247 return Type::PadData;
248}
249} // namespace InputCommon::CemuhookUDP