#include "devicecapture.hpp" #include "usb_is_device_setup_general.hpp" #include "usb_is_device_libusb.hpp" #include "usb_is_device_is_driver.hpp" #include "usb_is_device_communications.hpp" #include "usb_is_device_acquisition.hpp" #include "usb_is_device_acquisition_general.hpp" #include "usb_is_nitro_acquisition_capture.hpp" #include "usb_is_nitro_acquisition_emulator.hpp" #include "usb_is_twl_acquisition_capture.hpp" #include "usb_generic.hpp" #include #include #include // Code based off of Gericom's sample code. Distributed under the MIT License. Copyright (c) 2024 Gericom // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. #define SERIAL_NUMBER_SIZE (IS_DEVICE_REAL_SERIAL_NUMBER_SIZE + 1) #define MAX_TIME_WAIT 1.0 #define FRAME_BUFFER_SIZE 32 static void is_device_read_frame_cb(void* user_data, int transfer_length, int transfer_status); static bool initial_cleanup(const is_device_usb_device* usb_device_desc, is_device_device_handlers* handlers) { switch(usb_device_desc->device_type) { case IS_NITRO_EMULATOR_DEVICE: return initial_cleanup_emulator(usb_device_desc, handlers) != LIBUSB_SUCCESS; case IS_NITRO_CAPTURE_DEVICE: return initial_cleanup_capture(usb_device_desc, handlers) != LIBUSB_SUCCESS; case IS_TWL_CAPTURE_DEVICE: return initial_cleanup_twl_capture(usb_device_desc, handlers) != LIBUSB_SUCCESS; default: return false; } } std::string get_serial(const is_device_usb_device* usb_device_desc, is_device_device_handlers* handlers, int& curr_serial_extra_id) { uint8_t data[SERIAL_NUMBER_SIZE]; std::string serial_str = std::to_string(curr_serial_extra_id); bool conn_success = true; if(initial_cleanup(usb_device_desc, handlers)) conn_success = false; if (conn_success && (GetDeviceSerial(handlers, data, usb_device_desc) != LIBUSB_SUCCESS)) conn_success = false; if (conn_success) { data[IS_DEVICE_REAL_SERIAL_NUMBER_SIZE] = '\0'; serial_str = std::string((const char*)data); } else curr_serial_extra_id += 1; return serial_str; } void is_device_insert_device(std::vector& devices_list, is_device_device_handlers* handlers, const is_device_usb_device* usb_device_desc, int& curr_serial_extra_id_is_device, std::string path) { devices_list.emplace_back(get_serial(usb_device_desc, handlers, curr_serial_extra_id_is_device), usb_device_desc->name, usb_device_desc->long_name, path, CAPTURE_CONN_IS_NITRO, (void*)usb_device_desc, false, false, usb_device_desc->audio_enabled, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, usb_device_desc->max_audio_samples_size, 0, 0, 0, 0, HEIGHT_DS, usb_device_desc->video_data_type); } void is_device_insert_device(std::vector& devices_list, is_device_device_handlers* handlers, const is_device_usb_device* usb_device_desc, int& curr_serial_extra_id_is_device) { devices_list.emplace_back(get_serial(usb_device_desc, handlers, curr_serial_extra_id_is_device), usb_device_desc->name, usb_device_desc->long_name, CAPTURE_CONN_IS_NITRO, (void*)usb_device_desc, false, false, usb_device_desc->audio_enabled, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, usb_device_desc->max_audio_samples_size, 0, 0, 0, 0, HEIGHT_DS, usb_device_desc->video_data_type); } static is_device_device_handlers* usb_find_by_serial_number(const is_device_usb_device* usb_device_desc, CaptureDevice* device) { is_device_device_handlers* final_handlers = NULL; int curr_serial_extra_id = 0; final_handlers = is_device_libusb_serial_reconnection(usb_device_desc, device, curr_serial_extra_id); if (final_handlers == NULL) final_handlers = is_driver_serial_reconnection(device); return final_handlers; } void list_devices_is_device(std::vector &devices_list, std::vector &no_access_list) { const size_t num_is_device_desc = GetNumISDeviceDesc(); int* curr_serial_extra_id_is_device = new int[num_is_device_desc]; bool* no_access_elems = new bool[num_is_device_desc]; bool* not_supported_elems = new bool[num_is_device_desc]; for (int i = 0; i < num_is_device_desc; i++) { no_access_elems[i] = false; not_supported_elems[i] = false; curr_serial_extra_id_is_device[i] = 0; } is_device_libusb_list_devices(devices_list, no_access_elems, not_supported_elems, curr_serial_extra_id_is_device, num_is_device_desc); bool any_not_supported = false; for(int i = 0; i < num_is_device_desc; i++) any_not_supported |= not_supported_elems[i]; for(int i = 0; i < num_is_device_desc; i++) if(no_access_elems[i]) { const is_device_usb_device* usb_device = GetISDeviceDesc(i); no_access_list.emplace_back(usb_device->vid, usb_device->pid); } if(any_not_supported) is_driver_list_devices(devices_list, not_supported_elems, curr_serial_extra_id_is_device, num_is_device_desc); delete[] curr_serial_extra_id_is_device; delete[] no_access_elems; delete[] not_supported_elems; } static void is_device_connection_end(is_device_device_handlers* handlers, const is_device_usb_device *device_desc, bool interface_claimed = true) { if (handlers == NULL) return; if (handlers->usb_handle) is_device_libusb_end_connection(handlers, device_desc, interface_claimed); else is_driver_end_connection(handlers); delete handlers; } bool is_device_connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device) { const is_device_usb_device* usb_device_info = (const is_device_usb_device*)device->descriptor; is_device_device_handlers* handlers = usb_find_by_serial_number(usb_device_info, device); if(handlers == NULL) { capture_error_print(true, capture_data, "Device not found"); return false; } capture_data->handle = (void*)handlers; return true; } uint64_t usb_is_device_get_video_in_size(CaptureScreensType capture_type, is_device_type device_type) { if(device_type == IS_TWL_CAPTURE_DEVICE) return sizeof(ISTWLCaptureVideoReceived); if((capture_type == CAPTURE_SCREENS_TOP) || (capture_type == CAPTURE_SCREENS_BOTTOM)) return sizeof(ISNitroEmulatorVideoInputData) / 2; return sizeof(ISNitroEmulatorVideoInputData); } uint64_t usb_is_device_get_video_in_size(CaptureStatus* status) { return usb_is_device_get_video_in_size(status->capture_type, ((const is_device_usb_device*)(status->device.descriptor))->device_type); } uint64_t usb_is_device_get_video_in_size(CaptureData* capture_data) { return usb_is_device_get_video_in_size(&capture_data->status); } int set_acquisition_mode(is_device_device_handlers* handlers, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, const is_device_usb_device* usb_device_desc) { is_device_forward_config_values_screens capture_mode_flag = IS_DEVICE_FORWARD_CONFIG_MODE_BOTH; switch(capture_type) { case CAPTURE_SCREENS_TOP: capture_mode_flag = IS_DEVICE_FORWARD_CONFIG_MODE_TOP; break; case CAPTURE_SCREENS_BOTTOM: capture_mode_flag = IS_DEVICE_FORWARD_CONFIG_MODE_BOTTOM; break; default: break; } is_device_forward_config_values_rate capture_rate_flag = IS_DEVICE_FORWARD_CONFIG_RATE_FULL; switch(capture_speed) { case CAPTURE_SPEEDS_HALF: capture_rate_flag = IS_DEVICE_FORWARD_CONFIG_RATE_HALF; break; case CAPTURE_SPEEDS_THIRD: capture_rate_flag = IS_DEVICE_FORWARD_CONFIG_RATE_THIRD; break; case CAPTURE_SPEEDS_QUARTER: capture_rate_flag = IS_DEVICE_FORWARD_CONFIG_RATE_QUARTER; break; default: break; } return UpdateFrameForwardConfig(handlers, IS_DEVICE_FORWARD_CONFIG_COLOR_RGB24, capture_mode_flag, capture_rate_flag, usb_device_desc); } int EndAcquisition(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) { switch(((const is_device_usb_device*)(capture_data->status.device.descriptor))->device_type) { case IS_NITRO_EMULATOR_DEVICE: return EndAcquisitionEmulator(capture_data, is_device_capture_recv_data, do_drain_frames, start_frames, capture_type); case IS_NITRO_CAPTURE_DEVICE: return EndAcquisitionCapture(capture_data, is_device_capture_recv_data); case IS_TWL_CAPTURE_DEVICE: return EndAcquisitionTWLCapture(capture_data, is_device_capture_recv_data); default: return 0; } } int EndAcquisition(const is_device_usb_device* usb_device_desc, is_device_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) { switch(usb_device_desc->device_type) { case IS_NITRO_EMULATOR_DEVICE: return EndAcquisitionEmulator(usb_device_desc, handlers, do_drain_frames, start_frames, capture_type); case IS_NITRO_CAPTURE_DEVICE: return EndAcquisitionCapture(usb_device_desc, handlers); case IS_TWL_CAPTURE_DEVICE: return EndAcquisitionTWLCapture(usb_device_desc, handlers); default: return 0; } } void output_to_thread(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point* clock_start, size_t read_size) { // Output to the other threads... const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor; const auto curr_time = std::chrono::high_resolution_clock::now(); const std::chrono::duration diff = curr_time - (*clock_start); *clock_start = curr_time; if(usb_device_info->device_type == IS_TWL_CAPTURE_DEVICE) { CaptureDataSingleBuffer* target = capture_data->data_buffers.GetWriterBuffer(); // Copy data to buffer, with special memcpy which accounts for ftd2 header data and skips synch bytes size_t video_size = usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type); memcpy(&target->capture_buf.is_twl_capture_received.video_capture_in, &capture_buf->is_twl_capture_received.video_capture_in, video_size); if(read_size > video_size) { size_t audio_size = ((read_size - video_size) / sizeof(ISTWLCaptureSoundData)) * sizeof(ISTWLCaptureAudioReceived); memcpy(&target->capture_buf.is_twl_capture_received.audio_capture_in, &capture_buf->is_twl_capture_received.audio_capture_in, audio_size); } target->read = read_size; target->time_in_buf = diff.count(); capture_data->data_buffers.ReleaseWriterBuffer(); } else capture_data->data_buffers.WriteToBuffer(capture_buf, read_size, diff.count(), &capture_data->status.device, curr_capture_type); 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 void output_to_thread(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point* clock_start) { const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor; output_to_thread(capture_data, capture_buf, curr_capture_type, clock_start, usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type)); } int is_device_read_frame_and_output(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point &clock_start) { const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor; int ret = ReadFrame((is_device_device_handlers*)capture_data->handle, (uint8_t*)capture_buf, usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type), usb_device_info); if (ret < 0) return ret; output_to_thread(capture_data, capture_buf, curr_capture_type, &clock_start); return ret; } void is_device_read_frame_request(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data, CaptureScreensType curr_capture_type, uint32_t index) { if(is_device_capture_recv_data == NULL) return; if((*is_device_capture_recv_data->status) < 0) return; const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor; is_device_capture_recv_data->index = index; is_device_capture_recv_data->curr_capture_type = curr_capture_type; is_device_capture_recv_data->cb_data.function = is_device_read_frame_cb; ReadFrameAsync((is_device_device_handlers*)capture_data->handle, (uint8_t*)&is_device_capture_recv_data->buffer, usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type), usb_device_info, &is_device_capture_recv_data->cb_data); } static void end_is_device_read_frame_cb(ISDeviceCaptureReceivedData* is_device_capture_recv_data) { is_device_capture_recv_data->in_use = false; is_device_capture_recv_data->is_buffer_free_shared_mutex->specific_unlock(is_device_capture_recv_data->cb_data.internal_index); } static void is_device_read_frame_cb(void* user_data, int transfer_length, int transfer_status) { ISDeviceCaptureReceivedData* is_device_capture_recv_data = (ISDeviceCaptureReceivedData*)user_data; if((*is_device_capture_recv_data->status) < 0) return end_is_device_read_frame_cb(is_device_capture_recv_data); if(transfer_status != LIBUSB_TRANSFER_COMPLETED) { *is_device_capture_recv_data->status = LIBUSB_ERROR_OTHER; return end_is_device_read_frame_cb(is_device_capture_recv_data); } if(((int32_t)(is_device_capture_recv_data->index - (*is_device_capture_recv_data->last_index))) <= 0) { //*is_device_capture_recv_data->status = LIBUSB_ERROR_INTERRUPTED; return end_is_device_read_frame_cb(is_device_capture_recv_data); } *is_device_capture_recv_data->last_index = is_device_capture_recv_data->index; output_to_thread(is_device_capture_recv_data->capture_data, &is_device_capture_recv_data->buffer, is_device_capture_recv_data->curr_capture_type, is_device_capture_recv_data->clock_start); end_is_device_read_frame_cb(is_device_capture_recv_data); } int is_device_get_num_free_buffers(ISDeviceCaptureReceivedData* is_device_capture_recv_data) { int num_free = 0; for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) if(!is_device_capture_recv_data[i].in_use) num_free += 1; return num_free; } static void close_all_reads_error(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data, bool &async_read_closed, bool &reset_usb_device, bool do_reset) { if(get_is_device_status(is_device_capture_recv_data) >= 0) return; if(!async_read_closed) { for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) CloseAsyncRead((is_device_device_handlers*)capture_data->handle, &is_device_capture_recv_data[i].cb_data); async_read_closed = true; } if(do_reset && (!reset_usb_device)) { int ret = ResetUSBDevice((is_device_device_handlers*)capture_data->handle); if((ret == LIBUSB_ERROR_NO_DEVICE) || (ret == LIBUSB_ERROR_NOT_FOUND)) { for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) { is_device_capture_recv_data[i].cb_data.transfer_data_access.lock(); is_device_capture_recv_data[i].cb_data.transfer_data = NULL; is_device_capture_recv_data[i].cb_data.transfer_data_access.unlock(); is_device_capture_recv_data[i].in_use = false; } } reset_usb_device = 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, ISDeviceCaptureReceivedData* is_device_capture_recv_data, bool &async_read_closed, bool &reset_usb_device, const std::chrono::time_point &start_time) { if(has_too_much_time_passed(start_time)) { error_is_device_status(is_device_capture_recv_data, -1); close_all_reads_error(capture_data, is_device_capture_recv_data, async_read_closed, reset_usb_device, true); } } void wait_all_is_device_transfers_done(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data) { if(is_device_capture_recv_data == NULL) return; bool async_read_closed = false; bool reset_usb_device = false; close_all_reads_error(capture_data, is_device_capture_recv_data, async_read_closed, reset_usb_device, false); const auto start_time = std::chrono::high_resolution_clock::now(); for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) { void* transfer_data; do { is_device_capture_recv_data[i].cb_data.transfer_data_access.lock(); transfer_data = is_device_capture_recv_data[i].cb_data.transfer_data; is_device_capture_recv_data[i].cb_data.transfer_data_access.unlock(); if(transfer_data) { error_too_much_time_passed(capture_data, is_device_capture_recv_data, async_read_closed, reset_usb_device, start_time); is_device_capture_recv_data[i].cb_data.is_transfer_done_mutex->specific_timed_lock(i); } } while(transfer_data); } } void wait_all_is_device_buffers_free(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data) { if(is_device_capture_recv_data == NULL) return; bool async_read_closed = false; bool reset_usb_device = false; close_all_reads_error(capture_data, is_device_capture_recv_data, async_read_closed, reset_usb_device, false); const auto start_time = std::chrono::high_resolution_clock::now(); for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) while(is_device_capture_recv_data[i].in_use) { error_too_much_time_passed(capture_data, is_device_capture_recv_data, async_read_closed, reset_usb_device, start_time); is_device_capture_recv_data[i].is_buffer_free_shared_mutex->specific_timed_lock(i); } } void wait_one_is_device_buffer_free(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_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_CAPTURE_RECEIVED_DATA_BUFFERS; i++) { if(!is_device_capture_recv_data[i].in_use) done = true; } if(!done) { if(has_too_much_time_passed(start_time)) return; if(get_is_device_status(is_device_capture_recv_data) < 0) return; int dummy = 0; is_device_capture_recv_data[0].is_buffer_free_shared_mutex->general_timed_lock(&dummy); } } } bool is_device_are_buffers_all_free(ISDeviceCaptureReceivedData* is_device_capture_recv_data) { return is_device_get_num_free_buffers(is_device_capture_recv_data) == NUM_CAPTURE_RECEIVED_DATA_BUFFERS; } ISDeviceCaptureReceivedData* is_device_get_free_buffer(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data) { wait_one_is_device_buffer_free(capture_data, is_device_capture_recv_data); if(get_is_device_status(is_device_capture_recv_data) < 0) return NULL; for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) if(!is_device_capture_recv_data[i].in_use) { is_device_capture_recv_data[i].is_buffer_free_shared_mutex->specific_try_lock(i); is_device_capture_recv_data[i].in_use = true; return &is_device_capture_recv_data[i]; } return NULL; } int get_is_device_status(ISDeviceCaptureReceivedData* is_device_capture_recv_data) { return *is_device_capture_recv_data[0].status; } void error_is_device_status(ISDeviceCaptureReceivedData* is_device_capture_recv_data, int error_val) { if((error_val == 0) || (get_is_device_status(is_device_capture_recv_data) == 0)) *is_device_capture_recv_data[0].status = error_val; } void reset_is_device_status(ISDeviceCaptureReceivedData* is_device_capture_recv_data) { error_is_device_status(is_device_capture_recv_data, 0); } void is_device_acquisition_main_loop(CaptureData* capture_data) { if(!usb_is_initialized()) return; bool is_done_thread; ConsumerMutex has_data_been_processed; std::thread async_processing_thread; uint32_t last_index = -1; int status = 0; SharedConsumerMutex is_buffer_free_shared_mutex(NUM_CAPTURE_RECEIVED_DATA_BUFFERS); SharedConsumerMutex is_transfer_done_shared_mutex(NUM_CAPTURE_RECEIVED_DATA_BUFFERS); SharedConsumerMutex is_transfer_data_ready_shared_mutex(NUM_CAPTURE_RECEIVED_DATA_BUFFERS); std::chrono::time_point clock_start = std::chrono::high_resolution_clock::now(); ISDeviceCaptureReceivedData* is_device_capture_recv_data = new ISDeviceCaptureReceivedData[NUM_CAPTURE_RECEIVED_DATA_BUFFERS]; for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) { is_device_capture_recv_data[i].in_use = false; is_device_capture_recv_data[i].index = i; is_device_capture_recv_data[i].capture_data = capture_data; is_device_capture_recv_data[i].last_index = &last_index; is_device_capture_recv_data[i].clock_start = &clock_start; is_device_capture_recv_data[i].is_buffer_free_shared_mutex = &is_buffer_free_shared_mutex; is_device_capture_recv_data[i].status = &status; is_device_capture_recv_data[i].cb_data.actual_user_data = &is_device_capture_recv_data[i]; is_device_capture_recv_data[i].cb_data.transfer_data = NULL; is_device_capture_recv_data[i].cb_data.is_transfer_done_mutex = &is_transfer_done_shared_mutex; is_device_capture_recv_data[i].cb_data.internal_index = i; is_device_capture_recv_data[i].cb_data.is_transfer_data_ready_mutex = &is_transfer_data_ready_shared_mutex; is_device_capture_recv_data[i].cb_data.is_data_ready = false; } SetupISDeviceAsyncThread((is_device_device_handlers*)capture_data->handle, is_device_capture_recv_data, &async_processing_thread, &is_done_thread, &has_data_been_processed); capture_data->status.reset_hardware = false; switch(((const is_device_usb_device*)(capture_data->status.device.descriptor))->device_type) { case IS_NITRO_EMULATOR_DEVICE: is_nitro_acquisition_emulator_main_loop(capture_data, is_device_capture_recv_data); break; case IS_NITRO_CAPTURE_DEVICE: is_nitro_acquisition_capture_main_loop(capture_data, is_device_capture_recv_data); break; case IS_TWL_CAPTURE_DEVICE: is_twl_acquisition_capture_main_loop(capture_data, is_device_capture_recv_data); break; default: break; } wait_all_is_device_buffers_free(capture_data, is_device_capture_recv_data); EndISDeviceAsyncThread((is_device_device_handlers*)capture_data->handle, is_device_capture_recv_data, &async_processing_thread, &is_done_thread, &has_data_been_processed); delete []is_device_capture_recv_data; } void usb_is_device_acquisition_cleanup(CaptureData* capture_data) { if(!usb_is_initialized()) return; is_device_connection_end((is_device_device_handlers*)capture_data->handle, (const is_device_usb_device*)capture_data->status.device.descriptor); capture_data->handle = NULL; } bool is_device_is_capture(CaptureDevice* device) { if(device->cc_type != CAPTURE_CONN_IS_NITRO) return false; const is_device_usb_device* usb_device_info = (const is_device_usb_device*)device->descriptor; return (usb_device_info->device_type == IS_TWL_CAPTURE_DEVICE) || (usb_device_info->device_type == IS_NITRO_CAPTURE_DEVICE); } bool is_device_is_nitro(CaptureDevice* device) { if(device->cc_type != CAPTURE_CONN_IS_NITRO) return false; const is_device_usb_device* usb_device_info = (const is_device_usb_device*)device->descriptor; return (usb_device_info->device_type == IS_NITRO_EMULATOR_DEVICE) || (usb_device_info->device_type == IS_NITRO_CAPTURE_DEVICE); } bool is_device_is_twl(CaptureDevice* device) { if(device->cc_type != CAPTURE_CONN_IS_NITRO) return false; const is_device_usb_device* usb_device_info = (const is_device_usb_device*)device->descriptor; return (usb_device_info->device_type == IS_TWL_CAPTURE_DEVICE); } void usb_is_device_init() { return usb_init(); } void usb_is_device_close() { usb_close(); }