File: DualShockUDPProto.h

package info (click to toggle)
dolphin-emu 2503%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 111,624 kB
  • sloc: cpp: 787,747; ansic: 217,914; xml: 31,400; python: 4,226; yacc: 3,985; javascript: 2,430; makefile: 777; asm: 726; sh: 281; pascal: 257; perl: 97; objc: 75
file content (273 lines) | stat: -rw-r--r-- 5,907 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
// Copyright 2019 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <cstring>
#include <optional>

#include "Common/CommonTypes.h"
#include "Common/Hash.h"

namespace ciface::DualShockUDPClient::Proto
{
// CemuHook DualShockUDP protocol implementation using UdpServer.cs from
// https://github.com/Ryochan7/DS4Windows as documentation.
//
// WARNING: Little endian host assumed

constexpr u16 CEMUHOOK_PROTOCOL_VERSION = 1001;

constexpr int PORT_COUNT = 4;

constexpr std::array<u8, 4> CLIENT{'D', 'S', 'U', 'C'};
constexpr std::array<u8, 4> SERVER{'D', 'S', 'U', 'S'};

#pragma pack(push, 1)

enum class DsState : u8
{
  Disconnected = 0x00,
  Reserved = 0x01,
  Connected = 0x02
};

enum class DsConnection : u8
{
  None = 0x00,
  Usb = 0x01,
  Bluetooth = 0x02
};

enum class DsModel : u8
{
  None = 0,
  PartialGyro = 1,
  FullGyro = 2,
  Generic = 3
};

enum class DsBattery : u8
{
  None = 0x00,
  Dying = 0x01,
  Low = 0x02,
  Medium = 0x03,
  High = 0x04,
  Full = 0x05,
  Charging = 0xEE,
  Charged = 0xEF
};

enum RegisterFlags : u8
{
  AllPads = 0x00,
  PadID = 0x01,
  PadMACAdddress = 0x02
};

struct MessageHeader
{
  std::array<u8, 4> source;
  u16 protocol_version;
  u16 message_length;  // actually message size minus header size
  u32 crc32;
  u32 source_uid;
};

struct Touch
{
  u8 active;
  u8 id;
  s16 x;
  s16 y;
};

namespace MessageType
{
struct VersionRequest
{
  static constexpr auto FROM = CLIENT;
  static constexpr auto TYPE = 0x100000U;
  MessageHeader header;
  u32 message_type;
};

struct VersionResponse
{
  static constexpr auto FROM = SERVER;
  static constexpr auto TYPE = 0x100000U;
  MessageHeader header;
  u32 message_type;
  u16 max_protocol_version;
  std::array<u8, 2> padding;
};

struct ListPorts
{
  static constexpr auto FROM = CLIENT;
  static constexpr auto TYPE = 0x100001U;
  MessageHeader header;
  u32 message_type;
  u32 pad_request_count;
  std::array<u8, 4> pad_ids;
};

struct PortInfo
{
  static constexpr auto FROM = SERVER;
  static constexpr auto TYPE = 0x100001U;
  MessageHeader header;
  u32 message_type;
  u8 pad_id;
  DsState pad_state;
  DsModel model;
  DsConnection connection_type;
  std::array<u8, 6> pad_mac_address;
  DsBattery battery_status;
  u8 padding;
};

struct PadDataRequest
{
  static constexpr auto FROM = CLIENT;
  static constexpr auto TYPE = 0x100002U;
  MessageHeader header;
  u32 message_type;
  RegisterFlags register_flags;
  u8 pad_id_to_register;
  std::array<u8, 6> mac_address_to_register;
};

struct PadDataResponse
{
  static constexpr auto FROM = SERVER;
  static constexpr auto TYPE = 0x100002U;
  MessageHeader header;
  u32 message_type;
  u8 pad_id;
  DsState pad_state;
  DsModel model;
  DsConnection connection_type;
  std::array<u8, 6> pad_mac_address;
  DsBattery battery_status;
  u8 active;
  u32 hid_packet_counter;
  u8 button_states1;
  u8 button_states2;
  u8 button_ps;
  u8 button_touch;
  u8 left_stick_x;
  u8 left_stick_y_inverted;
  u8 right_stick_x;
  u8 right_stick_y_inverted;
  u8 button_dpad_left_analog;
  u8 button_dpad_down_analog;
  u8 button_dpad_right_analog;
  u8 button_dpad_up_analog;
  u8 button_square_analog;
  u8 button_cross_analog;
  u8 button_circle_analog;
  u8 button_triangle_analog;
  u8 button_r1_analog;
  u8 button_l1_analog;
  u8 trigger_r2;
  u8 trigger_l2;
  Touch touch1;
  Touch touch2;
  u64 accelerometer_timestamp_us;
  float accelerometer_x_g;
  float accelerometer_y_g;
  float accelerometer_z_g;
  float gyro_pitch_deg_s;
  float gyro_yaw_deg_s;
  float gyro_roll_deg_s;
};

struct FromServer
{
  union
  {
    struct
    {
      MessageHeader header;
      u32 message_type;
    };
    MessageType::VersionResponse version_response;
    MessageType::PortInfo port_info;
    MessageType::PadDataResponse pad_data_response;
  };
};

struct FromClient
{
  union
  {
    struct
    {
      MessageHeader header;
      u32 message_type;
    };
    MessageType::VersionRequest version_request;
    MessageType::ListPorts list_ports;
    MessageType::PadDataRequest pad_data_request;
  };
};
}  // namespace MessageType

template <typename MsgType>
struct Message
{
  Message() = default;

  explicit Message(u32 source_uid)
  {
    m_message.header.source = MsgType::FROM;
    m_message.header.protocol_version = CEMUHOOK_PROTOCOL_VERSION;
    m_message.header.message_length = sizeof(*this) - sizeof(m_message.header);
    m_message.header.source_uid = source_uid;
    m_message.message_type = MsgType::TYPE;
  }

  void Finish()
  {
    m_message.header.crc32 =
        Common::ComputeCRC32(reinterpret_cast<const u8*>(&m_message), sizeof(m_message));
  }

  template <class ToMsgType>
  std::optional<ToMsgType> CheckAndCastTo()
  {
    const u32 crc32_in_header = m_message.header.crc32;
    // zero out the crc32 in the packet once we got it since that's whats needed for calculation
    m_message.header.crc32 = 0;
    const u32 crc32_calculated =
        Common::ComputeCRC32(reinterpret_cast<const u8*>(&m_message), sizeof(ToMsgType));
    if (crc32_in_header != crc32_calculated)
    {
      NOTICE_LOG_FMT(
          CONTROLLERINTERFACE,
          "DualShockUDPClient Received message with bad CRC in header: got {:08x}, expected {:08x}",
          crc32_in_header, crc32_calculated);
      return std::nullopt;
    }
    if (m_message.header.protocol_version > CEMUHOOK_PROTOCOL_VERSION)
      return std::nullopt;
    if (m_message.header.source != ToMsgType::FROM)
      return std::nullopt;
    if (m_message.message_type != ToMsgType::TYPE)
      return std::nullopt;
    if (m_message.header.message_length + sizeof(m_message.header) > sizeof(ToMsgType))
      return std::nullopt;

    ToMsgType tomsg;
    memcpy(&tomsg, &m_message, sizeof(tomsg));
    return tomsg;
  }

  MsgType m_message{};
};

#pragma pack(pop)
}  // namespace ciface::DualShockUDPClient::Proto