diff --git a/include/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_acquisition.hpp b/include/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_acquisition.hpp index 35a36dd..3ee7f1b 100644 --- a/include/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_acquisition.hpp +++ b/include/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_acquisition.hpp @@ -8,10 +8,17 @@ #include "display_structs.hpp" #include "devicecapture.hpp" +#define PARTNER_CTR_CAPTURE_BASE_COMMAND 0xE007 +#define PARTNER_CTR_CAPTURE_COMMAND_INPUT 0x000F +#define PARTNER_CTR_CAPTURE_COMMAND_TOP_SCREEN 0x00C4 +#define PARTNER_CTR_CAPTURE_COMMAND_SECOND_TOP_SCREEN 0x00C5 +#define PARTNER_CTR_CAPTURE_COMMAND_BOT_SCREEN 0x00C6 +#define PARTNER_CTR_CAPTURE_COMMAND_AUDIO 0x00C7 + void list_devices_cypart_device(std::vector &devices_list, std::vector &no_access_list, bool* devices_allowed_scan); bool cypart_device_connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device); -uint64_t cypart_device_get_video_in_size(CaptureStatus* status); -uint64_t cypart_device_get_video_in_size(CaptureData* capture_data); +uint64_t cypart_device_get_video_in_size(CaptureStatus* status, bool is_3d); +uint64_t cypart_device_get_video_in_size(CaptureData* capture_data, bool is_3d); void cypart_device_acquisition_main_loop(CaptureData* capture_data); void usb_cypart_device_acquisition_cleanup(CaptureData* capture_data); void usb_cypart_device_init(); diff --git a/include/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_communications.hpp b/include/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_communications.hpp index 795ced7..d292274 100644 --- a/include/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_communications.hpp +++ b/include/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_communications.hpp @@ -23,7 +23,7 @@ const cypart_device_usb_device* GetCyPartnerCTRDeviceDesc(int index); const cy_device_usb_device* get_cy_usb_info(const cypart_device_usb_device* usb_device_desc); std::string read_serial_ctr_capture(cy_device_device_handlers* handlers, const cypart_device_usb_device* device); int capture_start(cy_device_device_handlers* handlers, const cypart_device_usb_device* device, std::string &serial); -int StartCaptureDma(cy_device_device_handlers* handlers, const cypart_device_usb_device* device); +int StartCaptureDma(cy_device_device_handlers* handlers, const cypart_device_usb_device* device, bool is_3d); int capture_end(cy_device_device_handlers* handlers, const cypart_device_usb_device* device); int ReadFrame(cy_device_device_handlers* handlers, uint8_t* buf, int length, const cypart_device_usb_device* device_desc); int ReadFrameAsync(cy_device_device_handlers* handlers, uint8_t* buf, int length, const cypart_device_usb_device* device_desc, cy_async_callback_data* cb_data); diff --git a/source/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_acquisition.cpp b/source/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_acquisition.cpp index 67c735b..7669a09 100644 --- a/source/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_acquisition.cpp +++ b/source/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_acquisition.cpp @@ -11,8 +11,57 @@ #include #include +// This code was developed by exclusively looking at Wireshark USB packet +// captures to learn the USB device's protocol. +// As such, it was developed in a clean room environment, to provide +// compatibility of the USB device with more (and more modern) hardware. +// Such an action is allowed under EU law. +// No reverse engineering of the original software was done to create this. + +#define SYNCH_VALUE_PARTNER_CTR PARTNER_CTR_CAPTURE_BASE_COMMAND + +#define TOTAL_WANTED_CONCURRENTLY_RUNNING_BUFFER_SIZE 0x100000 +#define SINGLE_RING_BUFFER_SLICE_SIZE 0x10000 +#define NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS (TOTAL_WANTED_CONCURRENTLY_RUNNING_BUFFER_SIZE / SINGLE_RING_BUFFER_SLICE_SIZE) + +#define NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS (3 * NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS) + +#define PARTNER_CTR_TOTAL_BUFFERS_SIZE (NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS * SINGLE_RING_BUFFER_SLICE_SIZE) + +#define ERROR_CTR_SCREEN_SEARCH_NOT_ENOUGH_DATA ((size_t)-1) +#define ERROR_CTR_SCREEN_SEARCH_NOT_SYNCHRONIZED ((size_t)-2) + +#define MAX_TIME_WAIT 1.0 + #define PARTNER_CTR_CYPRESS_USB_WINDOWS_DRIVER CYPRESS_WINDOWS_DEFAULT_USB_DRIVER +struct CypressPartnerCTRDeviceCaptureReceivedData { + CypressPartnerCTRDeviceCaptureReceivedData* array_ptr; + volatile bool in_use; + volatile bool to_process; + uint32_t index; + SharedConsumerMutex* is_buffer_free_shared_mutex; + int* status; + uint32_t* last_index; + uint8_t *ring_slice_buffer_arr; + bool* is_ring_buffer_slice_data_in_use_arr; + bool* is_ring_buffer_slice_data_ready_arr; + int ring_buffer_slice_index; + int* last_ring_buffer_slice_allocated; + int* first_usable_ring_buffer_slice_index; + bool* is_synchronized; + size_t* last_used_ring_buffer_slice_pos; + int* buffer_ring_slice_to_array_ptr; + bool* stored_is_3d; + CaptureData* capture_data; + std::chrono::time_point* clock_start; + cy_async_callback_data cb_data; +}; + +static void cypress_device_read_frame_cb(void* user_data, int transfer_length, int transfer_status); +static int get_cypress_device_status(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data); +static void error_cypress_device_status(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, int error_val); + static cy_device_device_handlers* usb_reconnect(const cypart_device_usb_device* usb_device_desc, CaptureDevice* device) { cy_device_device_handlers* final_handlers = NULL; int curr_serial_extra_id = 0; @@ -104,18 +153,816 @@ bool cypart_device_connect_usb(bool print_failed, CaptureData* capture_data, Cap return true; } -uint64_t cypart_device_get_video_in_size(CaptureStatus* status) { +static uint64_t cypart_device_get_video_in_size(bool is_3d) { + if(is_3d) + return ((2 * TOP_WIDTH_3DS * HEIGHT_3DS) + (BOT_WIDTH_3DS * HEIGHT_3DS)) * sizeof(VideoPixelBGR); + return ((TOP_WIDTH_3DS * HEIGHT_3DS) + (BOT_WIDTH_3DS * HEIGHT_3DS)) * sizeof(VideoPixelBGR); +} + +uint64_t cypart_device_get_video_in_size(CaptureStatus* status, bool is_3d) { + return cypart_device_get_video_in_size(is_3d); +} + +uint64_t cypart_device_get_video_in_size(CaptureData* capture_data, bool is_3d) { + return cypart_device_get_video_in_size(&capture_data->status, is_3d); +} + +static int cypress_device_read_frame_request(CaptureData* capture_data, CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, uint32_t index, bool is_3d) { + if(cypress_device_capture_recv_data == NULL) + return LIBUSB_SUCCESS; + if((*cypress_device_capture_recv_data->status) < 0) + return LIBUSB_SUCCESS; + const cypart_device_usb_device* usb_device_info = (const cypart_device_usb_device*)capture_data->status.device.descriptor; + cypress_device_capture_recv_data->index = index; + cypress_device_capture_recv_data->cb_data.function = cypress_device_read_frame_cb; + //size_t read_size = cypart_device_get_video_in_size(is_3d); + size_t read_size = SINGLE_RING_BUFFER_SLICE_SIZE; + int new_buffer_slice_index = ((*cypress_device_capture_recv_data->last_ring_buffer_slice_allocated) + 1) % NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS; + uint8_t* buffer = &cypress_device_capture_recv_data->ring_slice_buffer_arr[new_buffer_slice_index * SINGLE_RING_BUFFER_SLICE_SIZE]; + cypress_device_capture_recv_data->is_ring_buffer_slice_data_ready_arr[new_buffer_slice_index] = false; + cypress_device_capture_recv_data->is_ring_buffer_slice_data_in_use_arr[new_buffer_slice_index] = true; + cypress_device_capture_recv_data->buffer_ring_slice_to_array_ptr[new_buffer_slice_index] = cypress_device_capture_recv_data->cb_data.internal_index; + cypress_device_capture_recv_data->ring_buffer_slice_index = new_buffer_slice_index; + *cypress_device_capture_recv_data->last_ring_buffer_slice_allocated = new_buffer_slice_index; + cypress_device_capture_recv_data->is_buffer_free_shared_mutex->specific_try_lock(cypress_device_capture_recv_data->cb_data.internal_index); + cypress_device_capture_recv_data->in_use = true; + cypress_device_capture_recv_data->to_process = true; + return ReadFrameAsync((cy_device_device_handlers*)capture_data->handle, buffer, (int)read_size, usb_device_info, &cypress_device_capture_recv_data->cb_data); +} + +static void unlock_buffer_directly(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data_real, bool reset_slice_data_arr_info) { + int slice_index = cypress_device_capture_recv_data_real->ring_buffer_slice_index; + if(reset_slice_data_arr_info) { + cypress_device_capture_recv_data_real->is_ring_buffer_slice_data_ready_arr[slice_index] = false; + cypress_device_capture_recv_data_real->is_ring_buffer_slice_data_in_use_arr[slice_index] = false; + } + cypress_device_capture_recv_data_real->buffer_ring_slice_to_array_ptr[slice_index] = -1; + cypress_device_capture_recv_data_real->in_use = false; + if(!cypress_device_capture_recv_data_real->to_process) + cypress_device_capture_recv_data_real->is_buffer_free_shared_mutex->specific_unlock(cypress_device_capture_recv_data_real->cb_data.internal_index); +} + +static void unlock_buffer_slice_index(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, size_t slice_index, bool reset_slice_data_arr_info) { + int index = cypress_device_capture_recv_data->buffer_ring_slice_to_array_ptr[slice_index]; + if(index == -1) { + if(reset_slice_data_arr_info) { + cypress_device_capture_recv_data->is_ring_buffer_slice_data_ready_arr[slice_index] = false; + cypress_device_capture_recv_data->is_ring_buffer_slice_data_in_use_arr[slice_index] = false; + } + return; + } + unlock_buffer_directly(&cypress_device_capture_recv_data->array_ptr[index], reset_slice_data_arr_info); +} + +static void end_cypress_device_read_frame_cb(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, bool to_unlock) { + if(to_unlock) + unlock_buffer_directly(cypress_device_capture_recv_data, true); + cypress_device_capture_recv_data->to_process = false; + if(!cypress_device_capture_recv_data->in_use) + cypress_device_capture_recv_data->is_buffer_free_shared_mutex->specific_unlock(cypress_device_capture_recv_data->cb_data.internal_index); +} + +static PartnerCTRCaptureCommand read_partner_ctr_base_command(uint8_t* data) { + PartnerCTRCaptureCommand out_cmd; + // Important: Ensure the data after the buffers for a single frame + // is stopped by wrong magic value... + out_cmd.magic = read_le16(data, 0); + out_cmd.command = read_le16(data, 1); + out_cmd.payload_size = read_le32(data, 1); + return out_cmd; +} + +static size_t get_partner_ctr_size_command_header(PartnerCTRCaptureCommand read_command) { + switch (read_command.command) { + case PARTNER_CTR_CAPTURE_COMMAND_INPUT: + return sizeof(PartnerCTRCaptureCommandHeader0F); + case PARTNER_CTR_CAPTURE_COMMAND_TOP_SCREEN: + return sizeof(PartnerCTRCaptureCommandHeaderCxScreen); + case PARTNER_CTR_CAPTURE_COMMAND_SECOND_TOP_SCREEN: + return sizeof(PartnerCTRCaptureCommandHeaderCxScreen); + case PARTNER_CTR_CAPTURE_COMMAND_BOT_SCREEN: + return sizeof(PartnerCTRCaptureCommandHeaderCxScreen); + case PARTNER_CTR_CAPTURE_COMMAND_AUDIO: + return sizeof(PartnerCTRCaptureCommandHeaderC7); + default: + ActualConsoleOutTextError("Partner CTR Capture: Unknown command found! " + std::to_string(read_command.command)); + return sizeof(PartnerCTRCaptureCommandHeader0F); + } +} + +static USB3DSOptimizeColumnInfo convert_to_column_info(uint16_t value) { + // The macos compiler requires this... :/ + USB3DSOptimizeColumnInfo column_info; + column_info.has_extra_header_data_2d_only = (value >> 15) & 1; + column_info.buffer_num = (value >> 14) & 1; + column_info.unk = (value >> 10) & 0xF; + column_info.column_index = value & 0x3FF; + return column_info; +} + +static bool get_expect_extra_header_in_buffer(uint16_t value) { + return convert_to_column_info(value).has_extra_header_data_2d_only; +} + +static bool get_is_data_2d_only(uint16_t value) { + return convert_to_column_info(value).has_extra_header_data_2d_only; +} + +static bool get_is_pos_column_synch_in_buffer(uint8_t* buffer, size_t pos_to_check, uint16_t column_index) { + if(read_le16(buffer + pos_to_check) != SYNCH_VALUE_PARTNER_CTR) + return false; + return convert_to_column_info(read_le16(buffer + pos_to_check + 2)).column_index == column_index; +} + +static bool get_is_pos_column_synch_in_buffer(USB3DSOptimizeHeaderSoundData* in_ptr, uint16_t column_index) { + return get_is_pos_column_synch_in_buffer((uint8_t*)&in_ptr->header_info, 0, column_index); +} + +static bool get_is_pos_synch_in_buffer(uint8_t* buffer, size_t pos_to_check) { + PartnerCTRCaptureCommand base_command = read_partner_ctr_base_command(buffer + pos_to_check); + return base_command.magic == SYNCH_VALUE_PARTNER_CTR; +} + +static bool get_is_pos_first_synch_in_buffer(uint8_t* buffer, size_t pos_to_check) { + PartnerCTRCaptureCommand base_command = read_partner_ctr_base_command(buffer + pos_to_check); + return (base_command.magic == SYNCH_VALUE_PARTNER_CTR) && (base_command.command == PARTNER_CTR_CAPTURE_COMMAND_INPUT); +} + +static bool get_is_buffer_2d_fully_synced(CaptureReceived* buffer) { + USB8883DSOptimizeCaptureReceived* real_buffer = &buffer->cypress_optimize_received_888; + USB8883DSOptimizeCaptureReceivedExtraHeader* real_buffer_special = &buffer->cypress_optimize_received_888_extra_header; + bool is_special = get_expect_extra_header_in_buffer(read_le16((uint8_t*)&real_buffer->columns_data[0].header_sound.header_info.column_info)); + for(int i = 0; i < TOP_WIDTH_3DS; i++) + if(!get_is_pos_column_synch_in_buffer(&real_buffer->columns_data[i].header_sound, i)) + return false; + if(is_special && (!get_is_pos_column_synch_in_buffer(&real_buffer_special->columns_data[TOP_WIDTH_3DS].header_sound, TOP_WIDTH_3DS))) + return false; + return true; +} + +static bool get_is_buffer_3d_fully_synced(CaptureReceived* buffer) { + USB8883DSOptimizeCaptureReceived_3D* real_buffer = &buffer->cypress_optimize_received_888_3d; + for(int i = 0; i < TOP_WIDTH_3DS; i++) { + if(!get_is_pos_column_synch_in_buffer(&real_buffer->columns_data[i][0].header_sound, i * 2)) + return false; + if(!get_is_pos_column_synch_in_buffer(&real_buffer->columns_data[i][1].header_sound, (i * 2) + 1)) + return false; + } + if(!get_is_pos_column_synch_in_buffer(&real_buffer->bottom_only_column.header_sound, TOP_WIDTH_3DS * 2)) + return false; + return true; +} + +static bool get_is_buffer_fully_synched(CaptureReceived* buffer, bool is_3d) { + if(is_3d) + return get_is_buffer_3d_fully_synced(buffer); + return get_is_buffer_2d_fully_synced(buffer); +} + +static size_t get_pos_first_synch_in_buffer(uint8_t* buffer, int start_pos) { + for(int i = start_pos; i < (SINGLE_RING_BUFFER_SLICE_SIZE / 2); i++) { + if(get_is_pos_first_synch_in_buffer(buffer, i * 2)) + return i * 2; + } + return SINGLE_RING_BUFFER_SLICE_SIZE + 1; +} + +static int get_num_consecutive_ready_ring_buffer_slices(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, int start_index) { + for(int i = 0; i < NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS; i++) { + int index_check = (start_index + i) % NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS; + if(!cypress_device_capture_recv_data->is_ring_buffer_slice_data_ready_arr[index_check]) + return i; + } + // Should never happen + return NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS; +} + +static void unlock_buffers_from_x(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, int pos_to_unlock, int num_buffers_to_unlock, bool reset_slice_data_arr_info) { + for(int i = 0; i < num_buffers_to_unlock; i++) + unlock_buffer_slice_index(cypress_device_capture_recv_data, (pos_to_unlock + i) % NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS, reset_slice_data_arr_info); +} + +static void copy_slice_data_to_buffer(uint8_t* dst, uint8_t *src, size_t start_slice_index, size_t start_slice_pos, size_t copy_size) { + size_t start_byte = ((start_slice_index * SINGLE_RING_BUFFER_SLICE_SIZE) + start_slice_pos) % PARTNER_CTR_TOTAL_BUFFERS_SIZE; + if((start_byte + copy_size) <= PARTNER_CTR_TOTAL_BUFFERS_SIZE) + memcpy(dst, src + start_byte, copy_size); + else { + size_t upper_read_size = PARTNER_CTR_TOTAL_BUFFERS_SIZE - start_byte; + size_t start_read_size = copy_size - upper_read_size; + memcpy(dst, src + start_byte, upper_read_size); + memcpy(dst + upper_read_size, src, start_read_size); + } +} + +static void cypress_output_to_thread(CaptureData* capture_data, uint8_t *buffer_arr, size_t start_slice_index, size_t start_slice_pos, int internal_index, std::chrono::time_point* clock_start, size_t read_size, bool is_3d, bool should_be_3d) { + // Output to the other threads... + CaptureDataSingleBuffer* data_buf = capture_data->data_buffers.GetWriterBuffer(internal_index); + copy_slice_data_to_buffer((uint8_t*)&data_buf->capture_buf, buffer_arr, start_slice_index, start_slice_pos, read_size); + if(!get_is_buffer_fully_synched(&data_buf->capture_buf, is_3d)) { + capture_data->data_buffers.ReleaseWriterBuffer(internal_index, false); + return; + } + const auto curr_time = std::chrono::high_resolution_clock::now(); + const std::chrono::duration diff = curr_time - (*clock_start); + *clock_start = curr_time; + capture_data->data_buffers.WriteToBuffer(NULL, read_size, diff.count(), &capture_data->status.device, internal_index, is_3d, should_be_3d); + if (capture_data->status.cooldown_curr_in) + capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1; + capture_data->status.video_wait.unlock(); + capture_data->status.audio_wait.unlock(); +} + +static bool cypress_device_read_frame_not_synchronized(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, int &error) { + volatile int first_slice_to_check = *cypress_device_capture_recv_data->first_usable_ring_buffer_slice_index; + volatile int first_slice_pos_to_check = *cypress_device_capture_recv_data->last_used_ring_buffer_slice_pos; + bool found = false; + // Determine which buffer is the first which needs to still be checked + for(int i = 0; i < NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS; i++) { + int index_to_check = (first_slice_to_check + i) % NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS; + if(cypress_device_capture_recv_data->is_ring_buffer_slice_data_in_use_arr[index_to_check]) { + first_slice_to_check = index_to_check; + found = true; + break; + } + } + // Too many have been checked... Just fail. + if(!found) { + error = LIBUSB_ERROR_OTHER; + return false; + } + for(int i = 0; i < NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS; i++) { + int check_index = (first_slice_to_check + i) % NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS; + // Search for the synch values. If they're not there, free the buffer. + // If they are there, enque the buffer and switch mode. + // Checks all available buffers which have data (but in order). + if(cypress_device_capture_recv_data->is_ring_buffer_slice_data_ready_arr[check_index]) { + size_t start_result = 0; + if(check_index == first_slice_to_check) + start_result = first_slice_pos_to_check + 2; + size_t result = get_pos_first_synch_in_buffer(&cypress_device_capture_recv_data->ring_slice_buffer_arr[check_index * SINGLE_RING_BUFFER_SLICE_SIZE], start_result); + if(result >= SINGLE_RING_BUFFER_SLICE_SIZE) { + unlock_buffer_slice_index(cypress_device_capture_recv_data, check_index, true); + } + else { + *cypress_device_capture_recv_data->first_usable_ring_buffer_slice_index = check_index; + *cypress_device_capture_recv_data->last_used_ring_buffer_slice_pos = result; + *cypress_device_capture_recv_data->is_synchronized = true; + break; + } + } + else + break; + } + return true; +} + +static PartnerCTRCaptureCommand get_command_partner_ctr(uint8_t* data, size_t slice_index, size_t start_pos, size_t curr_pos) { + uint8_t tmp_buffer[sizeof(PartnerCTRCaptureCommand)]; + copy_slice_data_to_buffer(tmp_buffer, data, slice_index, start_pos + curr_pos, sizeof(PartnerCTRCaptureCommand)); + return read_partner_ctr_base_command(tmp_buffer); +} + +static size_t get_pos_next_command_partner_ctr(uint8_t* data, size_t slice_index, size_t start_pos, size_t curr_pos) { + uint8_t tmp_buffer[sizeof(PartnerCTRCaptureCommand)]; + copy_slice_data_to_buffer(tmp_buffer, data, slice_index, start_pos + curr_pos, sizeof(PartnerCTRCaptureCommand)); + PartnerCTRCaptureCommand read_command = read_partner_ctr_base_command(tmp_buffer); + return curr_pos + get_partner_ctr_size_command_header(read_command) + read_command.payload_size; +} + +static size_t find_pos_partner_ctr_x_screen(uint8_t* data, size_t slice_index, size_t start_pos, size_t curr_pos, size_t available_bytes) { + if((curr_pos + sizeof(PartnerCTRCaptureCommand)) > available_bytes) + return ERROR_CTR_SCREEN_SEARCH_NOT_ENOUGH_DATA; + PartnerCTRCaptureCommand read_command = get_command_partner_ctr(data, slice_index, start_pos, curr_pos); + if(read_command.magic != PARTNER_CTR_CAPTURE_BASE_COMMAND) + return ERROR_CTR_SCREEN_SEARCH_NOT_SYNCHRONIZED; + + if((read_command.command == PARTNER_CTR_CAPTURE_COMMAND_TOP_SCREEN) || (read_command.command == PARTNER_CTR_CAPTURE_COMMAND_BOT_SCREEN) || (read_command.command == PARTNER_CTR_CAPTURE_COMMAND_SECOND_TOP_SCREEN)) + return curr_pos; + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_AUDIO) + curr_pos = get_pos_next_command_partner_ctr(data, slice_index, start_pos, curr_pos); + + if((curr_pos + sizeof(PartnerCTRCaptureCommand)) > available_bytes) + return ERROR_CTR_SCREEN_SEARCH_NOT_ENOUGH_DATA; + read_command = get_command_partner_ctr(data, slice_index, start_pos, curr_pos); + if(read_command.magic != PARTNER_CTR_CAPTURE_BASE_COMMAND) + return ERROR_CTR_SCREEN_SEARCH_NOT_SYNCHRONIZED; + + if((read_command.command == PARTNER_CTR_CAPTURE_COMMAND_TOP_SCREEN) || (read_command.command == PARTNER_CTR_CAPTURE_COMMAND_BOT_SCREEN) || (read_command.command == PARTNER_CTR_CAPTURE_COMMAND_SECOND_TOP_SCREEN)) + return curr_pos; + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_INPUT) + curr_pos = get_pos_next_command_partner_ctr(data, slice_index, start_pos, curr_pos); + + if((curr_pos + sizeof(PartnerCTRCaptureCommand)) > available_bytes) + return ERROR_CTR_SCREEN_SEARCH_NOT_ENOUGH_DATA; + read_command = get_command_partner_ctr(data, slice_index, start_pos, curr_pos); + if(read_command.magic != PARTNER_CTR_CAPTURE_BASE_COMMAND) + return ERROR_CTR_SCREEN_SEARCH_NOT_SYNCHRONIZED; + + if((read_command.command == PARTNER_CTR_CAPTURE_COMMAND_TOP_SCREEN) || (read_command.command == PARTNER_CTR_CAPTURE_COMMAND_BOT_SCREEN) || (read_command.command == PARTNER_CTR_CAPTURE_COMMAND_SECOND_TOP_SCREEN)) + return curr_pos; + ActualConsoleOutTextError("Partner CTR Capture: Unknown command found! " + std::to_string(read_command.command)); + return ERROR_CTR_SCREEN_SEARCH_NOT_SYNCHRONIZED; +} + +// Package together a frame +static bool is_valid_frame_partner_ctr(uint8_t* data, size_t slice_index, size_t start_pos, size_t available_bytes, bool enabled_3d, size_t &out_end_pos, bool &synchronized) { + bool has_top = false; + bool has_top_second = !enabled_3d; + bool has_bottom = false; + size_t first_screen_pos = 0; + size_t second_screen_pos = 0; + size_t third_screen_pos = 0; + out_end_pos = 0; + + first_screen_pos = find_pos_partner_ctr_x_screen(data, slice_index, start_pos, 0, available_bytes); + out_end_pos = get_pos_next_command_partner_ctr(data, slice_index, start_pos, first_screen_pos); + if(first_screen_pos == ERROR_CTR_SCREEN_SEARCH_NOT_ENOUGH_DATA) + return false; + if(first_screen_pos == ERROR_CTR_SCREEN_SEARCH_NOT_SYNCHRONIZED) { + synchronized = false; + return false; + } + + PartnerCTRCaptureCommand read_command = get_command_partner_ctr(data, slice_index, start_pos, first_screen_pos); + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_TOP_SCREEN) + has_top = true; + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_BOT_SCREEN) + has_bottom = true; + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_SECOND_TOP_SCREEN) + has_top_second = true; + + second_screen_pos = find_pos_partner_ctr_x_screen(data, slice_index, start_pos, out_end_pos, available_bytes); + out_end_pos = get_pos_next_command_partner_ctr(data, slice_index, start_pos, second_screen_pos); + if(second_screen_pos == ERROR_CTR_SCREEN_SEARCH_NOT_ENOUGH_DATA) + return false; + if(second_screen_pos == ERROR_CTR_SCREEN_SEARCH_NOT_SYNCHRONIZED) { + synchronized = false; + return false; + } + + read_command = get_command_partner_ctr(data, slice_index, start_pos, second_screen_pos); + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_TOP_SCREEN) + has_top = true; + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_BOT_SCREEN) + has_bottom = true; + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_SECOND_TOP_SCREEN) + has_top_second = true; + + if(!enabled_3d) { + if(!(has_top && has_bottom)) { + synchronized = false; + return false; + } + if(out_end_pos > available_bytes) + return false; + + return true; + } + + third_screen_pos = find_pos_partner_ctr_x_screen(data, slice_index, start_pos, out_end_pos, available_bytes); + out_end_pos = get_pos_next_command_partner_ctr(data, slice_index, start_pos, third_screen_pos); + if(third_screen_pos == ERROR_CTR_SCREEN_SEARCH_NOT_ENOUGH_DATA) + return false; + if(third_screen_pos == ERROR_CTR_SCREEN_SEARCH_NOT_SYNCHRONIZED) { + synchronized = false; + return false; + } + + read_command = get_command_partner_ctr(data, slice_index, start_pos, third_screen_pos); + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_TOP_SCREEN) + has_top = true; + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_BOT_SCREEN) + has_bottom = true; + if(read_command.command == PARTNER_CTR_CAPTURE_COMMAND_SECOND_TOP_SCREEN) + has_top_second = true; + + if(!(has_top && has_bottom)) { + synchronized = false; + return false; + } + + // TODO: Find what to do here... + if(!(has_top && has_top_second && has_bottom)) { + synchronized = false; + return false; + } + if(out_end_pos > available_bytes) + return false; + + return true; +} + +// Package together a frame by calling is_valid_frame_partner_ctr, +// then try adding to the end of it audio data, if present and read... +static bool is_valid_frame_partner_ctr_attempt_add_sound(uint8_t* data, size_t slice_index, size_t start_pos, size_t available_bytes, bool enabled_3d, size_t &out_end_pos, bool &synchronized) { + bool is_valid = is_valid_frame_partner_ctr(data, slice_index, start_pos, available_bytes, enabled_3d, out_end_pos, synchronized); + if(!is_valid) + return false; + + if((out_end_pos + sizeof(PartnerCTRCaptureCommand)) > available_bytes) + return true; + + PartnerCTRCaptureCommand read_command = get_command_partner_ctr(data, slice_index, start_pos, out_end_pos); + if(read_command.magic != PARTNER_CTR_CAPTURE_BASE_COMMAND) + return true; + + if(read_command.command != PARTNER_CTR_CAPTURE_COMMAND_AUDIO) + return true; + + size_t tentative_pos = get_pos_next_command_partner_ctr(data, slice_index, start_pos, out_end_pos); + + if(tentative_pos > available_bytes) + return true; + + out_end_pos = tentative_pos; + return true; +} + +static void cypress_device_read_frame_synchronized(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data) { + volatile int read_slice_index = *cypress_device_capture_recv_data->first_usable_ring_buffer_slice_index; + volatile size_t read_slice_pos = *cypress_device_capture_recv_data->last_used_ring_buffer_slice_pos; + int num_consecutive_ready_buffers = get_num_consecutive_ready_ring_buffer_slices(cypress_device_capture_recv_data, read_slice_index); + bool should_be_3d_data = *cypress_device_capture_recv_data->stored_is_3d; + bool is_3d_data = should_be_3d_data; + size_t final_data_size = 0; + bool synchronized = true; + size_t curr_consecutive_available_bytes = (num_consecutive_ready_buffers * SINGLE_RING_BUFFER_SLICE_SIZE) - read_slice_pos; + bool is_data_ready = is_valid_frame_partner_ctr_attempt_add_sound(cypress_device_capture_recv_data->ring_slice_buffer_arr, read_slice_index, read_slice_pos, curr_consecutive_available_bytes, is_3d_data, final_data_size, synchronized); + if((!is_data_ready) && (!synchronized)) { + *cypress_device_capture_recv_data->is_synchronized = false; + return; + } + if(is_data_ready) { + // Enough data. Time to do output... + cypress_output_to_thread(cypress_device_capture_recv_data->capture_data, cypress_device_capture_recv_data->ring_slice_buffer_arr, read_slice_index, read_slice_pos, 0, cypress_device_capture_recv_data->clock_start, final_data_size, is_3d_data, should_be_3d_data); + // Keep the ring buffer going. + size_t raw_new_pos = read_slice_pos + final_data_size; + int new_slice_index = (int)(read_slice_index + (raw_new_pos / SINGLE_RING_BUFFER_SLICE_SIZE)); + size_t new_slice_pos = raw_new_pos % SINGLE_RING_BUFFER_SLICE_SIZE; + new_slice_index %= NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS; + // Fully unlock used buffers + unlock_buffers_from_x(cypress_device_capture_recv_data, read_slice_index, (new_slice_index + NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS - read_slice_index) % NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS, true); + // Update the position of the data + *cypress_device_capture_recv_data->first_usable_ring_buffer_slice_index = new_slice_index; + *cypress_device_capture_recv_data->last_used_ring_buffer_slice_pos = new_slice_pos; + } + // Partially unlock buffers already with data. + // Unlocks the transfers to be used for more. + // This is possible under the assumption that there are significantly + // more buffers than transfers!!! + // Without said assumption, you'd risk the two stepping on each other. + read_slice_index = *cypress_device_capture_recv_data->first_usable_ring_buffer_slice_index; + num_consecutive_ready_buffers = get_num_consecutive_ready_ring_buffer_slices(cypress_device_capture_recv_data, read_slice_index); + unlock_buffers_from_x(cypress_device_capture_recv_data, read_slice_index, num_consecutive_ready_buffers, false); +} + +static void cypress_device_read_frame_cb(void* user_data, int transfer_length, int transfer_status) { + CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data = (CypressPartnerCTRDeviceCaptureReceivedData*)user_data; + if((*cypress_device_capture_recv_data->status) < 0) + return end_cypress_device_read_frame_cb(cypress_device_capture_recv_data, true); + if((transfer_status != LIBUSB_TRANSFER_COMPLETED) || (transfer_length < SINGLE_RING_BUFFER_SLICE_SIZE)) { + int error = LIBUSB_ERROR_OTHER; + if(transfer_status == LIBUSB_TRANSFER_TIMED_OUT) + error = LIBUSB_ERROR_TIMEOUT; + *cypress_device_capture_recv_data->status = error; + return end_cypress_device_read_frame_cb(cypress_device_capture_recv_data, true); + } + cypress_device_capture_recv_data->is_ring_buffer_slice_data_ready_arr[cypress_device_capture_recv_data->ring_buffer_slice_index] = true; + // Not synchronized case + bool to_free = false; + bool continue_looping = true; + int remaining_iters_before_force_exit = 4; + volatile bool read_is_synchronized = *cypress_device_capture_recv_data->is_synchronized; + while(continue_looping) { + if(read_is_synchronized) + cypress_device_read_frame_synchronized(cypress_device_capture_recv_data); + else { + int error = 0; + bool retval = cypress_device_read_frame_not_synchronized(cypress_device_capture_recv_data, error); + if(!retval) { + *cypress_device_capture_recv_data->status = error; + return end_cypress_device_read_frame_cb(cypress_device_capture_recv_data, true); + } + } + volatile bool new_read_is_synchronized = *cypress_device_capture_recv_data->is_synchronized; + remaining_iters_before_force_exit--; + if((new_read_is_synchronized == read_is_synchronized) || (remaining_iters_before_force_exit == 0)) + continue_looping = false; + read_is_synchronized = new_read_is_synchronized; + } + return end_cypress_device_read_frame_cb(cypress_device_capture_recv_data, to_free); +} + +static void close_all_reads_error(CaptureData* capture_data, CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, bool &async_read_closed) { + cy_device_device_handlers* handlers = (cy_device_device_handlers*)capture_data->handle; + const cypart_device_usb_device* usb_device_desc = (const cypart_device_usb_device*)capture_data->status.device.descriptor; + if(get_cypress_device_status(cypress_device_capture_recv_data) < 0) { + if(!async_read_closed) { + if(handlers->usb_handle) { + for (int i = 0; i < NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS; i++) + CypressCloseAsyncRead(handlers, get_cy_usb_info(usb_device_desc), &cypress_device_capture_recv_data[i].cb_data); + } + else + CypressCloseAsyncRead(handlers, get_cy_usb_info(usb_device_desc), &cypress_device_capture_recv_data[0].cb_data); + async_read_closed = true; + } + } +} + +static bool has_too_much_time_passed(const std::chrono::time_point &start_time) { + const auto curr_time = std::chrono::high_resolution_clock::now(); + const std::chrono::duration diff = curr_time - start_time; + return diff.count() > MAX_TIME_WAIT; +} + +static void error_too_much_time_passed(CaptureData* capture_data, CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, bool &async_read_closed, const std::chrono::time_point &start_time) { + if(has_too_much_time_passed(start_time)) { + error_cypress_device_status(cypress_device_capture_recv_data, LIBUSB_ERROR_TIMEOUT); + close_all_reads_error(capture_data, cypress_device_capture_recv_data, async_read_closed); + } +} + +static void unlock_buffer_in_error_case(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, int status) { + if((status < 0) && (!cypress_device_capture_recv_data->to_process)) { + unlock_buffer_directly(cypress_device_capture_recv_data, true); + } +} + +static bool is_buffer_still_in_use(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data) { + return cypress_device_capture_recv_data->in_use || cypress_device_capture_recv_data->to_process; +} + +static void wait_all_cypress_device_buffers_free(CaptureData* capture_data, CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data) { + if(cypress_device_capture_recv_data == NULL) + return; + bool async_read_closed = false; + close_all_reads_error(capture_data, cypress_device_capture_recv_data, async_read_closed); + const auto start_time = std::chrono::high_resolution_clock::now(); + for(int i = 0; i < NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS; i++) + while(is_buffer_still_in_use(&cypress_device_capture_recv_data[i])) { + error_too_much_time_passed(capture_data, cypress_device_capture_recv_data, async_read_closed, start_time); + unlock_buffer_in_error_case(&cypress_device_capture_recv_data[i], get_cypress_device_status(cypress_device_capture_recv_data)); + cypress_device_capture_recv_data[i].is_buffer_free_shared_mutex->specific_timed_lock(i); + } +} + +static void wait_one_cypress_device_buffer_free(CaptureData* capture_data, CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data) { + bool done = false; + const auto start_time = std::chrono::high_resolution_clock::now(); + while(!done) { + for(int i = 0; i < NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS; i++) { + if(!is_buffer_still_in_use(&cypress_device_capture_recv_data[i])) + done = true; + } + if(!done) { + if(has_too_much_time_passed(start_time)) + return; + if(get_cypress_device_status(cypress_device_capture_recv_data) < 0) + return; + int dummy = 0; + cypress_device_capture_recv_data[0].is_buffer_free_shared_mutex->general_timed_lock(&dummy); + } + } +} + +static CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_get_free_buffer(CaptureData* capture_data, CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data) { + wait_one_cypress_device_buffer_free(capture_data, cypress_device_capture_recv_data); + if(get_cypress_device_status(cypress_device_capture_recv_data) < 0) + return NULL; + for(int i = 0; i < NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS; i++) + if(!is_buffer_still_in_use(&cypress_device_capture_recv_data[i])) { + return &cypress_device_capture_recv_data[i]; + } + return NULL; +} + +static int get_cypress_device_status(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data) { + return *cypress_device_capture_recv_data[0].status; +} + +static void error_cypress_device_status(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, int error_val) { + if((error_val == 0) || (get_cypress_device_status(cypress_device_capture_recv_data) == 0)) + *cypress_device_capture_recv_data[0].status = error_val; +} + +static void exported_error_cypress_device_status(void* data, int error_val) { + if(data == NULL) + return; + return error_cypress_device_status((CypressPartnerCTRDeviceCaptureReceivedData*)data, error_val); +} + +static bool get_buffer_and_schedule_read(CaptureData* capture_data, CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, uint32_t &index, bool &stored_is_3d, const std::string error_to_print) { + CypressPartnerCTRDeviceCaptureReceivedData* chosen_buffer = cypress_device_get_free_buffer(capture_data, cypress_device_capture_recv_data); + if(chosen_buffer == NULL) + error_cypress_device_status(cypress_device_capture_recv_data, LIBUSB_ERROR_TIMEOUT); + int ret = cypress_device_read_frame_request(capture_data, chosen_buffer, index++, stored_is_3d); + if(ret < 0) { + chosen_buffer->in_use = false; + cypress_device_capture_recv_data->to_process = false; + error_cypress_device_status(cypress_device_capture_recv_data, ret); + if(error_to_print != "") + capture_error_print(true, capture_data, error_to_print); + return false; + } + return true; +} + +static bool schedule_all_reads(CaptureData* capture_data, CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, uint32_t &index, bool &stored_is_3d, const std::string error_to_print) { + for(int i = 0; i < NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS; i++) { + if(!get_buffer_and_schedule_read(capture_data, cypress_device_capture_recv_data, index, stored_is_3d, error_to_print)) + return false; + } + return true; +} + +static int end_capture_from_capture_data(CaptureData* capture_data) { + cy_device_device_handlers* handlers = (cy_device_device_handlers*)capture_data->handle; + const cypart_device_usb_device* usb_device_desc = (const cypart_device_usb_device*)capture_data->status.device.descriptor; + return capture_end(handlers, usb_device_desc); +} + +static void reset_buffer_processing_data(CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data) { + *cypress_device_capture_recv_data[0].last_ring_buffer_slice_allocated = -1; + *cypress_device_capture_recv_data[0].first_usable_ring_buffer_slice_index = 0; + *cypress_device_capture_recv_data[0].last_used_ring_buffer_slice_pos = 0; + *cypress_device_capture_recv_data[0].is_synchronized = false; + *cypress_device_capture_recv_data[0].last_index = -1; + for(int i = 0; i < NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS; i++) { + cypress_device_capture_recv_data[0].is_ring_buffer_slice_data_ready_arr[i] = false; + cypress_device_capture_recv_data[0].is_ring_buffer_slice_data_in_use_arr[i] = false; + cypress_device_capture_recv_data[0].buffer_ring_slice_to_array_ptr[i] = -1; + } + for(int i = 0; i < NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS; i++) { + cypress_device_capture_recv_data[i].in_use = false; + cypress_device_capture_recv_data[i].to_process = false; + cypress_device_capture_recv_data[i].index = i; + cypress_device_capture_recv_data[i].ring_buffer_slice_index = -1; + } +} + +static int restart_captures_cc_reads(CaptureData* capture_data, CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, uint32_t &index, bool &stored_is_3d, bool could_use_3d, bool force_capture_start, std::chrono::time_point &clock_last_capture_start) { + cy_device_device_handlers* handlers = (cy_device_device_handlers*)capture_data->handle; + const cypart_device_usb_device* usb_device_desc = (const cypart_device_usb_device*)capture_data->status.device.descriptor; + int retval = end_capture_from_capture_data(capture_data); + if(retval < 0) + return retval; + error_cypress_device_status(cypress_device_capture_recv_data, 0); + default_sleep(100); + + bool set_max_again = false; + if(force_capture_start){ + std::string read_serial_dummy = ""; + retval = capture_start(handlers, usb_device_desc, read_serial_dummy); + clock_last_capture_start = std::chrono::high_resolution_clock::now(); + if(retval < 0) + return retval; + } + + bool is_new_3d = could_use_3d && get_3d_enabled(&capture_data->status); + if(is_new_3d != stored_is_3d) { + set_max_again = true; + stored_is_3d = is_new_3d; + } + + index = 0; + reset_buffer_processing_data(cypress_device_capture_recv_data); + + if(!schedule_all_reads(capture_data, cypress_device_capture_recv_data, index, stored_is_3d, "")) + return -1; + capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM; + StartCaptureDma(handlers, usb_device_desc, stored_is_3d); return 0; } +static bool cypart_device_acquisition_loop(CaptureData* capture_data, CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data, bool &stored_is_3d, bool could_use_3d) { + cy_device_device_handlers* handlers = (cy_device_device_handlers*)capture_data->handle; + const cypart_device_usb_device* usb_device_desc = (const cypart_device_usb_device*)capture_data->status.device.descriptor; + uint32_t index = 0; + uint64_t device_id = 0; + std::string read_serial = ""; + bool force_capture_start_key = false; + int ret = capture_start(handlers, usb_device_desc, read_serial); + std::chrono::time_point clock_last_capture_start = std::chrono::high_resolution_clock::now(); -uint64_t cypart_device_get_video_in_size(CaptureData* capture_data) { - return cypart_device_get_video_in_size(&capture_data->status); + if (ret < 0) { + capture_error_print(true, capture_data, "Capture Start: Failed"); + return false; + } + + CypressSetMaxTransferSize(handlers, get_cy_usb_info(usb_device_desc), 0x80000); + + if(!schedule_all_reads(capture_data, cypress_device_capture_recv_data, index, stored_is_3d, "Initial Reads: Failed")) + return false; + + StartCaptureDma(handlers, usb_device_desc, stored_is_3d); + while (capture_data->status.connected && capture_data->status.running) { + ret = get_cypress_device_status(cypress_device_capture_recv_data); + if(ret < 0) { + int cause_error = ret; + capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM + NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS; + wait_all_cypress_device_buffers_free(capture_data, cypress_device_capture_recv_data); + bool has_recovered = false; + if(cause_error == LIBUSB_ERROR_TIMEOUT) { + int timeout_ret = restart_captures_cc_reads(capture_data, cypress_device_capture_recv_data, index, stored_is_3d, could_use_3d, true, clock_last_capture_start); + if(timeout_ret >= 0) + has_recovered = true; + } + if(!has_recovered) { + capture_error_print(true, capture_data, "Disconnected: Read error"); + return false; + } + } + bool is_new_3d = could_use_3d && get_3d_enabled(&capture_data->status); + if(is_new_3d != stored_is_3d) { + capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM + NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS; + wait_all_cypress_device_buffers_free(capture_data, cypress_device_capture_recv_data); + ret = restart_captures_cc_reads(capture_data, cypress_device_capture_recv_data, index, stored_is_3d, could_use_3d, false, clock_last_capture_start); + if(ret < 0) { + capture_error_print(true, capture_data, "Disconnected: Update mode error"); + return false; + } + } + if(!get_buffer_and_schedule_read(capture_data, cypress_device_capture_recv_data, index, stored_is_3d, "Setup Read: Failed")) + return false; + } + return true; } void cypart_device_acquisition_main_loop(CaptureData* capture_data) { if(!usb_is_initialized()) return; + bool is_done_thread; + std::thread async_processing_thread; + cy_device_device_handlers* handlers = (cy_device_device_handlers*)capture_data->handle; + const cypart_device_usb_device* usb_device_desc = (const cypart_device_usb_device*)capture_data->status.device.descriptor; + + uint32_t last_index = -1; + int status = 0; + bool could_use_3d = get_3d_enabled(&capture_data->status, true); + bool stored_is_3d = could_use_3d && get_3d_enabled(&capture_data->status); + + uint8_t *ring_slice_buffer = new uint8_t[PARTNER_CTR_TOTAL_BUFFERS_SIZE]; + bool *is_ring_buffer_slice_data_ready = new bool[NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS]; + bool *is_ring_buffer_slice_data_in_use = new bool[NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS]; + int *buffer_ring_slice_to_array = new int[NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS]; + for(int i = 0; i < NUM_TOTAL_PARTNER_CTR_CYPRESS_BUFFERS; i++) { + is_ring_buffer_slice_data_ready[i] = false; + is_ring_buffer_slice_data_in_use[i] = false; + buffer_ring_slice_to_array[i] = -1; + } + int first_usable_ring_buffer_slice_index = 0; + bool is_synchronized = false; + size_t last_used_ring_buffer_slice_pos = 0; + int last_ring_buffer_slice_allocated = -1; + + std::vector cb_queue; + SharedConsumerMutex is_buffer_free_shared_mutex(NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS); + SharedConsumerMutex is_transfer_done_shared_mutex(NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS); + SharedConsumerMutex is_transfer_data_ready_shared_mutex(NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS); + std::chrono::time_point clock_start = std::chrono::high_resolution_clock::now(); + CypressPartnerCTRDeviceCaptureReceivedData* cypress_device_capture_recv_data = new CypressPartnerCTRDeviceCaptureReceivedData[NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS]; + for(int i = 0; i < NUM_PARTNER_CTR_CYPRESS_CONCURRENTLY_RUNNING_BUFFERS; i++) { + cypress_device_capture_recv_data[i].array_ptr = cypress_device_capture_recv_data; + cypress_device_capture_recv_data[i].in_use = false; + cypress_device_capture_recv_data[i].to_process = false; + cypress_device_capture_recv_data[i].index = i; + cypress_device_capture_recv_data[i].ring_buffer_slice_index = -1; + cypress_device_capture_recv_data[i].stored_is_3d = &stored_is_3d; + cypress_device_capture_recv_data[i].ring_slice_buffer_arr = ring_slice_buffer; + cypress_device_capture_recv_data[i].is_ring_buffer_slice_data_in_use_arr = is_ring_buffer_slice_data_in_use; + cypress_device_capture_recv_data[i].is_ring_buffer_slice_data_ready_arr = is_ring_buffer_slice_data_ready; + cypress_device_capture_recv_data[i].last_ring_buffer_slice_allocated = &last_ring_buffer_slice_allocated; + cypress_device_capture_recv_data[i].first_usable_ring_buffer_slice_index = &first_usable_ring_buffer_slice_index; + cypress_device_capture_recv_data[i].is_synchronized = &is_synchronized; + cypress_device_capture_recv_data[i].last_used_ring_buffer_slice_pos = &last_used_ring_buffer_slice_pos; + cypress_device_capture_recv_data[i].buffer_ring_slice_to_array_ptr = buffer_ring_slice_to_array; + cypress_device_capture_recv_data[i].capture_data = capture_data; + cypress_device_capture_recv_data[i].last_index = &last_index; + cypress_device_capture_recv_data[i].clock_start = &clock_start; + cypress_device_capture_recv_data[i].is_buffer_free_shared_mutex = &is_buffer_free_shared_mutex; + cypress_device_capture_recv_data[i].status = &status; + cypress_device_capture_recv_data[i].cb_data.actual_user_data = &cypress_device_capture_recv_data[i]; + cypress_device_capture_recv_data[i].cb_data.transfer_data = NULL; + cypress_device_capture_recv_data[i].cb_data.is_transfer_done_mutex = &is_transfer_done_shared_mutex; + cypress_device_capture_recv_data[i].cb_data.internal_index = i; + cypress_device_capture_recv_data[i].cb_data.is_data_ready = false; + cypress_device_capture_recv_data[i].cb_data.is_transfer_data_ready_mutex = &is_transfer_data_ready_shared_mutex; + cypress_device_capture_recv_data[i].cb_data.in_use_ptr = &cypress_device_capture_recv_data[i].to_process; + cypress_device_capture_recv_data[i].cb_data.error_function = exported_error_cypress_device_status; + cb_queue.push_back(&cypress_device_capture_recv_data[i].cb_data); + } + CypressSetupCypressDeviceAsyncThread(handlers, cb_queue, &async_processing_thread, &is_done_thread); + bool proper_return = cypart_device_acquisition_loop(capture_data, cypress_device_capture_recv_data, stored_is_3d, could_use_3d); + wait_all_cypress_device_buffers_free(capture_data, cypress_device_capture_recv_data); + CypressEndCypressDeviceAsyncThread(handlers, cb_queue, &async_processing_thread, &is_done_thread); + delete []cypress_device_capture_recv_data; + delete []ring_slice_buffer; + delete []is_ring_buffer_slice_data_ready; + delete []is_ring_buffer_slice_data_in_use; + delete []buffer_ring_slice_to_array; + + if(proper_return) + capture_end(handlers, usb_device_desc); } void usb_cypart_device_acquisition_cleanup(CaptureData* capture_data) { diff --git a/source/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_communications.cpp b/source/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_communications.cpp index 79a2f02..1a04477 100644 --- a/source/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_communications.cpp +++ b/source/CaptureDeviceSpecific/Partner_CTR/cypress_partner_ctr_communications.cpp @@ -401,7 +401,7 @@ int capture_start(cy_device_device_handlers* handlers, const cypart_device_usb_d } // Various unknown reads and writes from registers... -int StartCaptureDma(cy_device_device_handlers* handlers, const cypart_device_usb_device* device) { +int StartCaptureDma(cy_device_device_handlers* handlers, const cypart_device_usb_device* device, bool is_3d) { int ret = 0; uint32_t status = 0; uint32_t unk32 = 0; @@ -580,9 +580,15 @@ int capture_end(cy_device_device_handlers* handlers, const cypart_device_usb_dev } int ReadFrame(cy_device_device_handlers* handlers, uint8_t* buf, int length, const cypart_device_usb_device* device_desc) { - return 0; + // Maybe making this async would be better for lower end hardware... + int num_bytes = 0; + cypress_pipe_reset_bulk_in(handlers, get_cy_usb_info(device_desc)); + int ret = cypress_bulk_in_transfer(handlers, get_cy_usb_info(device_desc), buf, length, &num_bytes); + if(num_bytes != length) + return LIBUSB_ERROR_INTERRUPTED; + return ret; } int ReadFrameAsync(cy_device_device_handlers* handlers, uint8_t* buf, int length, const cypart_device_usb_device* device_desc, cy_async_callback_data* cb_data) { - return 0; + return cypress_bulk_in_async(handlers, get_cy_usb_info(device_desc), buf, length, cb_data); } diff --git a/source/conversions.cpp b/source/conversions.cpp index 388a9e0..1bb5b7b 100755 --- a/source/conversions.cpp +++ b/source/conversions.cpp @@ -5,6 +5,7 @@ #include "usb_ds_3ds_capture.hpp" #include "usb_is_device_acquisition.hpp" #include "cypress_optimize_3ds_acquisition.hpp" +#include "cypress_partner_ctr_acquisition.hpp" #include @@ -786,14 +787,6 @@ static void usb_is_device_convertVideoToOutput(CaptureReceived *p_in, VideoOutpu } } -// To be moved to the appropriate header, later... -#define PARTNER_CTR_CAPTURE_BASE_COMMAND 0xE007 -#define PARTNER_CTR_CAPTURE_COMMAND_INPUT 0x000F -#define PARTNER_CTR_CAPTURE_COMMAND_TOP_SCREEN 0x00C4 -#define PARTNER_CTR_CAPTURE_COMMAND_SECOND_TOP_SCREEN 0x00C5 -#define PARTNER_CTR_CAPTURE_COMMAND_BOT_SCREEN 0x00C6 -#define PARTNER_CTR_CAPTURE_COMMAND_AUDIO 0x00C7 - static PartnerCTRCaptureCommand read_partner_ctr_base_command(uint8_t* data) { PartnerCTRCaptureCommand out_cmd; // Important: Ensure the data after the buffers for a single frame diff --git a/source/devicecapture.cpp b/source/devicecapture.cpp index e652bc5..ce1f4cd 100755 --- a/source/devicecapture.cpp +++ b/source/devicecapture.cpp @@ -341,7 +341,7 @@ uint64_t get_video_in_size(CaptureData* capture_data, bool is_3d, bool should_be #endif #ifdef USE_PARTNER_CTR if(capture_data->status.device.cc_type == CAPTURE_CONN_PARTNER_CTR) - return cypart_device_get_video_in_size(capture_data); + return cypart_device_get_video_in_size(capture_data, is_3d); #endif return 0; } @@ -435,6 +435,8 @@ bool get_device_3d_implemented(CaptureStatus* capture_status) { return true; case CAPTURE_CONN_CYPRESS_OPTIMIZE: return true; + case CAPTURE_CONN_PARTNER_CTR: + return true; default: return false; }