Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
VoiceChatPacket.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <bit>
4#include <cstddef>
5#include <cstdint>
6#include <limits>
7#include <span>
8#include <vector>
9
11{
12 inline constexpr std::size_t MaximumPacketBytes =
13 std::numeric_limits<std::uint16_t>::max();
14
15 enum class EventKind : std::uint8_t
16 {
17 Start,
19 Volume,
20 Data,
23 Stop
24 };
25
27 {
28 std::uint16_t formatTag{};
29 std::uint16_t channels{};
30 std::uint32_t samplesPerSecond{};
31 std::uint32_t averageBytesPerSecond{};
32 std::uint16_t blockAlign{};
33 std::uint16_t bitsPerSample{};
34 std::uint16_t extraSize{};
35 };
36
37 struct Event
38 {
41 std::uint32_t value{};
42 std::span<const std::uint8_t> payload{};
43 };
44
45 enum class ParseStatus : std::uint8_t
46 {
47 Ok,
50 };
51
52 using EventSink = bool (*)(const Event&, std::uint32_t sourceId);
53
54 [[nodiscard]] constexpr std::uint32_t fourCC(
55 char a, char b, char c, char d) noexcept
56 {
57 return static_cast<std::uint32_t>(static_cast<unsigned char>(a)) |
58 (static_cast<std::uint32_t>(static_cast<unsigned char>(b)) << 8u) |
59 (static_cast<std::uint32_t>(static_cast<unsigned char>(c)) << 16u) |
60 (static_cast<std::uint32_t>(static_cast<unsigned char>(d)) << 24u);
61 }
62
63 [[nodiscard]] inline std::uint16_t readLe16(
64 std::span<const std::uint8_t> bytes, std::size_t offset) noexcept
65 {
66 return static_cast<std::uint16_t>(bytes[offset]) |
67 static_cast<std::uint16_t>(
68 static_cast<std::uint16_t>(bytes[offset + 1u]) << 8u);
69 }
70
71 [[nodiscard]] inline std::uint32_t readLe32(
72 std::span<const std::uint8_t> bytes, std::size_t offset) noexcept
73 {
74 return static_cast<std::uint32_t>(bytes[offset]) |
75 (static_cast<std::uint32_t>(bytes[offset + 1u]) << 8u) |
76 (static_cast<std::uint32_t>(bytes[offset + 2u]) << 16u) |
77 (static_cast<std::uint32_t>(bytes[offset + 3u]) << 24u);
78 }
79
81 std::span<const std::uint8_t> bytes) noexcept
82 {
83 return WaveFormat{
84 readLe16(bytes, 0u), readLe16(bytes, 2u),
85 readLe32(bytes, 4u), readLe32(bytes, 8u),
86 readLe16(bytes, 12u), readLe16(bytes, 14u),
87 readLe16(bytes, 16u)};
88 }
89
91 EventSink sink, std::uint32_t sourceId, const Event& event) noexcept
92 {
93 if (sink == nullptr || sink(event, sourceId))
94 return ParseStatus::Ok;
96 }
97
99 std::span<const std::uint8_t> bytes,
100 std::uint32_t sourceId,
101 EventSink sink) noexcept
102 {
103 std::size_t offset{};
104 while (offset < bytes.size())
105 {
106 if (bytes.size() - offset < 8u)
108 const auto id = readLe32(bytes, offset);
109 const auto size = static_cast<std::size_t>(readLe32(bytes, offset + 4u));
110 offset += 8u;
111 if (size > bytes.size() - offset)
113 const auto payload = bytes.subspan(offset, size);
114 Event event{};
115 if (id == fourCC('f', 'm', 't', ' '))
116 {
117 if (size < 18u)
119 event.kind = EventKind::Start;
120 event.format = readWaveFormat(payload);
121 }
122 else if (id == fourCC('d', 'i', 'm', 's'))
123 {
124 if (size < 4u)
126 event.kind = EventKind::Dimensions;
127 event.value = readLe32(payload, 0u);
128 }
129 else if (id == fourCC('v', 'o', 'l', 'm'))
130 {
131 if (size < 4u)
133 event.kind = EventKind::Volume;
134 event.value = readLe32(payload, 0u);
135 }
136 else
137 {
138 offset += size;
139 continue;
140 }
141
142 event.payload = payload;
143 const auto status = emit(sink, sourceId, event);
144 if (status != ParseStatus::Ok)
145 return status;
146 offset += size;
147 }
148 return ParseStatus::Ok;
149 }
150
152 std::span<const std::uint8_t> bytes,
153 std::uint32_t sourceId,
154 EventSink sink = nullptr) noexcept
155 {
156 if (bytes.empty() || bytes.size() > MaximumPacketBytes)
158
159 std::size_t offset{};
160 while (offset < bytes.size())
161 {
162 if (bytes.size() - offset < 8u)
164 const auto id = readLe32(bytes, offset);
165 const auto size = static_cast<std::size_t>(readLe32(bytes, offset + 4u));
166 offset += 8u;
167 if (size > bytes.size() - offset)
169 const auto payload = bytes.subspan(offset, size);
170
171 if (id == fourCC('C', 'H', 'A', 'T'))
172 {
173 const auto status = parseChatSubchunks(payload, sourceId, sink);
174 if (status != ParseStatus::Ok)
175 return status;
176 }
177 else
178 {
179 Event event{};
180 if (id == fourCC('D', 'A', 'T', 'N'))
181 event.kind = EventKind::Data;
182 else if (id == fourCC('D', 'A', 'T', '1'))
183 event.kind = EventKind::DataAfterSilence;
184 else if (id == fourCC('P', 'O', 'S', 'N'))
185 event.kind = EventKind::Position;
186 else if (id == fourCC('S', 'T', 'O', 'P'))
187 event.kind = EventKind::Stop;
188 else
189 {
190 offset += size;
191 continue;
192 }
193 event.payload = payload;
194 const auto status = emit(sink, sourceId, event);
195 if (status != ParseStatus::Ok)
196 return status;
197 }
198 offset += size;
199 }
200
201
202 return ParseStatus::Ok;
203 }
204
205 namespace detail
206 {
207 inline void appendLe16(std::vector<std::uint8_t>& bytes,
208 std::uint16_t value)
209 {
210 bytes.push_back(static_cast<std::uint8_t>(value));
211 bytes.push_back(static_cast<std::uint8_t>(value >> 8u));
212 }
213
214 inline void appendLe32(std::vector<std::uint8_t>& bytes,
215 std::uint32_t value)
216 {
217 for (unsigned shift = 0; shift < 32u; shift += 8u)
218 bytes.push_back(static_cast<std::uint8_t>(value >> shift));
219 }
220
221 [[nodiscard]] inline bool appendChunk(
222 std::vector<std::uint8_t>& bytes, std::uint32_t id,
223 std::span<const std::uint8_t> payload)
224 {
225 if (payload.size() > std::numeric_limits<std::uint32_t>::max() ||
226 bytes.size() + 8u + payload.size() > MaximumPacketBytes)
227 return false;
228 appendLe32(bytes, id);
229 appendLe32(bytes, static_cast<std::uint32_t>(payload.size()));
230 bytes.insert(bytes.end(), payload.begin(), payload.end());
231 return true;
232 }
233 }
234
235 [[nodiscard]] inline bool makeStartPacket(
236 WaveFormat format, std::span<const std::uint8_t> extraFormatBytes,
237 std::uint32_t dimensions, std::uint32_t volume,
238 std::vector<std::uint8_t>& packet)
239 {
240 if (extraFormatBytes.size() > std::numeric_limits<std::uint16_t>::max())
241 return false;
242 format.extraSize = static_cast<std::uint16_t>(extraFormatBytes.size());
243 std::vector<std::uint8_t> wireFormat;
244 wireFormat.reserve(18u + extraFormatBytes.size());
247 detail::appendLe32(wireFormat, format.samplesPerSecond);
248 detail::appendLe32(wireFormat, format.averageBytesPerSecond);
250 detail::appendLe16(wireFormat, format.bitsPerSample);
252 wireFormat.insert(wireFormat.end(),
253 extraFormatBytes.begin(), extraFormatBytes.end());
254
255 std::vector<std::uint8_t> chat;
256 const auto scalarChunk = [&](std::uint32_t id, std::uint32_t value)
257 {
258 std::vector<std::uint8_t> scalar;
259 scalar.reserve(4u);
261 return detail::appendChunk(chat, id, scalar);
262 };
263 if (!detail::appendChunk(chat, fourCC('f', 'm', 't', ' '), wireFormat) ||
264 !scalarChunk(fourCC('d', 'i', 'm', 's'), dimensions) ||
265 !scalarChunk(fourCC('v', 'o', 'l', 'm'), volume))
266 return false;
267 packet.clear();
268 return detail::appendChunk(packet, fourCC('C', 'H', 'A', 'T'), chat);
269 }
270
271 [[nodiscard]] inline bool makeDataPacket(
272 std::span<const std::uint8_t> data, bool afterSilence,
273 std::vector<std::uint8_t>& packet)
274 {
275 packet.clear();
276 return detail::appendChunk(packet,
277 afterSilence ? fourCC('D', 'A', 'T', '1')
278 : fourCC('D', 'A', 'T', 'N'), data);
279 }
280
281 [[nodiscard]] inline bool makeStopPacket(std::vector<std::uint8_t>& packet)
282 {
283 packet.clear();
284 return detail::appendChunk(packet, fourCC('S', 'T', 'O', 'P'), {});
285 }
286
288 std::span<const float> coordinates,
289 std::vector<std::uint8_t>& packet)
290 {
291 if (coordinates.empty() || coordinates.size() > 3u)
292 return false;
293 std::vector<std::uint8_t> payload;
294 payload.reserve(coordinates.size() * sizeof(float));
295 for (const float value : coordinates)
296 detail::appendLe32(payload, std::bit_cast<std::uint32_t>(value));
297 packet.clear();
298 return detail::appendChunk(packet, fourCC('P', 'O', 'S', 'N'), payload);
299 }
300}
std::vector< std::uint8_t > data
std::size_t offset
std::vector< std::byte > bytes
data::DataId id
unsigned index
std::uint8_t volume
std::uint16_t value
Definition Timers.cpp:16
SDL_GPUShaderFormat format
void appendLe16(std::vector< std::uint8_t > &bytes, std::uint16_t value)
bool appendChunk(std::vector< std::uint8_t > &bytes, std::uint32_t id, std::span< const std::uint8_t > payload)
void appendLe32(std::vector< std::uint8_t > &bytes, std::uint32_t value)
constexpr std::uint32_t fourCC(char a, char b, char c, char d) noexcept
std::uint16_t readLe16(std::span< const std::uint8_t > bytes, std::size_t offset) noexcept
ParseStatus parse(std::span< const std::uint8_t > bytes, std::uint32_t sourceId, EventSink sink=nullptr) noexcept
ParseStatus parseChatSubchunks(std::span< const std::uint8_t > bytes, std::uint32_t sourceId, EventSink sink) noexcept
std::uint32_t readLe32(std::span< const std::uint8_t > bytes, std::size_t offset) noexcept
bool makeDataPacket(std::span< const std::uint8_t > data, bool afterSilence, std::vector< std::uint8_t > &packet)
ParseStatus emit(EventSink sink, std::uint32_t sourceId, const Event &event) noexcept
bool(*)(const Event &, std::uint32_t sourceId) EventSink
bool makePositionPacket(std::span< const float > coordinates, std::vector< std::uint8_t > &packet)
bool makeStartPacket(WaveFormat format, std::span< const std::uint8_t > extraFormatBytes, std::uint32_t dimensions, std::uint32_t volume, std::vector< std::uint8_t > &packet)
constexpr std::size_t MaximumPacketBytes
WaveFormat readWaveFormat(std::span< const std::uint8_t > bytes) noexcept
bool makeStopPacket(std::vector< std::uint8_t > &packet)
std::span< const std::uint8_t > payload