13#include <ascii-chat/network/acip/handlers.h>
14#include <ascii-chat/network/acip/messages.h>
15#include <ascii-chat/network/acip/acds.h>
16#include <ascii-chat/network/packet.h>
17#include <ascii-chat/network/packet_parsing.h>
18#include <ascii-chat/audio/audio.h>
19#include <ascii-chat/util/endian.h>
20#include <ascii-chat/log/logging.h>
21#include <ascii-chat/asciichat_errno.h>
22#include <ascii-chat/common.h>
30 const acip_client_callbacks_t *callbacks);
33 const acip_server_callbacks_t *callbacks);
41#define HANDLER_HASH_SIZE 32
42#define CLIENT_HANDLER_COUNT 19
43#define SERVER_HANDLER_COUNT 19
54#define HANDLER_HASH(type) ((type) % HANDLER_HASH_SIZE)
59 log_dev_every(4500 * US_PER_MS_INT,
"HANDLER_HASH_LOOKUP: type=%d, hash=%u", type, h);
62 log_dev_every(4500 * US_PER_MS_INT,
" Checking slot %u: key=%d, handler_idx=%d", slot, table[slot].key,
63 table[slot].handler_idx);
64 if (table[slot].key == 0) {
65 log_dev_every(4500 * US_PER_MS_INT,
" Empty slot found - packet type %d not in hash table", type);
68 if (table[slot].key == type) {
69 log_dev_every(4500 * US_PER_MS_INT,
" Found match at slot %u, handler_idx=%d", slot, table[slot].handler_idx);
73 log_debug(
" No match found after checking all slots");
81 [0] = {PACKET_TYPE_AUDIO_BATCH, 1},
82 [1] = {PACKET_TYPE_AUDIO_OPUS_BATCH, 2},
83 [6] = {PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT, 14},
84 [7] = {PACKET_TYPE_CRYPTO_AUTH_CHALLENGE, 15},
85 [8] = {PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP, 16},
86 [9] = {PACKET_TYPE_PING, 6},
87 [10] = {PACKET_TYPE_PONG, 7},
88 [11] = {PACKET_TYPE_CRYPTO_AUTH_FAILED, 17},
89 [14] = {PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE, 18},
90 [15] = {PACKET_TYPE_CLEAR_CONSOLE, 8},
91 [16] = {PACKET_TYPE_SERVER_STATE, 3},
92 [17] = {PACKET_TYPE_CRYPTO_REKEY_REQUEST, 9},
93 [18] = {PACKET_TYPE_CRYPTO_REKEY_RESPONSE, 10},
94 [19] = {PACKET_TYPE_ERROR_MESSAGE, 4},
95 [20] = {PACKET_TYPE_REMOTE_LOG, 5},
96 [21] = {PACKET_TYPE_ACIP_SESSION_JOINED, 13},
97 [24] = {PACKET_TYPE_ASCII_FRAME, 0},
98 [25] = {PACKET_TYPE_ACIP_WEBRTC_SDP, 11},
99 [26] = {PACKET_TYPE_ACIP_WEBRTC_ICE, 12},
106 [0] = {PACKET_TYPE_AUDIO_BATCH, 2},
107 [1] = {PACKET_TYPE_PROTOCOL_VERSION, 0},
108 [2] = {PACKET_TYPE_AUDIO_OPUS_BATCH, 3},
109 [8] = {PACKET_TYPE_CLIENT_CAPABILITIES, 4},
110 [9] = {PACKET_TYPE_PING, 5},
111 [10] = {PACKET_TYPE_PONG, 6},
112 [11] = {PACKET_TYPE_CLIENT_JOIN, 7},
113 [12] = {PACKET_TYPE_CLIENT_LEAVE, 8},
114 [13] = {PACKET_TYPE_STREAM_START, 9},
115 [14] = {PACKET_TYPE_STREAM_STOP, 10},
116 [15] = {PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP, 16},
117 [17] = {PACKET_TYPE_CRYPTO_REKEY_REQUEST, 13},
118 [18] = {PACKET_TYPE_CRYPTO_REKEY_RESPONSE, 14},
119 [19] = {PACKET_TYPE_ERROR_MESSAGE, 12},
120 [20] = {PACKET_TYPE_REMOTE_LOG, 11},
121 [21] = {PACKET_TYPE_CRYPTO_REKEY_COMPLETE, 15},
122 [22] = {PACKET_TYPE_CRYPTO_AUTH_RESPONSE, 17},
123 [23] = {PACKET_TYPE_CRYPTO_NO_ENCRYPTION, 18},
124 [25] = {PACKET_TYPE_IMAGE_FRAME, 1},
133static asciichat_error_t handle_client_ascii_frame(
const void *payload,
size_t payload_len,
134 const acip_client_callbacks_t *callbacks);
135static asciichat_error_t handle_client_audio_batch(
const void *payload,
size_t payload_len,
136 const acip_client_callbacks_t *callbacks);
137static asciichat_error_t handle_client_audio_opus_batch(
const void *payload,
size_t payload_len,
138 const acip_client_callbacks_t *callbacks);
139static asciichat_error_t handle_client_server_state(
const void *payload,
size_t payload_len,
140 const acip_client_callbacks_t *callbacks);
141static asciichat_error_t handle_client_error_message(
const void *payload,
size_t payload_len,
142 const acip_client_callbacks_t *callbacks);
143static asciichat_error_t handle_client_remote_log(
const void *payload,
size_t payload_len,
144 const acip_client_callbacks_t *callbacks);
145static asciichat_error_t handle_client_ping(
const void *payload,
size_t payload_len,
146 const acip_client_callbacks_t *callbacks);
147static asciichat_error_t handle_client_pong(
const void *payload,
size_t payload_len,
148 const acip_client_callbacks_t *callbacks);
149static asciichat_error_t handle_client_clear_console(
const void *payload,
size_t payload_len,
150 const acip_client_callbacks_t *callbacks);
151static asciichat_error_t handle_client_crypto_rekey_request(
const void *payload,
size_t payload_len,
152 const acip_client_callbacks_t *callbacks);
153static asciichat_error_t handle_client_crypto_rekey_response(
const void *payload,
size_t payload_len,
154 const acip_client_callbacks_t *callbacks);
155static asciichat_error_t handle_client_webrtc_sdp(
const void *payload,
size_t payload_len,
156 const acip_client_callbacks_t *callbacks);
157static asciichat_error_t handle_client_webrtc_ice(
const void *payload,
size_t payload_len,
158 const acip_client_callbacks_t *callbacks);
159static asciichat_error_t handle_client_session_joined(
const void *payload,
size_t payload_len,
160 const acip_client_callbacks_t *callbacks);
161static asciichat_error_t handle_client_crypto_key_exchange_init(
const void *payload,
size_t payload_len,
162 const acip_client_callbacks_t *callbacks);
163static asciichat_error_t handle_client_crypto_auth_challenge(
const void *payload,
size_t payload_len,
164 const acip_client_callbacks_t *callbacks);
165static asciichat_error_t handle_client_crypto_server_auth_resp(
const void *payload,
size_t payload_len,
166 const acip_client_callbacks_t *callbacks);
167static asciichat_error_t handle_client_crypto_auth_failed(
const void *payload,
size_t payload_len,
168 const acip_client_callbacks_t *callbacks);
169static asciichat_error_t handle_client_crypto_handshake_complete(
const void *payload,
size_t payload_len,
170 const acip_client_callbacks_t *callbacks);
174 handle_client_ascii_frame,
175 handle_client_audio_batch,
176 handle_client_audio_opus_batch,
177 handle_client_server_state,
178 handle_client_error_message,
179 handle_client_remote_log,
182 handle_client_clear_console,
183 handle_client_crypto_rekey_request,
184 handle_client_crypto_rekey_response,
185 handle_client_webrtc_sdp,
186 handle_client_webrtc_ice,
187 handle_client_session_joined,
188 handle_client_crypto_key_exchange_init,
189 handle_client_crypto_auth_challenge,
190 handle_client_crypto_server_auth_resp,
191 handle_client_crypto_auth_failed,
192 handle_client_crypto_handshake_complete,
196 size_t payload_len,
const acip_client_callbacks_t *callbacks) {
197 if (!transport || !callbacks) {
198 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid transport or callbacks");
203 int idx = handler_hash_lookup(g_client_handler_hash, type);
205 return SET_ERRNO(ERROR_INVALID_PARAM,
"Unhandled client packet type: %d", type);
208 return g_client_handlers[idx](payload, payload_len, callbacks);
215static asciichat_error_t handle_client_ascii_frame(
const void *payload,
size_t payload_len,
216 const acip_client_callbacks_t *callbacks) {
217 if (!callbacks->on_ascii_frame) {
221 if (payload_len <
sizeof(ascii_frame_packet_t)) {
222 return SET_ERRNO(ERROR_INVALID_PARAM,
"ASCII_FRAME payload too small");
226 ascii_frame_packet_t header;
227 memcpy(&header, payload,
sizeof(header));
230 header.width = NET_TO_HOST_U32(header.width);
231 header.height = NET_TO_HOST_U32(header.height);
232 header.original_size = NET_TO_HOST_U32(header.original_size);
233 header.compressed_size = NET_TO_HOST_U32(header.compressed_size);
234 header.checksum = NET_TO_HOST_U32(header.checksum);
235 header.flags = NET_TO_HOST_U32(header.flags);
238 const void *frame_data = (
const uint8_t *)payload +
sizeof(ascii_frame_packet_t);
239 size_t frame_data_len = payload_len -
sizeof(ascii_frame_packet_t);
242 if (header.width == 0 || header.height == 0) {
243 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid frame dimensions: %ux%u (width and height must be > 0)",
244 header.width, header.height);
249 const uint32_t MAX_WIDTH = 16384;
250 const uint32_t MAX_HEIGHT = 16384;
251 if (header.width > MAX_WIDTH || header.height > MAX_HEIGHT) {
252 return SET_ERRNO(ERROR_INVALID_PARAM,
"Frame dimensions too large: %ux%u (max: %ux%u)", header.width, header.height,
253 MAX_WIDTH, MAX_HEIGHT);
256 callbacks->on_ascii_frame(&header, frame_data, frame_data_len, callbacks->app_ctx);
260static asciichat_error_t handle_client_audio_batch(
const void *payload,
size_t payload_len,
261 const acip_client_callbacks_t *callbacks) {
262 if (!callbacks->on_audio_batch) {
266 if (payload_len <
sizeof(audio_batch_packet_t)) {
267 return SET_ERRNO(ERROR_INVALID_PARAM,
"AUDIO_BATCH payload too small");
271 const audio_batch_packet_t *batch_header = (
const audio_batch_packet_t *)payload;
272 uint32_t batch_count = NET_TO_HOST_U32(batch_header->batch_count);
273 uint32_t total_samples = NET_TO_HOST_U32(batch_header->total_samples);
274 uint32_t sample_rate = NET_TO_HOST_U32(batch_header->sample_rate);
275 uint32_t channels = NET_TO_HOST_U32(batch_header->channels);
279 if (sample_rate == 0 || (sample_rate < 8000 || sample_rate > 192000) ||
280 (sample_rate != 8000 && sample_rate != 16000 && sample_rate != 24000 && sample_rate != 32000 &&
281 sample_rate != 44100 && sample_rate != 48000 && sample_rate != 96000 && sample_rate != 192000)) {
284 "Invalid audio sample rate: %u Hz (expected: 8000, 16000, 24000, 32000, 44100, 48000, 96000, or 192000)",
289 if (channels == 0 || channels > 8) {
290 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid audio channel count: %u (expected: 1-8)", channels);
297 size_t expected_size =
sizeof(audio_batch_packet_t) + (total_samples *
sizeof(uint32_t));
298 if (payload_len != expected_size) {
299 return SET_ERRNO(ERROR_INVALID_PARAM,
"AUDIO_BATCH size mismatch");
303 const uint8_t *samples_ptr = (
const uint8_t *)payload +
sizeof(audio_batch_packet_t);
306 float *samples = SAFE_MALLOC(total_samples *
sizeof(
float),
float *);
308 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate audio batch buffer");
312 if (result != ASCIICHAT_OK) {
318 audio_batch_packet_t header_copy = *batch_header;
319 header_copy.batch_count = batch_count;
320 header_copy.total_samples = total_samples;
321 header_copy.sample_rate = sample_rate;
322 header_copy.channels = channels;
324 callbacks->on_audio_batch(&header_copy, samples, total_samples, callbacks->app_ctx);
330static asciichat_error_t handle_client_audio_opus_batch(
const void *payload,
size_t payload_len,
331 const acip_client_callbacks_t *callbacks) {
332 if (!callbacks->on_audio_opus_batch) {
337 callbacks->on_audio_opus_batch(payload, payload_len, callbacks->app_ctx);
341static asciichat_error_t handle_client_server_state(
const void *payload,
size_t payload_len,
342 const acip_client_callbacks_t *callbacks) {
343 if (!callbacks->on_server_state) {
347 if (payload_len !=
sizeof(server_state_packet_t)) {
348 return SET_ERRNO(ERROR_INVALID_PARAM,
"SERVER_STATE size mismatch");
351 callbacks->on_server_state((
const server_state_packet_t *)payload, callbacks->app_ctx);
355static asciichat_error_t handle_client_error_message(
const void *payload,
size_t payload_len,
356 const acip_client_callbacks_t *callbacks) {
357 if (!callbacks->on_error) {
361 if (payload_len <
sizeof(error_packet_t)) {
362 return SET_ERRNO(ERROR_INVALID_PARAM,
"ERROR_MESSAGE payload too small");
365 const error_packet_t *header = (
const error_packet_t *)payload;
366 const char *message = (
const char *)payload +
sizeof(error_packet_t);
367 size_t msg_len = payload_len -
sizeof(error_packet_t);
370 char msg_buffer[MAX_ERROR_MESSAGE_LENGTH + 1];
371 size_t copy_len = msg_len < MAX_ERROR_MESSAGE_LENGTH ? msg_len : MAX_ERROR_MESSAGE_LENGTH;
372 memcpy(msg_buffer, message, copy_len);
373 msg_buffer[copy_len] =
'\0';
375 callbacks->on_error(header, msg_buffer, callbacks->app_ctx);
379static asciichat_error_t handle_client_remote_log(
const void *payload,
size_t payload_len,
380 const acip_client_callbacks_t *callbacks) {
381 if (!callbacks->on_remote_log) {
385 if (payload_len <
sizeof(remote_log_packet_t)) {
386 return SET_ERRNO(ERROR_INVALID_PARAM,
"REMOTE_LOG payload too small");
389 const remote_log_packet_t *header = (
const remote_log_packet_t *)payload;
390 const char *message = (
const char *)payload +
sizeof(remote_log_packet_t);
391 size_t msg_len = payload_len -
sizeof(remote_log_packet_t);
394 char msg_buffer[512];
395 size_t copy_len = msg_len <
sizeof(msg_buffer) - 1 ? msg_len : sizeof(msg_buffer) - 1;
396 memcpy(msg_buffer, message, copy_len);
397 msg_buffer[copy_len] =
'\0';
399 callbacks->on_remote_log(header, msg_buffer, callbacks->app_ctx);
403static asciichat_error_t handle_client_ping(
const void *payload,
size_t payload_len,
404 const acip_client_callbacks_t *callbacks) {
408 if (callbacks->on_ping) {
409 callbacks->on_ping(callbacks->app_ctx);
414static asciichat_error_t handle_client_pong(
const void *payload,
size_t payload_len,
415 const acip_client_callbacks_t *callbacks) {
419 if (callbacks->on_pong) {
420 callbacks->on_pong(callbacks->app_ctx);
425static asciichat_error_t handle_client_clear_console(
const void *payload,
size_t payload_len,
426 const acip_client_callbacks_t *callbacks) {
430 if (callbacks->on_clear_console) {
431 callbacks->on_clear_console(callbacks->app_ctx);
436static asciichat_error_t handle_client_crypto_rekey_request(
const void *payload,
size_t payload_len,
437 const acip_client_callbacks_t *callbacks) {
438 if (callbacks->on_crypto_rekey_request) {
439 callbacks->on_crypto_rekey_request(payload, payload_len, callbacks->app_ctx);
444static asciichat_error_t handle_client_crypto_rekey_response(
const void *payload,
size_t payload_len,
445 const acip_client_callbacks_t *callbacks) {
446 if (callbacks->on_crypto_rekey_response) {
447 callbacks->on_crypto_rekey_response(payload, payload_len, callbacks->app_ctx);
452static asciichat_error_t handle_client_webrtc_sdp(
const void *payload,
size_t payload_len,
453 const acip_client_callbacks_t *callbacks) {
454 if (!callbacks->on_webrtc_sdp) {
458 if (payload_len <
sizeof(acip_webrtc_sdp_t)) {
459 return SET_ERRNO(ERROR_INVALID_PARAM,
"WEBRTC_SDP payload too small");
462 const acip_webrtc_sdp_t *sdp = (
const acip_webrtc_sdp_t *)payload;
463 callbacks->on_webrtc_sdp(sdp, payload_len, callbacks->app_ctx);
467static asciichat_error_t handle_client_webrtc_ice(
const void *payload,
size_t payload_len,
468 const acip_client_callbacks_t *callbacks) {
469 if (!callbacks->on_webrtc_ice) {
473 if (payload_len <
sizeof(acip_webrtc_ice_t)) {
474 return SET_ERRNO(ERROR_INVALID_PARAM,
"WEBRTC_ICE payload too small");
477 const acip_webrtc_ice_t *ice = (
const acip_webrtc_ice_t *)payload;
478 callbacks->on_webrtc_ice(ice, payload_len, callbacks->app_ctx);
482static asciichat_error_t handle_client_session_joined(
const void *payload,
size_t payload_len,
483 const acip_client_callbacks_t *callbacks) {
484 if (!callbacks->on_session_joined) {
488 if (payload_len <
sizeof(acip_session_joined_t)) {
489 return SET_ERRNO(ERROR_INVALID_PARAM,
"SESSION_JOINED payload too small (got %zu, need %zu)", payload_len,
490 sizeof(acip_session_joined_t));
494 const acip_session_joined_t *joined = (
const acip_session_joined_t *)payload;
497 if (joined->success) {
498 log_debug(
"Session join succeeded: session_id=%.16s, participant_id=%.16s, server=%s:%u, type=%s",
499 (
const char *)joined->session_id, (
const char *)joined->participant_id, joined->server_address,
500 joined->server_port, joined->session_type == 1 ?
"WebRTC" :
"DirectTCP");
502 log_warn(
"Session join failed: error %d: %s", joined->error_code, joined->error_message);
506 callbacks->on_session_joined(joined, callbacks->app_ctx);
510static asciichat_error_t handle_client_crypto_key_exchange_init(
const void *payload,
size_t payload_len,
511 const acip_client_callbacks_t *callbacks) {
512 if (!callbacks->on_crypto_key_exchange_init) {
517 callbacks->on_crypto_key_exchange_init(PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT, payload, payload_len,
522static asciichat_error_t handle_client_crypto_auth_challenge(
const void *payload,
size_t payload_len,
523 const acip_client_callbacks_t *callbacks) {
524 if (!callbacks->on_crypto_auth_challenge) {
529 callbacks->on_crypto_auth_challenge(PACKET_TYPE_CRYPTO_AUTH_CHALLENGE, payload, payload_len, callbacks->app_ctx);
533static asciichat_error_t handle_client_crypto_server_auth_resp(
const void *payload,
size_t payload_len,
534 const acip_client_callbacks_t *callbacks) {
535 if (!callbacks->on_crypto_server_auth_resp) {
540 callbacks->on_crypto_server_auth_resp(PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP, payload, payload_len, callbacks->app_ctx);
544static asciichat_error_t handle_client_crypto_auth_failed(
const void *payload,
size_t payload_len,
545 const acip_client_callbacks_t *callbacks) {
546 if (!callbacks->on_crypto_auth_failed) {
551 callbacks->on_crypto_auth_failed(PACKET_TYPE_CRYPTO_AUTH_FAILED, payload, payload_len, callbacks->app_ctx);
555static asciichat_error_t handle_client_crypto_handshake_complete(
const void *payload,
size_t payload_len,
556 const acip_client_callbacks_t *callbacks) {
557 if (!callbacks->on_crypto_handshake_complete) {
562 callbacks->on_crypto_handshake_complete(PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE, payload, payload_len,
572static asciichat_error_t handle_server_image_frame(
const void *payload,
size_t payload_len,
void *client_ctx,
573 const acip_server_callbacks_t *callbacks);
574static asciichat_error_t handle_server_audio_batch(
const void *payload,
size_t payload_len,
void *client_ctx,
575 const acip_server_callbacks_t *callbacks);
576static asciichat_error_t handle_server_audio_opus_batch(
const void *payload,
size_t payload_len,
void *client_ctx,
577 const acip_server_callbacks_t *callbacks);
578static asciichat_error_t handle_server_client_join(
const void *payload,
size_t payload_len,
void *client_ctx,
579 const acip_server_callbacks_t *callbacks);
580static asciichat_error_t handle_server_client_leave(
const void *payload,
size_t payload_len,
void *client_ctx,
581 const acip_server_callbacks_t *callbacks);
582static asciichat_error_t handle_server_stream_start(
const void *payload,
size_t payload_len,
void *client_ctx,
583 const acip_server_callbacks_t *callbacks);
584static asciichat_error_t handle_server_stream_stop(
const void *payload,
size_t payload_len,
void *client_ctx,
585 const acip_server_callbacks_t *callbacks);
586static asciichat_error_t handle_server_capabilities(
const void *payload,
size_t payload_len,
void *client_ctx,
587 const acip_server_callbacks_t *callbacks);
588static asciichat_error_t handle_server_ping(
const void *payload,
size_t payload_len,
void *client_ctx,
589 const acip_server_callbacks_t *callbacks);
590static asciichat_error_t handle_server_remote_log(
const void *payload,
size_t payload_len,
void *client_ctx,
591 const acip_server_callbacks_t *callbacks);
592static asciichat_error_t handle_server_protocol_version(
const void *payload,
size_t payload_len,
void *client_ctx,
593 const acip_server_callbacks_t *callbacks);
594static asciichat_error_t handle_server_pong(
const void *payload,
size_t payload_len,
void *client_ctx,
595 const acip_server_callbacks_t *callbacks);
596static asciichat_error_t handle_server_error_message(
const void *payload,
size_t payload_len,
void *client_ctx,
597 const acip_server_callbacks_t *callbacks);
598static asciichat_error_t handle_server_crypto_rekey_request(
const void *payload,
size_t payload_len,
void *client_ctx,
599 const acip_server_callbacks_t *callbacks);
600static asciichat_error_t handle_server_crypto_rekey_response(
const void *payload,
size_t payload_len,
void *client_ctx,
601 const acip_server_callbacks_t *callbacks);
602static asciichat_error_t handle_server_crypto_rekey_complete(
const void *payload,
size_t payload_len,
void *client_ctx,
603 const acip_server_callbacks_t *callbacks);
604static asciichat_error_t handle_server_crypto_key_exchange_resp(
const void *payload,
size_t payload_len,
606 const acip_server_callbacks_t *callbacks);
607static asciichat_error_t handle_server_crypto_auth_response(
const void *payload,
size_t payload_len,
void *client_ctx,
608 const acip_server_callbacks_t *callbacks);
609static asciichat_error_t handle_server_crypto_no_encryption(
const void *payload,
size_t payload_len,
void *client_ctx,
610 const acip_server_callbacks_t *callbacks);
614 handle_server_protocol_version,
615 handle_server_image_frame,
616 handle_server_audio_batch,
617 handle_server_audio_opus_batch,
618 handle_server_capabilities,
621 handle_server_client_join,
622 handle_server_client_leave,
623 handle_server_stream_start,
624 handle_server_stream_stop,
625 handle_server_remote_log,
626 handle_server_error_message,
627 handle_server_crypto_rekey_request,
628 handle_server_crypto_rekey_response,
629 handle_server_crypto_rekey_complete,
630 handle_server_crypto_key_exchange_resp,
631 handle_server_crypto_auth_response,
632 handle_server_crypto_no_encryption,
636 size_t payload_len,
void *client_ctx,
637 const acip_server_callbacks_t *callbacks) {
638 if (!transport || !callbacks) {
639 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid transport or callbacks");
644 log_info(
"ACIP_HANDLE: Received packet type=%d (0x%04x), payload_len=%zu", type, type, payload_len);
647 int idx = handler_hash_lookup(g_server_handler_hash, type);
649 log_error(
"ACIP_HANDLER_NOT_FOUND: No handler for packet type=%d (0x%04x)", type, type);
650 return SET_ERRNO(ERROR_INVALID_PARAM,
"Unhandled server packet type: %d", type);
653 log_info(
"ACIP_DISPATCH_HANDLER: type=%d, handler_idx=%d, calling handler...", type, idx);
654 asciichat_error_t result = g_server_handlers[idx](payload, payload_len, client_ctx, callbacks);
655 log_info(
"ACIP_HANDLER_RESULT: type=%d, result=%d", type, result);
663static asciichat_error_t handle_server_image_frame(
const void *payload,
size_t payload_len,
void *client_ctx,
664 const acip_server_callbacks_t *callbacks) {
665 log_info(
"ACIP_IMAGE_FRAME_HANDLER: Received IMAGE_FRAME packet, payload_len=%zu, client_ctx=%p", payload_len,
668 if (!callbacks->on_image_frame) {
669 log_warn(
"ACIP_IMAGE_FRAME_HANDLER: No callback registered for on_image_frame");
673 log_info(
"ACIP_IMAGE_FRAME_HANDLER: Callback is registered, checking payload size (need %zu bytes)",
674 sizeof(image_frame_packet_t));
676 if (payload_len <
sizeof(image_frame_packet_t)) {
677 log_error(
"ACIP_IMAGE_FRAME_HANDLER: Payload too small: %zu bytes (need %zu)", payload_len,
678 sizeof(image_frame_packet_t));
679 return SET_ERRNO(ERROR_INVALID_PARAM,
"IMAGE_FRAME payload too small: %zu bytes (need %zu)", payload_len,
680 sizeof(image_frame_packet_t));
684 const uint8_t *payload_bytes = (
const uint8_t *)payload;
685 log_dev_every(4500 * US_PER_MS_INT,
"IMAGE_FRAME payload (%zu bytes):", payload_len);
688 size_t max_bytes = (payload_len < 48) ? payload_len : 48;
689 for (
size_t i = 0; i < max_bytes && hex_pos <
sizeof(hex_buf) - 4; i++) {
690 hex_pos += snprintf(hex_buf + hex_pos,
sizeof(hex_buf) - hex_pos,
"%02x ", payload_bytes[i]);
692 log_dev_every(4500 * US_PER_MS_INT,
" First bytes: %s", hex_buf);
695 image_frame_packet_t header;
696 memcpy(&header, payload,
sizeof(header));
699 log_dev_every(4500 * US_PER_MS_INT,
"IMAGE_FRAME header (raw network order):");
700 log_dev_every(4500 * US_PER_MS_INT,
" width=0x%08x height=0x%08x pixel_format=0x%08x compressed_size=0x%08x",
701 header.width, header.height, header.pixel_format, header.compressed_size);
704 header.width = NET_TO_HOST_U32(header.width);
705 header.height = NET_TO_HOST_U32(header.height);
706 header.pixel_format = NET_TO_HOST_U32(header.pixel_format);
707 header.compressed_size = NET_TO_HOST_U32(header.compressed_size);
708 header.checksum = NET_TO_HOST_U32(header.checksum);
709 header.timestamp = NET_TO_HOST_U32(header.timestamp);
712 log_dev_every(4500 * US_PER_MS_INT,
"IMAGE_FRAME header (after host byte order):");
713 log_dev_every(4500 * US_PER_MS_INT,
" width=%u height=%u pixel_format=%u compressed_size=%u", header.width,
714 header.height, header.pixel_format, header.compressed_size);
717 const void *pixel_data = (
const uint8_t *)payload +
sizeof(image_frame_packet_t);
718 size_t pixel_data_len = payload_len -
sizeof(image_frame_packet_t);
721 if (header.width == 0 || header.height == 0) {
722 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid image dimensions: %ux%u (width and height must be > 0)",
723 header.width, header.height);
728 const uint32_t MAX_WIDTH = 8192;
729 const uint32_t MAX_HEIGHT = 8192;
730 if (header.width > MAX_WIDTH || header.height > MAX_HEIGHT) {
731 return SET_ERRNO(ERROR_INVALID_PARAM,
"Image dimensions too large: %ux%u (max: %ux%u)", header.width, header.height,
732 MAX_WIDTH, MAX_HEIGHT);
737 if (header.pixel_format == 0 || header.pixel_format > 4) {
738 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid pixel format: %u (expected: 1-4)", header.pixel_format);
741 callbacks->on_image_frame(&header, pixel_data, pixel_data_len, client_ctx, callbacks->app_ctx);
745static asciichat_error_t handle_server_audio_batch(
const void *payload,
size_t payload_len,
void *client_ctx,
746 const acip_server_callbacks_t *callbacks) {
747 if (!callbacks->on_audio_batch) {
751 if (payload_len <
sizeof(audio_batch_packet_t)) {
752 return SET_ERRNO(ERROR_INVALID_PARAM,
"AUDIO_BATCH payload too small");
756 const audio_batch_packet_t *batch_header = (
const audio_batch_packet_t *)payload;
757 uint32_t batch_count = NET_TO_HOST_U32(batch_header->batch_count);
758 uint32_t total_samples = NET_TO_HOST_U32(batch_header->total_samples);
759 uint32_t sample_rate = NET_TO_HOST_U32(batch_header->sample_rate);
760 uint32_t channels = NET_TO_HOST_U32(batch_header->channels);
763 size_t expected_size =
sizeof(audio_batch_packet_t) + (total_samples *
sizeof(uint32_t));
764 if (payload_len != expected_size) {
765 return SET_ERRNO(ERROR_INVALID_PARAM,
"AUDIO_BATCH size mismatch");
769 const uint8_t *samples_ptr = (
const uint8_t *)payload +
sizeof(audio_batch_packet_t);
772 float *samples = SAFE_MALLOC(total_samples *
sizeof(
float),
float *);
774 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate audio batch buffer");
778 if (result != ASCIICHAT_OK) {
784 audio_batch_packet_t header_copy = *batch_header;
785 header_copy.batch_count = batch_count;
786 header_copy.total_samples = total_samples;
787 header_copy.sample_rate = sample_rate;
788 header_copy.channels = channels;
790 callbacks->on_audio_batch(&header_copy, samples, total_samples, client_ctx, callbacks->app_ctx);
796static asciichat_error_t handle_server_audio_opus_batch(
const void *payload,
size_t payload_len,
void *client_ctx,
797 const acip_server_callbacks_t *callbacks) {
798 if (!callbacks->on_audio_opus_batch) {
802 callbacks->on_audio_opus_batch(payload, payload_len, client_ctx, callbacks->app_ctx);
806static asciichat_error_t handle_server_client_join(
const void *payload,
size_t payload_len,
void *client_ctx,
807 const acip_server_callbacks_t *callbacks) {
808 if (!callbacks->on_client_join) {
812 callbacks->on_client_join(payload, payload_len, client_ctx, callbacks->app_ctx);
816static asciichat_error_t handle_server_client_leave(
const void *payload,
size_t payload_len,
void *client_ctx,
817 const acip_server_callbacks_t *callbacks) {
821 if (!callbacks->on_client_leave) {
825 callbacks->on_client_leave(client_ctx, callbacks->app_ctx);
829static asciichat_error_t handle_server_stream_start(
const void *payload,
size_t payload_len,
void *client_ctx,
830 const acip_server_callbacks_t *callbacks) {
831 if (!callbacks->on_stream_start) {
835 if (payload_len <
sizeof(uint32_t)) {
836 return SET_ERRNO(ERROR_INVALID_PARAM,
"STREAM_START payload too small (got %zu, expected %zu)", payload_len,
841 uint32_t stream_types_net;
842 memcpy(&stream_types_net, payload,
sizeof(uint32_t));
843 uint32_t stream_types = NET_TO_HOST_U32(stream_types_net);
845 callbacks->on_stream_start(stream_types, client_ctx, callbacks->app_ctx);
849static asciichat_error_t handle_server_stream_stop(
const void *payload,
size_t payload_len,
void *client_ctx,
850 const acip_server_callbacks_t *callbacks) {
851 if (!callbacks->on_stream_stop) {
855 if (payload_len <
sizeof(uint32_t)) {
856 return SET_ERRNO(ERROR_INVALID_PARAM,
"STREAM_STOP payload too small (got %zu, expected %zu)", payload_len,
861 uint32_t stream_types_net;
862 memcpy(&stream_types_net, payload,
sizeof(uint32_t));
863 uint32_t stream_types = NET_TO_HOST_U32(stream_types_net);
865 callbacks->on_stream_stop(stream_types, client_ctx, callbacks->app_ctx);
869static asciichat_error_t handle_server_capabilities(
const void *payload,
size_t payload_len,
void *client_ctx,
870 const acip_server_callbacks_t *callbacks) {
871 log_debug(
"handle_server_capabilities: payload_len=%zu, client_ctx=%p, callbacks=%p", payload_len, client_ctx,
872 (
const void *)callbacks);
874 if (!callbacks->on_capabilities) {
875 log_error(
"on_capabilities callback is NULL!");
879 log_error(
"Calling on_capabilities callback...");
880 callbacks->on_capabilities(payload, payload_len, client_ctx, callbacks->app_ctx);
881 log_error(
"on_capabilities callback returned");
885static asciichat_error_t handle_server_ping(
const void *payload,
size_t payload_len,
void *client_ctx,
886 const acip_server_callbacks_t *callbacks) {
890 if (callbacks->on_ping) {
891 callbacks->on_ping(client_ctx, callbacks->app_ctx);
896static asciichat_error_t handle_server_remote_log(
const void *payload,
size_t payload_len,
void *client_ctx,
897 const acip_server_callbacks_t *callbacks) {
898 if (!callbacks->on_remote_log) {
902 if (payload_len <
sizeof(remote_log_packet_t)) {
903 return SET_ERRNO(ERROR_INVALID_PARAM,
"REMOTE_LOG payload too small");
906 const remote_log_packet_t *header = (
const remote_log_packet_t *)payload;
907 const char *message = (
const char *)payload +
sizeof(remote_log_packet_t);
908 size_t msg_len = payload_len -
sizeof(remote_log_packet_t);
911 char msg_buffer[512];
912 size_t copy_len = msg_len <
sizeof(msg_buffer) - 1 ? msg_len : sizeof(msg_buffer) - 1;
913 memcpy(msg_buffer, message, copy_len);
914 msg_buffer[copy_len] =
'\0';
916 callbacks->on_remote_log(header, msg_buffer, client_ctx, callbacks->app_ctx);
920static asciichat_error_t handle_server_protocol_version(
const void *payload,
size_t payload_len,
void *client_ctx,
921 const acip_server_callbacks_t *callbacks) {
922 if (!callbacks->on_protocol_version) {
926 if (payload_len !=
sizeof(protocol_version_packet_t)) {
927 return SET_ERRNO(ERROR_INVALID_PARAM,
"PROTOCOL_VERSION size mismatch");
930 callbacks->on_protocol_version((
const protocol_version_packet_t *)payload, client_ctx, callbacks->app_ctx);
934static asciichat_error_t handle_server_pong(
const void *payload,
size_t payload_len,
void *client_ctx,
935 const acip_server_callbacks_t *callbacks) {
939 if (callbacks->on_pong) {
940 callbacks->on_pong(client_ctx, callbacks->app_ctx);
945static asciichat_error_t handle_server_error_message(
const void *payload,
size_t payload_len,
void *client_ctx,
946 const acip_server_callbacks_t *callbacks) {
947 if (!callbacks->on_error) {
951 if (payload_len <
sizeof(error_packet_t)) {
952 return SET_ERRNO(ERROR_INVALID_PARAM,
"ERROR_MESSAGE payload too small");
955 const error_packet_t *header = (
const error_packet_t *)payload;
956 const char *message = (
const char *)payload +
sizeof(error_packet_t);
957 size_t msg_len = payload_len -
sizeof(error_packet_t);
960 char msg_buffer[MAX_ERROR_MESSAGE_LENGTH + 1];
961 size_t copy_len = msg_len < MAX_ERROR_MESSAGE_LENGTH ? msg_len : MAX_ERROR_MESSAGE_LENGTH;
962 memcpy(msg_buffer, message, copy_len);
963 msg_buffer[copy_len] =
'\0';
965 callbacks->on_error(header, msg_buffer, client_ctx, callbacks->app_ctx);
969static asciichat_error_t handle_server_crypto_rekey_request(
const void *payload,
size_t payload_len,
void *client_ctx,
970 const acip_server_callbacks_t *callbacks) {
971 if (callbacks->on_crypto_rekey_request) {
972 callbacks->on_crypto_rekey_request(payload, payload_len, client_ctx, callbacks->app_ctx);
977static asciichat_error_t handle_server_crypto_rekey_response(
const void *payload,
size_t payload_len,
void *client_ctx,
978 const acip_server_callbacks_t *callbacks) {
979 if (callbacks->on_crypto_rekey_response) {
980 callbacks->on_crypto_rekey_response(payload, payload_len, client_ctx, callbacks->app_ctx);
985static asciichat_error_t handle_server_crypto_rekey_complete(
const void *payload,
size_t payload_len,
void *client_ctx,
986 const acip_server_callbacks_t *callbacks) {
987 if (callbacks->on_crypto_rekey_complete) {
988 callbacks->on_crypto_rekey_complete(payload, payload_len, client_ctx, callbacks->app_ctx);
993static asciichat_error_t handle_server_crypto_key_exchange_resp(
const void *payload,
size_t payload_len,
995 const acip_server_callbacks_t *callbacks) {
996 if (!callbacks->on_crypto_key_exchange_resp) {
1001 callbacks->on_crypto_key_exchange_resp(PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP, payload, payload_len, client_ctx,
1002 callbacks->app_ctx);
1003 return ASCIICHAT_OK;
1006static asciichat_error_t handle_server_crypto_auth_response(
const void *payload,
size_t payload_len,
void *client_ctx,
1007 const acip_server_callbacks_t *callbacks) {
1008 if (!callbacks->on_crypto_auth_response) {
1009 return ASCIICHAT_OK;
1013 callbacks->on_crypto_auth_response(PACKET_TYPE_CRYPTO_AUTH_RESPONSE, payload, payload_len, client_ctx,
1014 callbacks->app_ctx);
1015 return ASCIICHAT_OK;
1018static asciichat_error_t handle_server_crypto_no_encryption(
const void *payload,
size_t payload_len,
void *client_ctx,
1019 const acip_server_callbacks_t *callbacks) {
1020 if (!callbacks->on_crypto_no_encryption) {
1021 return ASCIICHAT_OK;
1025 callbacks->on_crypto_no_encryption(PACKET_TYPE_CRYPTO_NO_ENCRYPTION, payload, payload_len, client_ctx,
1026 callbacks->app_ctx);
1027 return ASCIICHAT_OK;
asciichat_error_t audio_dequantize_samples(const uint8_t *samples_ptr, uint32_t total_samples, float *out_samples)
asciichat_error_t acip_handle_server_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, void *client_ctx, const acip_server_callbacks_t *callbacks)
asciichat_error_t acip_handle_client_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, const acip_client_callbacks_t *callbacks)
asciichat_error_t(* acip_client_handler_func_t)(const void *payload, size_t payload_len, const acip_client_callbacks_t *callbacks)
#define HANDLER_HASH(type)
asciichat_error_t(* acip_server_handler_func_t)(const void *payload, size_t payload_len, void *client_ctx, const acip_server_callbacks_t *callbacks)
#define CLIENT_HANDLER_COUNT
#define HANDLER_HASH_SIZE
#define SERVER_HANDLER_COUNT
Hash table entry for packet type to handler mapping.
uint8_t handler_idx
Handler index (0-based)
packet_type_t key
Packet type (0 = empty slot)