diff --git a/lib/include/chiaki/feedback.h b/lib/include/chiaki/feedback.h index be66384..d0be7a1 100644 --- a/lib/include/chiaki/feedback.h +++ b/lib/include/chiaki/feedback.h @@ -38,7 +38,7 @@ CHIAKI_EXPORT void chiaki_feedback_state_format_v9(uint8_t *buf, ChiakiFeedbackS /** * @param buf buffer of at least CHIAKI_FEEDBACK_STATE_BUF_SIZE_V12 */ -CHIAKI_EXPORT void chiaki_feedback_state_format_v12(uint8_t *buf, ChiakiFeedbackState *state); +CHIAKI_EXPORT void chiaki_feedback_state_format_v12(uint8_t *buf, ChiakiFeedbackState *state, bool enable_dualsense); #define CHIAKI_HISTORY_EVENT_SIZE_MAX 0x5 diff --git a/lib/include/chiaki/session.h b/lib/include/chiaki/session.h index 3a60417..4f9f932 100644 --- a/lib/include/chiaki/session.h +++ b/lib/include/chiaki/session.h @@ -78,6 +78,7 @@ typedef struct chiaki_connect_info_t ChiakiConnectVideoProfile video_profile; bool video_profile_auto_downgrade; // Downgrade video_profile if server does not seem to support it. bool enable_keyboard; + bool enable_dualsense; } ChiakiConnectInfo; @@ -121,6 +122,14 @@ typedef struct chiaki_rumble_event_t uint8_t right; // high-frequency } ChiakiRumbleEvent; +typedef struct chiaki_trigger_effects_event_t +{ + uint8_t type_left; + uint8_t type_right; + uint8_t left[10]; + uint8_t right[10]; +} ChiakiTriggerEffectsEvent; + typedef enum { CHIAKI_EVENT_CONNECTED, CHIAKI_EVENT_LOGIN_PIN_REQUEST, @@ -129,6 +138,7 @@ typedef enum { CHIAKI_EVENT_KEYBOARD_REMOTE_CLOSE, CHIAKI_EVENT_RUMBLE, CHIAKI_EVENT_QUIT, + CHIAKI_EVENT_TRIGGER_EFFECTS, } ChiakiEventType; typedef struct chiaki_event_t @@ -139,6 +149,7 @@ typedef struct chiaki_event_t ChiakiQuitEvent quit; ChiakiKeyboardEvent keyboard; ChiakiRumbleEvent rumble; + ChiakiTriggerEffectsEvent trigger_effects; struct { bool pin_incorrect; // false on first request, true if the pin entered before was incorrect @@ -170,6 +181,7 @@ typedef struct chiaki_session_t ChiakiConnectVideoProfile video_profile; bool video_profile_auto_downgrade; bool enable_keyboard; + bool enable_dualsense; } connect_info; ChiakiTarget target; @@ -191,6 +203,7 @@ typedef struct chiaki_session_t ChiakiVideoSampleCallback video_sample_cb; void *video_sample_cb_user; ChiakiAudioSink audio_sink; + ChiakiAudioSink haptics_sink; ChiakiThread session_thread; @@ -246,6 +259,14 @@ static inline void chiaki_session_set_audio_sink(ChiakiSession *session, ChiakiA session->audio_sink = *sink; } +/** + * @param sink contents are copied + */ +static inline void chiaki_session_set_haptics_sink(ChiakiSession *session, ChiakiAudioSink *sink) +{ + session->haptics_sink = *sink; +} + #ifdef __cplusplus } #endif diff --git a/lib/include/chiaki/streamconnection.h b/lib/include/chiaki/streamconnection.h index ba1817a..90578c6 100644 --- a/lib/include/chiaki/streamconnection.h +++ b/lib/include/chiaki/streamconnection.h @@ -32,6 +32,7 @@ typedef struct chiaki_stream_connection_t ChiakiPacketStats packet_stats; ChiakiAudioReceiver *audio_receiver; ChiakiVideoReceiver *video_receiver; + ChiakiAudioReceiver *haptics_receiver; ChiakiFeedbackSender feedback_sender; /** diff --git a/lib/include/chiaki/takion.h b/lib/include/chiaki/takion.h index 1715842..15d62e5 100644 --- a/lib/include/chiaki/takion.h +++ b/lib/include/chiaki/takion.h @@ -27,7 +27,8 @@ extern "C" { typedef enum chiaki_takion_message_data_type_t { CHIAKI_TAKION_MESSAGE_DATA_TYPE_PROTOBUF = 0, CHIAKI_TAKION_MESSAGE_DATA_TYPE_RUMBLE = 7, - CHIAKI_TAKION_MESSAGE_DATA_TYPE_9 = 9 + CHIAKI_TAKION_MESSAGE_DATA_TYPE_9 = 9, + CHIAKI_TAKION_MESSAGE_DATA_TYPE_TRIGGER_EFFECTS = 11, } ChiakiTakionMessageDataType; typedef struct chiaki_takion_av_packet_t @@ -36,6 +37,7 @@ typedef struct chiaki_takion_av_packet_t ChiakiSeqNum16 frame_index; bool uses_nalu_info_structs; bool is_video; + bool is_haptics; ChiakiSeqNum16 unit_index; uint16_t units_in_frame_total; // source + units_in_frame_fec uint16_t units_in_frame_fec; @@ -46,8 +48,6 @@ typedef struct chiaki_takion_av_packet_t uint64_t key_pos; - uint8_t byte_before_audio_data; - uint8_t *data; // not owned size_t data_size; } ChiakiTakionAVPacket; @@ -106,6 +106,7 @@ typedef struct chiaki_takion_connect_info_t ChiakiTakionCallback cb; void *cb_user; bool enable_crypt; + bool enable_dualsense; uint8_t protocol_version; } ChiakiTakionConnectInfo; @@ -162,6 +163,8 @@ typedef struct chiaki_takion_t ChiakiTakionAVPacketParse av_packet_parse; ChiakiKeyState key_state; + + bool enable_dualsense; } ChiakiTakion; diff --git a/lib/protobuf/takion.proto b/lib/protobuf/takion.proto index 748d70a..ceef0f1 100644 --- a/lib/protobuf/takion.proto +++ b/lib/protobuf/takion.proto @@ -312,7 +312,8 @@ message ControllerConnectionPayload { VITA = 3; XINPUT = 4; MOBILE = 5; - BOND = 6; + DUALSENSE = 6; + VR2SENSE = 7; } } diff --git a/lib/src/audioreceiver.c b/lib/src/audioreceiver.c index 9fec69d..744d807 100644 --- a/lib/src/audioreceiver.c +++ b/lib/src/audioreceiver.c @@ -5,7 +5,7 @@ #include -static void chiaki_audio_receiver_frame(ChiakiAudioReceiver *audio_receiver, ChiakiSeqNum16 frame_index, uint8_t *buf, size_t buf_size); +static void chiaki_audio_receiver_frame(ChiakiAudioReceiver *audio_receiver, ChiakiSeqNum16 frame_index, bool is_haptics, uint8_t *buf, size_t buf_size); CHIAKI_EXPORT ChiakiErrorCode chiaki_audio_receiver_init(ChiakiAudioReceiver *audio_receiver, ChiakiSession *session, ChiakiPacketStats *packet_stats) { @@ -102,14 +102,14 @@ CHIAKI_EXPORT void chiaki_audio_receiver_av_packet(ChiakiAudioReceiver *audio_re frame_index = packet->frame_index - fec_units_count + fec_index; } - chiaki_audio_receiver_frame(audio_receiver, frame_index, packet->data + unit_size * i, unit_size); + chiaki_audio_receiver_frame(audio_receiver, frame_index, packet->is_haptics, packet->data + unit_size * i, unit_size); } if(audio_receiver->packet_stats) chiaki_packet_stats_push_seq(audio_receiver->packet_stats, packet->frame_index); } -static void chiaki_audio_receiver_frame(ChiakiAudioReceiver *audio_receiver, ChiakiSeqNum16 frame_index, uint8_t *buf, size_t buf_size) +static void chiaki_audio_receiver_frame(ChiakiAudioReceiver *audio_receiver, ChiakiSeqNum16 frame_index, bool is_haptics, uint8_t *buf, size_t buf_size) { chiaki_mutex_lock(&audio_receiver->mutex); @@ -117,7 +117,9 @@ static void chiaki_audio_receiver_frame(ChiakiAudioReceiver *audio_receiver, Chi goto beach; audio_receiver->frame_index_prev = frame_index; - if(audio_receiver->session->audio_sink.frame_cb) + if(is_haptics && audio_receiver->session->haptics_sink.frame_cb) + audio_receiver->session->haptics_sink.frame_cb(buf, buf_size, audio_receiver->session->haptics_sink.user); + else if(!is_haptics && audio_receiver->session->audio_sink.frame_cb) audio_receiver->session->audio_sink.frame_cb(buf, buf_size, audio_receiver->session->audio_sink.user); beach: diff --git a/lib/src/ctrl.c b/lib/src/ctrl.c index 9dfc277..29a39b2 100644 --- a/lib/src/ctrl.c +++ b/lib/src/ctrl.c @@ -488,17 +488,25 @@ static void ctrl_message_received(ChiakiCtrl *ctrl, uint16_t msg_type, uint8_t * static void ctrl_enable_optional_features(ChiakiCtrl *ctrl) { - if(!ctrl->session->connect_info.enable_keyboard) - return; - // TODO: Last byte of pre_enable request is random (?) - // TODO: Signature ?! - uint8_t enable = 1; - uint8_t pre_enable[4] = { 0x00, 0x01, 0x01, 0x80 }; - uint8_t signature[0x10] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - ctrl_message_send(ctrl, 0xD, signature, 0x10); - ctrl_message_send(ctrl, 0x36, pre_enable, 4); - ctrl_message_send(ctrl, CTRL_MESSAGE_TYPE_KEYBOARD_ENABLE_TOGGLE, &enable, 1); - ctrl_message_send(ctrl, 0x36, pre_enable, 4); + if(ctrl->session->connect_info.enable_dualsense) + { + CHIAKI_LOGI(ctrl->session->log, "Enabling DualSense features"); + const uint8_t enable[3] = { 0x00, 0x40, 0x00 }; + ctrl_message_send(ctrl, 0x13, enable, 3); + } + if(ctrl->session->connect_info.enable_keyboard) + { + CHIAKI_LOGI(ctrl->session->log, "Enabling Keyboard"); + // TODO: Last byte of pre_enable request is random (?) + // TODO: Signature ?! + uint8_t enable = 1; + uint8_t pre_enable[4] = { 0x00, 0x01, 0x01, 0x80 }; + uint8_t signature[0x10] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + ctrl_message_send(ctrl, 0xD, signature, 0x10); + ctrl_message_send(ctrl, 0x36, pre_enable, 4); + ctrl_message_send(ctrl, CTRL_MESSAGE_TYPE_KEYBOARD_ENABLE_TOGGLE, &enable, 1); + ctrl_message_send(ctrl, 0x36, pre_enable, 4); + } } static void ctrl_message_received_session_id(ChiakiCtrl *ctrl, uint8_t *payload, size_t payload_size) diff --git a/lib/src/feedback.c b/lib/src/feedback.c index 1ae190e..d585a88 100644 --- a/lib/src/feedback.c +++ b/lib/src/feedback.c @@ -76,12 +76,12 @@ CHIAKI_EXPORT void chiaki_feedback_state_format_v9(uint8_t *buf, ChiakiFeedbackS *((chiaki_unaligned_uint16_t *)(buf + 0x17)) = htons((uint16_t)state->right_y); } -CHIAKI_EXPORT void chiaki_feedback_state_format_v12(uint8_t *buf, ChiakiFeedbackState *state) +CHIAKI_EXPORT void chiaki_feedback_state_format_v12(uint8_t *buf, ChiakiFeedbackState *state, bool enable_dualsense) { chiaki_feedback_state_format_v9(buf, state); buf[0x19] = 0x0; buf[0x1a] = 0x0; - buf[0x1b] = 0x1; // 1 for Shock, 0 for Sense + buf[0x1b] = enable_dualsense ? 0x0 : 0x1; } CHIAKI_EXPORT ChiakiErrorCode chiaki_feedback_history_event_set_button(ChiakiFeedbackHistoryEvent *event, uint64_t button, uint8_t state) diff --git a/lib/src/session.c b/lib/src/session.c index b5b928f..ea8e09d 100644 --- a/lib/src/session.c +++ b/lib/src/session.c @@ -227,6 +227,7 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_init(ChiakiSession *session, Chiaki session->connect_info.video_profile = connect_info->video_profile; session->connect_info.video_profile_auto_downgrade = connect_info->video_profile_auto_downgrade; session->connect_info.enable_keyboard = connect_info->enable_keyboard; + session->connect_info.enable_dualsense = connect_info->enable_dualsense; return CHIAKI_ERR_SUCCESS; error_stop_pipe: diff --git a/lib/src/streamconnection.c b/lib/src/streamconnection.c index 2a7bb6f..7187c1b 100644 --- a/lib/src/streamconnection.c +++ b/lib/src/streamconnection.c @@ -47,7 +47,9 @@ static void stream_connection_takion_cb(ChiakiTakionEvent *event, void *user); static void stream_connection_takion_data(ChiakiStreamConnection *stream_connection, ChiakiTakionMessageDataType data_type, uint8_t *buf, size_t buf_size); static void stream_connection_takion_data_protobuf(ChiakiStreamConnection *stream_connection, uint8_t *buf, size_t buf_size); static void stream_connection_takion_data_rumble(ChiakiStreamConnection *stream_connection, uint8_t *buf, size_t buf_size); +static void stream_connection_takion_data_trigger_effects(ChiakiStreamConnection *stream_connection, uint8_t *buf, size_t buf_size); static ChiakiErrorCode stream_connection_send_big(ChiakiStreamConnection *stream_connection); +static ChiakiErrorCode stream_connection_send_controller_connection(ChiakiStreamConnection *stream_connection); static ChiakiErrorCode stream_connection_send_disconnect(ChiakiStreamConnection *stream_connection); static void stream_connection_takion_data_idle(ChiakiStreamConnection *stream_connection, uint8_t *buf, size_t buf_size); static void stream_connection_takion_data_expect_bang(ChiakiStreamConnection *stream_connection, uint8_t *buf, size_t buf_size); @@ -79,6 +81,7 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_init(ChiakiStreamConnecti stream_connection->video_receiver = NULL; stream_connection->audio_receiver = NULL; + stream_connection->haptics_receiver = NULL; err = chiaki_mutex_init(&stream_connection->feedback_sender_mutex, false); if(err != CHIAKI_ERR_SUCCESS) @@ -143,6 +146,7 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_run(ChiakiStreamConnectio takion_info.ip_dontfrag = false; takion_info.enable_crypt = true; + takion_info.enable_dualsense = session->connect_info.enable_dualsense; takion_info.protocol_version = chiaki_target_is_ps5(session->target) ? 12 : 9; takion_info.cb = stream_connection_takion_cb; @@ -164,12 +168,20 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_run(ChiakiStreamConnectio return CHIAKI_ERR_UNKNOWN; } + stream_connection->haptics_receiver = chiaki_audio_receiver_new(session, NULL); + if(!stream_connection->haptics_receiver) + { + CHIAKI_LOGE(session->log, "StreamConnection failed to initialize Haptics Receiver"); + err = CHIAKI_ERR_UNKNOWN; + goto err_audio_receiver; + } + stream_connection->video_receiver = chiaki_video_receiver_new(session, &stream_connection->packet_stats); if(!stream_connection->video_receiver) { CHIAKI_LOGE(session->log, "StreamConnection failed to initialize Video Receiver"); err = CHIAKI_ERR_UNKNOWN; - goto err_audio_receiver; + goto err_haptics_receiver; } stream_connection->state = STATE_TAKION_CONNECT; @@ -321,6 +333,10 @@ err_video_receiver: chiaki_video_receiver_free(stream_connection->video_receiver); stream_connection->video_receiver = NULL; +err_haptics_receiver: + chiaki_audio_receiver_free(stream_connection->haptics_receiver); + stream_connection->haptics_receiver = NULL; + err_audio_receiver: chiaki_audio_receiver_free(stream_connection->audio_receiver); stream_connection->audio_receiver = NULL; @@ -376,6 +392,9 @@ static void stream_connection_takion_data(ChiakiStreamConnection *stream_connect case CHIAKI_TAKION_MESSAGE_DATA_TYPE_RUMBLE: stream_connection_takion_data_rumble(stream_connection, buf, buf_size); break; + case CHIAKI_TAKION_MESSAGE_DATA_TYPE_TRIGGER_EFFECTS: + stream_connection_takion_data_trigger_effects(stream_connection, buf, buf_size); + break; default: break; } @@ -415,6 +434,24 @@ static void stream_connection_takion_data_rumble(ChiakiStreamConnection *stream_ chiaki_session_send_event(stream_connection->session, &event); } + +static void stream_connection_takion_data_trigger_effects(ChiakiStreamConnection *stream_connection, uint8_t *buf, size_t buf_size) +{ + if(buf_size < 25) + { + CHIAKI_LOGE(stream_connection->log, "StreamConnection got trigger effects packet with size %#llx < 25", + (unsigned long long)buf_size); + return; + } + ChiakiEvent event = { 0 }; + event.type = CHIAKI_EVENT_TRIGGER_EFFECTS; + event.trigger_effects.type_left = buf[1]; + event.trigger_effects.type_right = buf[2]; + memcpy(&event.trigger_effects.left, buf + 5, 10); + memcpy(&event.trigger_effects.right, buf + 15, 10); + chiaki_session_send_event(stream_connection->session, &event); +} + static void stream_connection_takion_data_handle_disconnect(ChiakiStreamConnection *stream_connection, uint8_t *buf, size_t buf_size) { tkproto_TakionMessage msg; @@ -460,7 +497,7 @@ static void stream_connection_takion_data_idle(ChiakiStreamConnection *stream_co return; } - CHIAKI_LOGV(stream_connection->log, "StreamConnection received data"); + CHIAKI_LOGV(stream_connection->log, "StreamConnection received data with msg.type == %d", msg.type); chiaki_log_hexdump(stream_connection->log, CHIAKI_LOG_VERBOSE, buf, buf_size); if(msg.type == tkproto_TakionMessage_PayloadType_DISCONNECT) @@ -522,7 +559,8 @@ static void stream_connection_takion_data_expect_bang(ChiakiStreamConnection *st return; } - CHIAKI_LOGE(stream_connection->log, "StreamConnection expected bang payload but received something else"); + CHIAKI_LOGE(stream_connection->log, "StreamConnection expected bang payload but received something else: %d", msg.type); + chiaki_log_hexdump(stream_connection->log, CHIAKI_LOG_VERBOSE, buf, buf_size); return; } @@ -584,6 +622,12 @@ static void stream_connection_takion_data_expect_bang(ChiakiStreamConnection *st // stream_connection->state_mutex is expected to be locked by the caller of this function stream_connection->state_finished = true; chiaki_cond_signal(&stream_connection->state_cond); + err = stream_connection_send_controller_connection(stream_connection); + if(err != CHIAKI_ERR_SUCCESS) + { + CHIAKI_LOGE(stream_connection->log, "StreamConnection failed to send controller connection"); + goto error; + } return; error: stream_connection->state_failed = true; @@ -811,6 +855,37 @@ static ChiakiErrorCode stream_connection_send_big(ChiakiStreamConnection *stream return err; } +static ChiakiErrorCode stream_connection_send_controller_connection(ChiakiStreamConnection *stream_connection) +{ + ChiakiSession *session = stream_connection->session; + tkproto_TakionMessage msg; + memset(&msg, 0, sizeof(msg)); + + msg.type = tkproto_TakionMessage_PayloadType_CONTROLLERCONNECTION; + msg.has_controller_connection_payload = true; + msg.controller_connection_payload.has_connected = true; + msg.controller_connection_payload.connected = true; + msg.controller_connection_payload.has_controller_id = false; + msg.controller_connection_payload.has_controller_type = true; + msg.controller_connection_payload.controller_type = session->connect_info.enable_dualsense + ? tkproto_ControllerConnectionPayload_ControllerType_DUALSENSE + : tkproto_ControllerConnectionPayload_ControllerType_DUALSHOCK4; + + uint8_t buf[2048]; + size_t buf_size; + + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + bool pbr = pb_encode(&stream, tkproto_TakionMessage_fields, &msg); + if(!pbr) + { + CHIAKI_LOGE(stream_connection->log, "StreamConnection controller connection protobuf encoding failed"); + return CHIAKI_ERR_UNKNOWN; + } + + buf_size = stream.bytes_written; + return chiaki_takion_send_message_data(&stream_connection->takion, 1, 1, buf, buf_size, NULL); +} + static ChiakiErrorCode stream_connection_send_streaminfo_ack(ChiakiStreamConnection *stream_connection) { tkproto_TakionMessage msg; @@ -867,6 +942,8 @@ static void stream_connection_takion_av(ChiakiStreamConnection *stream_connectio if(packet->is_video) chiaki_video_receiver_av_packet(stream_connection->video_receiver, packet); + else if(packet->is_haptics) + chiaki_audio_receiver_av_packet(stream_connection->haptics_receiver, packet); else chiaki_audio_receiver_av_packet(stream_connection->audio_receiver, packet); } diff --git a/lib/src/takion.c b/lib/src/takion.c index c433977..f786d6f 100644 --- a/lib/src/takion.c +++ b/lib/src/takion.c @@ -57,7 +57,8 @@ typedef enum takion_packet_type_t { TAKION_PACKET_TYPE_CONGESTION = 5, TAKION_PACKET_TYPE_FEEDBACK_STATE = 6, TAKION_PACKET_TYPE_CLIENT_INFO = 8, - TAKION_PACKET_TYPE_PAD_INFO_EVENT = 9 + TAKION_PACKET_TYPE_PAD_INFO_EVENT = 9, + TAKION_PACKET_TYPE_PAD_ADAPTIVE_TRIGGERS = 11, } TakionPacketType; /** @@ -215,6 +216,7 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_takion_connect(ChiakiTakion *takion, Chiaki takion->postponed_packets = NULL; takion->postponed_packets_size = 0; takion->postponed_packets_count = 0; + takion->enable_dualsense = info->enable_dualsense; CHIAKI_LOGI(takion->log, "Takion connecting (version %u)", (unsigned int)info->protocol_version); @@ -556,7 +558,7 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_takion_send_feedback_state(ChiakiTakion *ta else { buf_sz = 0xc + CHIAKI_FEEDBACK_STATE_BUF_SIZE_V12; - chiaki_feedback_state_format_v12(buf + 0xc, feedback_state); + chiaki_feedback_state_format_v12(buf + 0xc, feedback_state, takion->enable_dualsense); } return takion_send_feedback_packet(takion, buf, buf_sz); } @@ -950,6 +952,7 @@ static void takion_flush_data_queue(ChiakiTakion *takion) if(data_type != CHIAKI_TAKION_MESSAGE_DATA_TYPE_PROTOBUF && data_type != CHIAKI_TAKION_MESSAGE_DATA_TYPE_RUMBLE + && data_type != CHIAKI_TAKION_MESSAGE_DATA_TYPE_TRIGGER_EFFECTS && data_type != CHIAKI_TAKION_MESSAGE_DATA_TYPE_9) { CHIAKI_LOGW(takion->log, "Takion received data with unexpected data type %#x", data_type); @@ -1308,7 +1311,7 @@ static ChiakiErrorCode av_packet_parse(bool v12, ChiakiTakionAVPacket *packet, C if(v12 && !packet->is_video) { - packet->byte_before_audio_data = *av; + packet->is_haptics = *av == 0x02; av += 1; av_size -= 1; }