From 7c3cc5fee8c8d0abd172f42a9d98908e344911ef Mon Sep 17 00:00:00 2001 From: Lorenzooone Date: Sat, 19 Oct 2024 21:47:33 +0200 Subject: [PATCH] Add IS Nitro Capture support --- README.md | 2 +- include/capture_structs.hpp | 6 +- include/usb_is_nitro.hpp | 59 +++--- include/utils.hpp | 4 + source/3dscapture_ftd3.cpp | 2 +- source/usb_ds_3ds_capture.cpp | 27 +-- source/usb_is_nitro.cpp | 312 ++++++++++++++++++++++++-------- source/usb_is_nitro_capture.cpp | 203 +++++++++++++++------ source/utils.cpp | 40 +++- usb_rules/51-isnitro.rules | 2 + 10 files changed, 461 insertions(+), 196 deletions(-) diff --git a/README.md b/README.md index cffb29e..1ed614a 100755 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ cc3dsfs is a multi-platform capture and display program for [3dscapture's](https://3dscapture.com/) N3DSXL, 3DS and DS (old) capture boards written in C++. The main goal is to offer the ability to use the Capture Card with a TV, via fullscreen mode. -IS Nitro Emulator support (for newer revisions) is also present. Though results may vary (and the amount of video delay may be significantly higher based on the cable used). +IS Nitro Emulator (newer revisions) and IS Nitro Capture support is also present. Though results may vary (and the amount of video delay may be significantly higher based on the cable used). ## Features diff --git a/include/capture_structs.hpp b/include/capture_structs.hpp index b2130b6..16a253f 100755 --- a/include/capture_structs.hpp +++ b/include/capture_structs.hpp @@ -102,19 +102,19 @@ union CaptureReceived { }; struct CaptureDevice { - CaptureDevice(std::string serial_number, std::string name, CaptureConnectionType cc_type, bool is_3ds, bool has_3d, bool has_audio, int width, int height, int max_samples_in, int rgb_bits_size, int base_rotation, int top_screen_x, int top_screen_y, int bot_screen_x, int bot_screen_y): serial_number(serial_number), name(name), cc_type(cc_type), is_3ds(is_3ds), has_3d(has_3d), has_audio(has_audio), width(width), height(height), max_samples_in(max_samples_in), rgb_bits_size(rgb_bits_size), base_rotation(base_rotation), top_screen_x(top_screen_x), top_screen_y(top_screen_y), bot_screen_x(bot_screen_x), bot_screen_y(bot_screen_y) {} - CaptureDevice(): serial_number(""), name(""), cc_type(CAPTURE_CONN_USB), is_3ds(false), has_3d(false), has_audio(false), width(0), height(0), max_samples_in(0), rgb_bits_size(0), base_rotation(0), top_screen_x(0), top_screen_y(0), bot_screen_x(0), bot_screen_y(0) {} + CaptureDevice(std::string serial_number, std::string name, CaptureConnectionType cc_type, const void* descriptor, bool is_3ds, bool has_3d, bool has_audio, int width, int height, int max_samples_in, int base_rotation, int top_screen_x, int top_screen_y, int bot_screen_x, int bot_screen_y): serial_number(serial_number), name(name), cc_type(cc_type), descriptor(descriptor), is_3ds(is_3ds), has_3d(has_3d), has_audio(has_audio), width(width), height(height), max_samples_in(max_samples_in), base_rotation(base_rotation), top_screen_x(top_screen_x), top_screen_y(top_screen_y), bot_screen_x(bot_screen_x), bot_screen_y(bot_screen_y) {} + CaptureDevice(): serial_number(""), name(""), cc_type(CAPTURE_CONN_USB), descriptor(NULL), is_3ds(false), has_3d(false), has_audio(false), width(0), height(0), max_samples_in(0), base_rotation(0), top_screen_x(0), top_screen_y(0), bot_screen_x(0), bot_screen_y(0) {} std::string serial_number; std::string name; CaptureConnectionType cc_type; + const void* descriptor; bool is_3ds; bool has_3d; bool has_audio; int width; int height; int max_samples_in; - int rgb_bits_size; int base_rotation; int top_screen_x; int top_screen_y; diff --git a/include/usb_is_nitro.hpp b/include/usb_is_nitro.hpp index 2e8be91..4999c52 100644 --- a/include/usb_is_nitro.hpp +++ b/include/usb_is_nitro.hpp @@ -7,24 +7,33 @@ #define IS_NITRO_REAL_SERIAL_NUMBER_SIZE 10 -enum is_nitro_forward_enable_values { - IS_NITRO_FORWARD_ENABLE_DISABLE = 0, - IS_NITRO_FORWARD_ENABLE_ENABLE = 1, - IS_NITRO_FORWARD_ENABLE_RESTART = 2, +enum is_nitro_forward_config_values_colors { + IS_NITRO_FORWARD_CONFIG_COLOR_RGB24 = 0, }; -enum is_nitro_forward_config_values { - IS_NITRO_FORWARD_CONFIG_COLOR_RGB24 = 0, +enum is_nitro_forward_config_values_screens { IS_NITRO_FORWARD_CONFIG_MODE_BOTH = 0, - IS_NITRO_FORWARD_CONFIG_MODE_TOP = 4, - IS_NITRO_FORWARD_CONFIG_MODE_BOTTOM = 8, + IS_NITRO_FORWARD_CONFIG_MODE_TOP = 1, + IS_NITRO_FORWARD_CONFIG_MODE_BOTTOM = 2, +}; + +enum is_nitro_forward_config_values_rate { IS_NITRO_FORWARD_CONFIG_RATE_FULL = 0, IS_NITRO_FORWARD_CONFIG_RATE_HALF = 1, IS_NITRO_FORWARD_CONFIG_RATE_THIRD = 2, IS_NITRO_FORWARD_CONFIG_RATE_QUARTER = 3, }; +enum is_nitro_possible_devices { + IS_NITRO_EMULATOR_COMMON_ID, + IS_NITRO_EMULATOR_RARE_ID, + IS_NITRO_CAPTURE_ID, +}; + +#define FIRST_IS_NITRO_DEVICE_ID IS_NITRO_EMULATOR_COMMON_ID + struct is_nitro_usb_device { + std::string name; int vid; int pid; int default_config; @@ -34,29 +43,19 @@ struct is_nitro_usb_device { int ep1_out; int product_id; int manufacturer_id; + bool is_capture; }; -extern const is_nitro_usb_device usb_is_nitro_desc; - -/* -int SendReadCommand(libusb_device_handle *handle, uint16_t command, uint8_t* buf, int length); -int SendWriteCommand(libusb_device_handle *handle, uint16_t command, uint8_t* buf, int length); -int ReadNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_size, uint8_t* buf, int count); -int ReadNecMemU16(libusb_device_handle *handle, uint32_t address, uint16_t* out); -int ReadNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t* out); -int WriteNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_size, uint8_t* buf, int count); -int WriteNecMemU16(libusb_device_handle *handle, uint32_t address, uint16_t value); -int WriteNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t value); -*/ - -int DisableLca2(libusb_device_handle *handle); -int StartUsbCaptureDma(libusb_device_handle *handle); -int StopUsbCaptureDma(libusb_device_handle *handle); -int SetForwardFrameCount(libusb_device_handle *handle, uint16_t count); -int GetFrameCounter(libusb_device_handle *handle, uint16_t* out); -int GetDeviceSerial(libusb_device_handle *handle, uint8_t* buf); -int UpdateFrameForwardConfig(libusb_device_handle *handle, uint8_t value); -int UpdateFrameForwardEnable(libusb_device_handle *handle, uint8_t value); -int ReadFrame(libusb_device_handle *handle, uint8_t* buf, int length); +const is_nitro_usb_device* GetISNitroDesc(is_nitro_possible_devices wanted_device_id); +int DisableLca2(libusb_device_handle *handle, const is_nitro_usb_device* device_desc); +int StartUsbCaptureDma(libusb_device_handle *handle, const is_nitro_usb_device* device_desc); +int StopUsbCaptureDma(libusb_device_handle *handle, const is_nitro_usb_device* device_desc); +int SetForwardFrameCount(libusb_device_handle *handle, uint16_t count, const is_nitro_usb_device* device_desc); +int SetForwardFramePermanent(libusb_device_handle *handle, const is_nitro_usb_device* device_desc); +int GetFrameCounter(libusb_device_handle *handle, uint16_t* out, const is_nitro_usb_device* device_desc); +int GetDeviceSerial(libusb_device_handle *handle, uint8_t* buf, const is_nitro_usb_device* device_desc); +int UpdateFrameForwardConfig(libusb_device_handle *handle, is_nitro_forward_config_values_colors colors, is_nitro_forward_config_values_screens screens, is_nitro_forward_config_values_rate rate, const is_nitro_usb_device* device_desc); +int UpdateFrameForwardEnable(libusb_device_handle *handle, bool enable, bool restart, const is_nitro_usb_device* device_desc); +int ReadFrame(libusb_device_handle *handle, uint8_t* buf, int length, const is_nitro_usb_device* device_desc); #endif diff --git a/include/utils.hpp b/include/utils.hpp index d7d18a3..c228a45 100755 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -33,6 +33,10 @@ uint32_t to_le(uint32_t value); uint32_t to_be(uint32_t value); uint16_t to_le(uint16_t value); uint16_t to_be(uint16_t value); +uint32_t from_le(uint32_t value); +uint32_t from_be(uint32_t value); +uint16_t from_le(uint16_t value); +uint16_t from_be(uint16_t value); std::string get_float_str_decimals(float value, int decimals); void init_threads(void); diff --git a/source/3dscapture_ftd3.cpp b/source/3dscapture_ftd3.cpp index 83985d9..ad977b2 100755 --- a/source/3dscapture_ftd3.cpp +++ b/source/3dscapture_ftd3.cpp @@ -98,7 +98,7 @@ void list_devices_ftd3(std::vector &devices_list) { for(int j = 0; j < sizeof(valid_descriptions) / sizeof(*valid_descriptions); j++) { if(Description == valid_descriptions[j]) { for(int u = 0; u < debug_multiplier; u++) - devices_list.emplace_back(std::string(SerialNumber), "N3DSXL", CAPTURE_CONN_FTD3, true, true, true, HEIGHT_3DS, TOP_WIDTH_3DS + BOT_WIDTH_3DS, N3DSXL_SAMPLES_IN, IN_VIDEO_BPP_SIZE_3DS, 90, 0, 0, TOP_WIDTH_3DS, 0); + devices_list.emplace_back(std::string(SerialNumber), "N3DSXL", CAPTURE_CONN_FTD3, (void*)NULL, true, true, true, HEIGHT_3DS, TOP_WIDTH_3DS + BOT_WIDTH_3DS, N3DSXL_SAMPLES_IN, 90, 0, 0, TOP_WIDTH_3DS, 0); break; } } diff --git a/source/usb_ds_3ds_capture.cpp b/source/usb_ds_3ds_capture.cpp index 70d0f11..ec1f0c7 100755 --- a/source/usb_ds_3ds_capture.cpp +++ b/source/usb_ds_3ds_capture.cpp @@ -26,7 +26,6 @@ enum usb_capture_status { struct usb_device { bool is_3ds; - int bpp; int vid; int pid; int default_config; @@ -51,7 +50,7 @@ struct usb_ds_status { }; static const usb_device usb_3ds_desc = { -.is_3ds = true, .bpp = IN_VIDEO_BPP_SIZE_3DS, +.is_3ds = true, .vid = 0x16D0, .pid = 0x06A3, .default_config = 1, .capture_interface = 0, .control_timeout = 30, .bulk_timeout = 50, @@ -63,7 +62,7 @@ static const usb_device usb_3ds_desc = { }; static const usb_device usb_old_ds_desc = { -.is_3ds = false, .bpp = IN_VIDEO_BPP_SIZE_DS_BASIC, +.is_3ds = false, .vid = 0x16D0, .pid = 0x0647, .default_config = 1, .capture_interface = 0, .control_timeout = 500, .bulk_timeout = 500, @@ -115,12 +114,7 @@ static bool capture_get_has_3d(libusb_device_handle *handle, const usb_device* u } static const usb_device* _get_usb_device_desc(CaptureDevice* device) { - const usb_device* usb_device_desc = &usb_3ds_desc; - if(!device->is_3ds) { - if(device->rgb_bits_size == usb_old_ds_desc.bpp) - usb_device_desc = &usb_old_ds_desc; - } - return usb_device_desc; + return (usb_device*)device->descriptor; } static const usb_device* get_usb_device_desc(CaptureData* capture_data) { @@ -149,16 +143,9 @@ static bool insert_device(std::vector &devices_list, const usb_de return true; std::string serial_str = get_serial(handle, usb_descriptor, curr_serial_extra_id); if(usb_device_desc->is_3ds) - devices_list.emplace_back(serial_str, "3DS", CAPTURE_CONN_USB, true, capture_get_has_3d(handle, usb_device_desc), true, HEIGHT_3DS, TOP_WIDTH_3DS + BOT_WIDTH_3DS, O3DS_SAMPLES_IN, usb_device_desc->bpp, 90, 0, 0, TOP_WIDTH_3DS, 0); - else { - bool has_audio = false; - int max_samples_in = 0; - if(usb_device_desc->bpp > IN_VIDEO_BPP_SIZE_DS_BASIC) { - has_audio = true; - max_samples_in = DS_SAMPLES_IN; - } - devices_list.emplace_back(serial_str, "DS", CAPTURE_CONN_USB, false, false, has_audio, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, max_samples_in, usb_device_desc->bpp, 0, 0, 0, 0, HEIGHT_DS); - } + devices_list.emplace_back(serial_str, "3DS", CAPTURE_CONN_USB, (void*)usb_device_desc, true, capture_get_has_3d(handle, usb_device_desc), true, HEIGHT_3DS, TOP_WIDTH_3DS + BOT_WIDTH_3DS, O3DS_SAMPLES_IN, 90, 0, 0, TOP_WIDTH_3DS, 0); + else + devices_list.emplace_back(serial_str, "DS", CAPTURE_CONN_USB, (void*)usb_device_desc, false, false, false, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, 0, 0, 0, 0, 0, HEIGHT_DS); libusb_close(handle); return true; } @@ -440,7 +427,7 @@ void usb_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, Cap if(!enabled_3d) usb_3DSconvertVideoToOutput(&p_in->usb_received_3ds, p_out); } - else if(capture_device->rgb_bits_size == IN_VIDEO_BPP_SIZE_DS_BASIC) + else usb_oldDSconvertVideoToOutput(&p_in->usb_received_old_ds, p_out); } diff --git a/source/usb_is_nitro.cpp b/source/usb_is_nitro.cpp index 6cb22e6..8b5b60b 100644 --- a/source/usb_is_nitro.cpp +++ b/source/usb_is_nitro.cpp @@ -31,26 +31,51 @@ #define REG_USB_DMA_CONTROL_2 0x0C000028 #define REG_USB_BIU_CONTROL_2 0x0C0000A4 -enum is_nitro_packet_dir { - IS_NITRO_PACKET_DIR_WRITE = 0x10, - IS_NITRO_PACKET_DIR_READ = 0x11 +enum is_nitro_packet_emulator_dir { + IS_NITRO_PACKET_EMU_DIR_WRITE = 0x10, + IS_NITRO_PACKET_EMU_DIR_READ = 0x11 +}; + +enum is_nitro_packet_capture_dir { + IS_NITRO_PACKET_CAP_DIR_WRITE = 0x00, + IS_NITRO_PACKET_CAP_DIR_READ = 0x01 }; enum is_nitro_packet_type { IS_NITRO_PACKET_TYPE_COMMAND = 0, IS_NITRO_PACKET_TYPE_EMU_MEMORY = 1, - IS_NITRO_PACKET_TYPE_AGB_SRAM = 2, + IS_NITRO_EMULATOR_PACKET_TYPE_AGB_SRAM = 2, + IS_NITRO_CAPTURE_PACKET_TYPE_DMA_CONTROL = 2, IS_NITRO_PACKET_TYPE_CAPTURE = 3, IS_NITRO_PACKET_TYPE_AGB_CART_ROM = 4, IS_NITRO_PACKET_TYPE_AGB_BUS2 = 5 }; -enum is_nitro_command { - IS_NITRO_CMD_GET_SERIAL = 0x13, - IS_NITRO_CMD_READ_NEC_MEM = 0x17, - IS_NITRO_CMD_WRITE_NEC_MEM = 0x24, - IS_NITRO_CMD_SET_READ_NEC_MEM = 0x25, - IS_NITRO_CMD_AD = 0xAD, +enum is_nitro_emulator_command { + IS_NITRO_EMU_CMD_GET_SERIAL = 0x13, + IS_NITRO_EMU_CMD_READ_NEC_MEM = 0x17, + IS_NITRO_EMU_CMD_WRITE_NEC_MEM = 0x24, + IS_NITRO_EMU_CMD_SET_READ_NEC_MEM = 0x25, + IS_NITRO_EMU_CMD_AD = 0xAD, +}; + +enum is_nitro_capture_command { + IS_NITRO_CAP_CMD_ENABLE_CAP = 0x00, + IS_NITRO_CAP_CMD_GET_SERIAL = 0x03, + IS_NITRO_CAP_CMD_SET_FWD_RATE = 0x12, + IS_NITRO_CAP_CMD_SET_FWD_MODE = 0x13, + IS_NITRO_CAP_CMD_SET_FWD_COLOURS = 0x14, + IS_NITRO_CAP_CMD_SET_FWD_FRAMES = 0x15, + IS_NITRO_CAP_CMD_SET_FWD_RESTART = 0x1C, +}; + +enum is_nitro_emulator_forward_bits { + IS_NITRO_EMULATOR_FORWARD_ENABLE_BIT = 0, + IS_NITRO_EMULATOR_FORWARD_COUNTER_RESTART_BIT = 1, +}; + +enum is_nitro_capture_forward_bits { + IS_NITRO_CAPTURE_FORWARD_COUNTER_RESTART_BIT = 0, }; #pragma pack(push, 1) @@ -71,14 +96,46 @@ struct PACKED is_nitro_packet_header { }; #pragma pack(pop) -const is_nitro_usb_device usb_is_nitro_desc = { +const is_nitro_usb_device usb_is_nitro_emu_rare_desc = { +.name = "IS Nitro Emulator(R)", +.vid = 0x0f6e, .pid = 0x0400, +.default_config = 1, .default_interface = 0, +.bulk_timeout = 500, +.ep2_in = 2 | LIBUSB_ENDPOINT_IN, .ep1_out = 1 | LIBUSB_ENDPOINT_OUT, +.product_id = 2, .manufacturer_id = 1, .is_capture = false +}; + +const is_nitro_usb_device usb_is_nitro_emu_common_desc = { +.name = "IS Nitro Emulator", .vid = 0x0f6e, .pid = 0x0404, .default_config = 1, .default_interface = 0, .bulk_timeout = 500, .ep2_in = 2 | LIBUSB_ENDPOINT_IN, .ep1_out = 1 | LIBUSB_ENDPOINT_OUT, -.product_id = 2, .manufacturer_id = 1 +.product_id = 2, .manufacturer_id = 1, .is_capture = false }; +const is_nitro_usb_device usb_is_nitro_cap_desc = { +.name = "IS Nitro Capture", +.vid = 0x0f6e, .pid = 0x0403, +.default_config = 1, .default_interface = 0, +.bulk_timeout = 500, +.ep2_in = 2 | LIBUSB_ENDPOINT_IN, .ep1_out = 1 | LIBUSB_ENDPOINT_OUT, +.product_id = 2, .manufacturer_id = 1, .is_capture = true +}; + +const is_nitro_usb_device* GetISNitroDesc(is_nitro_possible_devices wanted_device_id) { + switch(wanted_device_id) { + case IS_NITRO_EMULATOR_COMMON_ID: + return &usb_is_nitro_emu_common_desc; + case IS_NITRO_EMULATOR_RARE_ID: + return &usb_is_nitro_emu_rare_desc; + case IS_NITRO_CAPTURE_ID: + return &usb_is_nitro_cap_desc; + default: + return &usb_is_nitro_emu_common_desc; + } +} + static void fix_endianness_header(is_nitro_packet_header* header) { header->command = to_le(header->command); header->address = to_le(header->address); @@ -100,10 +157,15 @@ static int bulk_in(libusb_device_handle *handle, const is_nitro_usb_device* usb_ return libusb_bulk_transfer(handle, usb_device_desc->ep2_in, buf, length, transferred, usb_device_desc->bulk_timeout); } -static int SendWritePacket(libusb_device_handle *handle, uint16_t command, is_nitro_packet_type type, uint32_t address, uint8_t* buf, int length) { +static int SendWritePacket(libusb_device_handle *handle, uint16_t command, is_nitro_packet_type type, uint32_t address, uint8_t* buf, int length, const is_nitro_usb_device* device_desc, bool expect_result = true) { is_nitro_packet_header header; + bool append_mode = true; + if(device_desc->is_capture) + append_mode = false; uint8_t single_usb_packet[USB_PACKET_LIMIT]; int single_packet_covered_size = USB_PACKET_LIMIT - sizeof(header); + if(!append_mode) + single_packet_covered_size = USB_PACKET_LIMIT; int num_iters = (length + single_packet_covered_size - 1) / single_packet_covered_size; if(!num_iters) num_iters = 1; @@ -112,105 +174,169 @@ static int SendWritePacket(libusb_device_handle *handle, uint16_t command, is_ni if(transfer_size > single_packet_covered_size) transfer_size = single_packet_covered_size; + uint8_t packet_direction = IS_NITRO_PACKET_EMU_DIR_WRITE; + if(device_desc->is_capture) + packet_direction = IS_NITRO_PACKET_CAP_DIR_WRITE; + header.command = command; - header.direction = IS_NITRO_PACKET_DIR_WRITE; + header.direction = packet_direction; header.type = type; header.address = address; header.length = transfer_size; header.padding = 0; fix_endianness_header(&header); + int ret = 0; + int num_bytes = 0; for(int j = 0; j < sizeof(is_nitro_packet_header); j++) single_usb_packet[j] = ((uint8_t*)&header)[j]; - for(int j = 0; j < transfer_size; j++) - single_usb_packet[sizeof(is_nitro_packet_header) + j] = buf[(i * single_packet_covered_size) + j]; - int num_bytes = 0; - int ret = bulk_out(handle, &usb_is_nitro_desc, single_usb_packet, transfer_size + sizeof(is_nitro_packet_header), &num_bytes); + if(append_mode && (buf != NULL)) { + for(int j = 0; j < transfer_size; j++) + single_usb_packet[sizeof(is_nitro_packet_header) + j] = buf[(i * single_packet_covered_size) + j]; + ret = bulk_out(handle, device_desc, single_usb_packet, transfer_size + sizeof(is_nitro_packet_header), &num_bytes); + } + else { + int header_bytes = 0; + ret = bulk_out(handle, device_desc, single_usb_packet, sizeof(is_nitro_packet_header), &header_bytes); + if(ret < 0) + return ret; + if(header_bytes != sizeof(is_nitro_packet_header)) + return LIBUSB_ERROR_INTERRUPTED; + if((buf != NULL) && (transfer_size > 0)) + ret = bulk_out(handle, device_desc, &buf[(i * single_packet_covered_size)], transfer_size, &num_bytes); + num_bytes += header_bytes; + } if(ret < 0) return ret; if(num_bytes != (transfer_size + sizeof(is_nitro_packet_header))) return LIBUSB_ERROR_INTERRUPTED; } + if(device_desc->is_capture && expect_result) { + uint8_t status[16]; + int status_bytes = 0; + int ret = bulk_in(handle, device_desc, status, sizeof(status), &status_bytes); + if(ret < 0) + return ret; + if(status_bytes != sizeof(status)) + return LIBUSB_ERROR_INTERRUPTED; + if(status[0] != 1) + return LIBUSB_ERROR_OTHER; + } return LIBUSB_SUCCESS; } -static int SendReadPacket(libusb_device_handle *handle, uint16_t command, is_nitro_packet_type type, uint32_t address, uint8_t* buf, int length) { +static int SendReadPacket(libusb_device_handle *handle, uint16_t command, is_nitro_packet_type type, uint32_t address, uint8_t* buf, int length, const is_nitro_usb_device* device_desc, bool expect_result = true) { is_nitro_packet_header header; int single_packet_covered_size = USB_PACKET_LIMIT; int num_iters = (length + single_packet_covered_size - 1) / single_packet_covered_size; + if(num_iters == 0) + num_iters = 1; for(int i = 0; i < num_iters; i++) { int transfer_size = length - (i * single_packet_covered_size); if(transfer_size > single_packet_covered_size) transfer_size = single_packet_covered_size; + uint8_t packet_direction = IS_NITRO_PACKET_EMU_DIR_READ; + if(device_desc->is_capture) + packet_direction = IS_NITRO_PACKET_CAP_DIR_READ; + header.command = command; - header.direction = IS_NITRO_PACKET_DIR_READ; + header.direction = packet_direction; header.type = type; header.address = address; header.length = transfer_size; header.padding = 0; fix_endianness_header(&header); int num_bytes = 0; - int ret = bulk_out(handle, &usb_is_nitro_desc, (uint8_t*)&header, sizeof(is_nitro_packet_header), &num_bytes); + int ret = bulk_out(handle, device_desc, (uint8_t*)&header, sizeof(is_nitro_packet_header), &num_bytes); if(ret < 0) return ret; if(num_bytes != sizeof(is_nitro_packet_header)) return LIBUSB_ERROR_INTERRUPTED; - ret = bulk_in(handle, &usb_is_nitro_desc, buf + (i * single_packet_covered_size), transfer_size, &num_bytes); + if(buf != NULL) { + ret = bulk_in(handle, device_desc, buf + (i * single_packet_covered_size), transfer_size, &num_bytes); + if(ret < 0) + return ret; + if(num_bytes != transfer_size) + return LIBUSB_ERROR_INTERRUPTED; + } + } + if(device_desc->is_capture && expect_result) { + uint8_t status[16]; + int status_bytes = 0; + int ret = bulk_in(handle, device_desc, status, sizeof(status), &status_bytes); if(ret < 0) return ret; - if(num_bytes != transfer_size) + if(status_bytes != sizeof(status)) return LIBUSB_ERROR_INTERRUPTED; + if(status[0] != 1) + return LIBUSB_ERROR_OTHER; } return LIBUSB_SUCCESS; } -int SendReadCommand(libusb_device_handle *handle, uint16_t command, uint8_t* buf, int length) { - return SendReadPacket(handle, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length); +int SendReadCommand(libusb_device_handle *handle, uint16_t command, uint8_t* buf, int length, const is_nitro_usb_device* device_desc) { + return SendReadPacket(handle, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length, device_desc); } -int SendWriteCommand(libusb_device_handle *handle, uint16_t command, uint8_t* buf, int length) { - return SendWritePacket(handle, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length); +int SendWriteCommand(libusb_device_handle *handle, uint16_t command, uint8_t* buf, int length, const is_nitro_usb_device* device_desc) { + return SendWritePacket(handle, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length, device_desc); } -int GetDeviceSerial(libusb_device_handle *handle, uint8_t* buf) { - return SendReadCommand(handle, IS_NITRO_CMD_GET_SERIAL, buf, IS_NITRO_REAL_SERIAL_NUMBER_SIZE); +int SendReadCommandU32(libusb_device_handle *handle, uint16_t command, uint32_t* out, const is_nitro_usb_device* device_desc) { + uint32_t buffer; + int ret = SendReadCommand(handle, command, (uint8_t*)&buffer, sizeof(uint32_t), device_desc); + if(ret < 0) + return ret; + *out = from_le(buffer); + return 0; } -int ReadNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_size, uint8_t* buf, int count) { +int SendWriteCommandU32(libusb_device_handle *handle, uint16_t command, uint32_t value, const is_nitro_usb_device* device_desc) { + uint32_t buffer = to_le(value); + return SendWriteCommand(handle, command, (uint8_t*)&buffer, sizeof(uint32_t), device_desc); +} + +int GetDeviceSerial(libusb_device_handle *handle, uint8_t* buf, const is_nitro_usb_device* device_desc) { + if(device_desc->is_capture) + return SendReadCommand(handle, IS_NITRO_CAP_CMD_GET_SERIAL, buf, IS_NITRO_REAL_SERIAL_NUMBER_SIZE, device_desc); + return SendReadCommand(handle, IS_NITRO_EMU_CMD_GET_SERIAL, buf, IS_NITRO_REAL_SERIAL_NUMBER_SIZE, device_desc); +} + +int ReadNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_size, uint8_t* buf, int count, const is_nitro_usb_device* device_desc) { is_nitro_nec_packet_header header; - header.command = IS_NITRO_CMD_SET_READ_NEC_MEM; + header.command = IS_NITRO_EMU_CMD_SET_READ_NEC_MEM; header.unit_size = unit_size; header.count = count; header.address = address; fix_endianness_header(&header); - int ret = SendWriteCommand(handle, header.command, (uint8_t*)&header, sizeof(is_nitro_nec_packet_header)); + int ret = SendWriteCommand(handle, header.command, (uint8_t*)&header, sizeof(is_nitro_nec_packet_header), device_desc); if(ret < 0) return ret; - return SendReadCommand(handle, IS_NITRO_CMD_READ_NEC_MEM, buf, count * unit_size); + return SendReadCommand(handle, IS_NITRO_EMU_CMD_READ_NEC_MEM, buf, count * unit_size, device_desc); } -int ReadNecMemU16(libusb_device_handle *handle, uint32_t address, uint16_t* out) { - uint8_t buffer[2]; - int ret = ReadNecMem(handle, address, 2, buffer, 1); +int ReadNecMemU16(libusb_device_handle *handle, uint32_t address, uint16_t* out, const is_nitro_usb_device* device_desc) { + uint16_t buffer; + int ret = ReadNecMem(handle, address, 2, (uint8_t*)&buffer, 1, device_desc); if(ret < 0) return ret; - *out = buffer[0] | (buffer[1] << 8); + *out = from_le(buffer); return 0; } -int ReadNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t* out) { - uint8_t buffer[4]; - int ret = ReadNecMem(handle, address, 2, buffer, 2); +int ReadNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t* out, const is_nitro_usb_device* device_desc) { + uint32_t buffer; + int ret = ReadNecMem(handle, address, 2, (uint8_t*)&buffer, 2, device_desc); if(ret < 0) return ret; - *out = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + *out = from_le(buffer); return 0; } -int WriteNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_size, uint8_t* buf, int count) { +int WriteNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_size, uint8_t* buf, int count, const is_nitro_usb_device* device_desc) { uint8_t* buffer = new uint8_t[(count * unit_size) + sizeof(is_nitro_nec_packet_header)]; is_nitro_nec_packet_header header; - header.command = IS_NITRO_CMD_WRITE_NEC_MEM; + header.command = IS_NITRO_EMU_CMD_WRITE_NEC_MEM; header.unit_size = unit_size; header.count = count; header.address = address; @@ -219,80 +345,106 @@ int WriteNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_siz buffer[i] = ((uint8_t*)&header)[i]; for(int i = 0; i < count * unit_size; i++) buffer[i + sizeof(is_nitro_nec_packet_header)] = buf[i]; - int ret = SendWriteCommand(handle, header.command, buffer, (count * unit_size) + sizeof(is_nitro_nec_packet_header)); + int ret = SendWriteCommand(handle, header.command, buffer, (count * unit_size) + sizeof(is_nitro_nec_packet_header), device_desc); delete []buffer; return ret; } -int WriteNecMemU16(libusb_device_handle *handle, uint32_t address, uint16_t value) { - uint8_t buffer[2]; - buffer[0] = value & 0xFF; - buffer[1] = (value >> 8) & 0xFF; - return WriteNecMem(handle, address, 2, buffer, 1); +int WriteNecMemU16(libusb_device_handle *handle, uint32_t address, uint16_t value, const is_nitro_usb_device* device_desc) { + uint16_t buffer = to_le(value); + return WriteNecMem(handle, address, 2, (uint8_t*)&buffer, 1, device_desc); } -int WriteNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t value) { - uint8_t buffer[4]; - buffer[0] = value & 0xFF; - buffer[1] = (value >> 8) & 0xFF; - buffer[2] = (value >> 16) & 0xFF; - buffer[3] = (value >> 24) & 0xFF; - return WriteNecMem(handle, address, 2, buffer, 2); +int WriteNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t value, const is_nitro_usb_device* device_desc) { + uint32_t buffer = to_le(value); + return WriteNecMem(handle, address, 2, (uint8_t*)&buffer, 2, device_desc); } -int DisableLca2(libusb_device_handle *handle) { - int ret = WriteNecMemU16(handle, 0x00805180, 0); +int DisableLca2(libusb_device_handle *handle, const is_nitro_usb_device* device_desc) { + if(device_desc->is_capture) + return LIBUSB_SUCCESS; + int ret = WriteNecMemU16(handle, 0x00805180, 0, device_desc); if(ret < 0) return ret; - ret = WriteNecMemU16(handle, 0xF84000A, 1); + ret = WriteNecMemU16(handle, 0x0F84000A, 1, device_desc); if(ret < 0) return ret; //default_sleep(2000); - return WriteNecMemU16(handle, 0xF84000A, 0); + return WriteNecMemU16(handle, 0x0F84000A, 0, device_desc); } -int StartUsbCaptureDma(libusb_device_handle *handle) { - int ret = WriteNecMemU16(handle, REG_USB_DMA_CONTROL_2, 2); - if(ret < 0) - return ret; - return WriteNecMemU16(handle, REG_USB_BIU_CONTROL_2, 1); +int StartUsbCaptureDma(libusb_device_handle *handle, const is_nitro_usb_device* device_desc) { + if(!device_desc->is_capture) { + int ret = WriteNecMemU16(handle, REG_USB_DMA_CONTROL_2, 2, device_desc); + if(ret < 0) + return ret; + return WriteNecMemU16(handle, REG_USB_BIU_CONTROL_2, 1, device_desc); + } + return SendReadPacket(handle, IS_NITRO_CAP_CMD_ENABLE_CAP, IS_NITRO_CAPTURE_PACKET_TYPE_DMA_CONTROL, 0, NULL, 1, device_desc, false); } -int StopUsbCaptureDma(libusb_device_handle *handle) { - int ret = WriteNecMemU16(handle, REG_USB_DMA_CONTROL_2, 0); - if(ret < 0) - return ret; - return WriteNecMemU16(handle, REG_USB_BIU_CONTROL_2, 0); +int StopUsbCaptureDma(libusb_device_handle *handle, const is_nitro_usb_device* device_desc) { + if(!device_desc->is_capture) { + int ret = WriteNecMemU16(handle, REG_USB_DMA_CONTROL_2, 0, device_desc); + if(ret < 0) + return ret; + return WriteNecMemU16(handle, REG_USB_BIU_CONTROL_2, 0, device_desc); + } + return SendReadPacket(handle, IS_NITRO_CAP_CMD_ENABLE_CAP, IS_NITRO_CAPTURE_PACKET_TYPE_DMA_CONTROL, 0, NULL, 0, device_desc); } -int SetForwardFrameCount(libusb_device_handle *handle, uint16_t count) { +int SetForwardFrameCount(libusb_device_handle *handle, uint16_t count, const is_nitro_usb_device* device_desc) { if(!count) return LIBUSB_ERROR_INTERRUPTED; count -= 1; - return WriteNecMemU32(handle, 0x800000C, (count >> 8) | ((count & 0xFF) << 16)); + if(!device_desc->is_capture) + return WriteNecMemU32(handle, 0x0800000C, (count >> 8) | ((count & 0xFF) << 16), device_desc); + return SendWriteCommandU32(handle, IS_NITRO_CAP_CMD_SET_FWD_FRAMES, count, device_desc); } -int GetFrameCounter(libusb_device_handle *handle, uint16_t* out) { +int SetForwardFramePermanent(libusb_device_handle *handle, const is_nitro_usb_device* device_desc) { + return SetForwardFrameCount(handle, 0x4001, device_desc); +} + +int GetFrameCounter(libusb_device_handle *handle, uint16_t* out, const is_nitro_usb_device* device_desc) { uint32_t counter = 0; - int ret = ReadNecMemU32(handle, 0x08000028, &counter); + int ret = ReadNecMemU32(handle, 0x08000028, &counter, device_desc); if(ret < 0) return ret; *out = (counter & 0xFF) | ((counter & 0xFF0000) >> 8); return ret; } -int UpdateFrameForwardConfig(libusb_device_handle *handle, uint8_t value) { - return WriteNecMemU16(handle, 0x0800000A, value); +int UpdateFrameForwardConfig(libusb_device_handle *handle, is_nitro_forward_config_values_colors colors, is_nitro_forward_config_values_screens screens, is_nitro_forward_config_values_rate rate, const is_nitro_usb_device* device_desc) { + if(!device_desc->is_capture) + return WriteNecMemU16(handle, 0x0800000A, ((colors & 1) << 4) | ((screens & 3) << 2) | ((rate & 3) << 0), device_desc); + int ret = SendWriteCommandU32(handle, IS_NITRO_CAP_CMD_SET_FWD_COLOURS, colors & 1, device_desc); + if(ret < 0) + return ret; + ret = SendWriteCommandU32(handle, IS_NITRO_CAP_CMD_SET_FWD_RATE, rate & 3, device_desc); + if(ret < 0) + return ret; + return SendWriteCommandU32(handle, IS_NITRO_CAP_CMD_SET_FWD_MODE, screens & 3, device_desc); } -int UpdateFrameForwardEnable(libusb_device_handle *handle, uint8_t value) { - return WriteNecMemU16(handle, 0x08000008, value); +int UpdateFrameForwardEnable(libusb_device_handle *handle, bool enable, bool restart, const is_nitro_usb_device* device_desc) { + uint32_t value = 0; + if(!device_desc->is_capture) { + if(enable) + value |= (1 << IS_NITRO_EMULATOR_FORWARD_ENABLE_BIT); + if(restart) + value |= (1 << IS_NITRO_EMULATOR_FORWARD_COUNTER_RESTART_BIT); + return WriteNecMemU16(handle, 0x08000008, value, device_desc); + } + if(restart) + value |= (1 << IS_NITRO_CAPTURE_FORWARD_COUNTER_RESTART_BIT); + return SendWriteCommandU32(handle, IS_NITRO_CAP_CMD_SET_FWD_RESTART, value, device_desc); } -int ReadFrame(libusb_device_handle *handle, uint8_t* buf, int length) { +int ReadFrame(libusb_device_handle *handle, uint8_t* buf, int length, const is_nitro_usb_device* device_desc) { // Maybe making this async would be better for lower end hardware... int num_bytes = 0; - int ret = bulk_in(handle, &usb_is_nitro_desc, buf, length, &num_bytes); + int ret = bulk_in(handle, device_desc, buf, length, &num_bytes); if(num_bytes != length) return LIBUSB_ERROR_INTERRUPTED; return ret; diff --git a/source/usb_is_nitro_capture.cpp b/source/usb_is_nitro_capture.cpp index 6447272..aa6b803 100644 --- a/source/usb_is_nitro_capture.cpp +++ b/source/usb_is_nitro_capture.cpp @@ -40,9 +40,12 @@ enum usb_capture_status { USB_CAPTURE_ERROR }; -static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames, CaptureScreensType capture_type); -static int StartCapture(libusb_device_handle *handle, uint16_t *out_frame_count, float* single_frame_time, CaptureScreensType capture_type); -static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames, CaptureScreensType capture_type); +static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); +static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); +static void is_nitro_capture_capture_main_loop(CaptureData* capture_data); +static void is_nitro_emulator_capture_main_loop(CaptureData* capture_data); +static int StartEmulatorCapture(libusb_device_handle *handle, uint16_t &out_frame_count, float &single_frame_time, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); +static int StartCaptureCapture(libusb_device_handle *handle, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); static std::string get_serial(const is_nitro_usb_device* usb_device_desc, libusb_device_handle *handle, int &curr_serial_extra_id) { uint8_t data[SERIAL_NUMBER_SIZE]; @@ -52,13 +55,13 @@ static std::string get_serial(const is_nitro_usb_device* usb_device_desc, libusb conn_success = false; if(conn_success && libusb_claim_interface(handle, usb_device_desc->default_interface) != LIBUSB_SUCCESS) conn_success = false; - if(conn_success && EndCapture(handle, false, 0, CAPTURE_SCREENS_BOTH) != LIBUSB_SUCCESS) + if((!usb_device_desc->is_capture) && conn_success && EndCapture(handle, false, 0, CAPTURE_SCREENS_BOTH, usb_device_desc) != LIBUSB_SUCCESS) conn_success = false; - if(conn_success && (GetDeviceSerial(handle, data) != LIBUSB_SUCCESS)) { + if(conn_success && (GetDeviceSerial(handle, data, usb_device_desc) != LIBUSB_SUCCESS)) { int ret = 0; while(ret >= 0) - ret = drain_frames(handle, FRAME_BUFFER_SIZE * 2, 0, CAPTURE_SCREENS_TOP); - if((GetDeviceSerial(handle, data) != LIBUSB_SUCCESS)) + ret = drain_frames(handle, FRAME_BUFFER_SIZE * 2, 0, CAPTURE_SCREENS_TOP, usb_device_desc); + if((GetDeviceSerial(handle, data, usb_device_desc) != LIBUSB_SUCCESS)) conn_success = false; } if(conn_success) { @@ -72,18 +75,18 @@ static std::string get_serial(const is_nitro_usb_device* usb_device_desc, libusb return serial_str; } -static bool insert_device(std::vector &devices_list, const is_nitro_usb_device* usb_device_desc, libusb_device *usb_device, libusb_device_descriptor *usb_descriptor, int &curr_serial_extra_id) { +static int insert_device(std::vector &devices_list, const is_nitro_usb_device* usb_device_desc, libusb_device *usb_device, libusb_device_descriptor *usb_descriptor, int &curr_serial_extra_id) { libusb_device_handle *handle = NULL; if((usb_descriptor->idVendor != usb_device_desc->vid) || (usb_descriptor->idProduct != usb_device_desc->pid)) - return false; + return LIBUSB_ERROR_NOT_FOUND; if((usb_descriptor->iManufacturer != usb_device_desc->manufacturer_id) || (usb_descriptor->iProduct != usb_device_desc->product_id)) - return false; + return LIBUSB_ERROR_NOT_FOUND; int result = libusb_open(usb_device, &handle); - if(result || (handle == NULL)) - return true; - devices_list.emplace_back(get_serial(usb_device_desc, handle, curr_serial_extra_id), "IS Nitro Emulator", CAPTURE_CONN_IS_NITRO, false, false, false, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, 0, 24, 0, 0, 0, 0, HEIGHT_DS); + if((result < 0) || (handle == NULL)) + return result; + devices_list.emplace_back(get_serial(usb_device_desc, handle, curr_serial_extra_id), usb_device_desc->name, CAPTURE_CONN_IS_NITRO, (void*)usb_device_desc, false, false, false, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, 0, 0, 0, 0, 0, HEIGHT_DS); libusb_close(handle); - return true; + return result; } static libusb_device_handle* usb_find_by_serial_number(const is_nitro_usb_device* usb_device_desc, std::string &serial_number) { @@ -133,36 +136,47 @@ void list_devices_is_nitro(std::vector &devices_list) { int result = libusb_get_device_descriptor(usb_devices[i], &usb_descriptor); if(result < 0) continue; - if(insert_device(devices_list, &usb_is_nitro_desc, usb_devices[i], &usb_descriptor, curr_serial_extra_id_is_nitro_emulator)) + result = insert_device(devices_list, GetISNitroDesc(IS_NITRO_EMULATOR_COMMON_ID), usb_devices[i], &usb_descriptor, curr_serial_extra_id_is_nitro_emulator); + if(result != LIBUSB_ERROR_NOT_FOUND) { continue; + } + result = insert_device(devices_list, GetISNitroDesc(IS_NITRO_EMULATOR_RARE_ID), usb_devices[i], &usb_descriptor, curr_serial_extra_id_is_nitro_emulator); + if(result != LIBUSB_ERROR_NOT_FOUND) { + continue; + } + result = insert_device(devices_list, GetISNitroDesc(IS_NITRO_CAPTURE_ID), usb_devices[i], &usb_descriptor, curr_serial_extra_id_is_nitro_emulator); + if(result != LIBUSB_ERROR_NOT_FOUND) { + continue; + } } if(num_devices >= 0) libusb_free_device_list(usb_devices, 1); } -static void is_nitro_connection_end(libusb_device_handle *dev, bool interface_claimed = true) { +static void is_nitro_connection_end(libusb_device_handle *dev, const is_nitro_usb_device *device_desc, bool interface_claimed = true) { if(interface_claimed) - libusb_release_interface(dev, usb_is_nitro_desc.default_interface); + libusb_release_interface(dev, device_desc->default_interface); libusb_close(dev); } bool is_nitro_connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device) { if(!usb_is_initialized()) return false; - libusb_device_handle *dev = usb_find_by_serial_number(&usb_is_nitro_desc, device->serial_number); + const is_nitro_usb_device* usb_device_info = (const is_nitro_usb_device*)device->descriptor; + libusb_device_handle *dev = usb_find_by_serial_number(usb_device_info, device->serial_number); if(!dev) { capture_error_print(true, capture_data, "Device not found"); return false; } - if(libusb_set_configuration(dev, usb_is_nitro_desc.default_config) != LIBUSB_SUCCESS) { + if(libusb_set_configuration(dev, usb_device_info->default_config) != LIBUSB_SUCCESS) { capture_error_print(true, capture_data, "Configuration failed"); - is_nitro_connection_end(dev, false); + is_nitro_connection_end(dev, usb_device_info, false); return false; } - if(libusb_claim_interface(dev, usb_is_nitro_desc.default_interface) != LIBUSB_SUCCESS) { + if(libusb_claim_interface(dev, usb_device_info->default_interface) != LIBUSB_SUCCESS) { capture_error_print(true, capture_data, "Interface claim failed"); - is_nitro_connection_end(dev, false); + is_nitro_connection_end(dev, usb_device_info, false); return false; } capture_data->handle = (void*)dev; @@ -180,40 +194,41 @@ uint64_t usb_is_nitro_emulator_get_video_in_size(CaptureData* capture_data) { return _is_nitro_emulator_get_video_in_size(capture_data->status.capture_type); } -static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames, CaptureScreensType capture_type) { +static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) { ISNitroEmulatorVideoInputData video_in_buffer; for (int i = start_frames; i < num_frames; i++) { - int ret = ReadFrame(handle, (uint8_t*)&video_in_buffer, _is_nitro_emulator_get_video_in_size(capture_type)); + int ret = ReadFrame(handle, (uint8_t*)&video_in_buffer, _is_nitro_emulator_get_video_in_size(capture_type), usb_device_desc); if(ret < 0) return ret; } return LIBUSB_SUCCESS; } -static int set_capture_mode(libusb_device_handle *handle, CaptureScreensType capture_type) { - uint8_t capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_BOTH; +static int set_capture_mode(libusb_device_handle *handle, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) { + is_nitro_forward_config_values_screens capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_BOTH; if(capture_type == CAPTURE_SCREENS_TOP) capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_TOP; if(capture_type == CAPTURE_SCREENS_BOTTOM) capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_BOTTOM; - return UpdateFrameForwardConfig(handle, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24 | capture_mode_flag | IS_NITRO_FORWARD_CONFIG_RATE_FULL); + return UpdateFrameForwardConfig(handle, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24, capture_mode_flag, IS_NITRO_FORWARD_CONFIG_RATE_FULL, usb_device_desc); } -static int StartCapture(libusb_device_handle *handle, uint16_t &out_frame_count, float &single_frame_time, CaptureScreensType capture_type) { - int ret = DisableLca2(handle); +static int StartEmulatorCapture(libusb_device_handle *handle, uint16_t &out_frame_count, float &single_frame_time, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) { + int ret = 0; + ret = DisableLca2(handle, usb_device_desc); if(ret < 0) return ret; - ret = set_capture_mode(handle, capture_type); + ret = set_capture_mode(handle, capture_type, usb_device_desc); if(ret < 0) return ret; - ret = SetForwardFrameCount(handle, FRAME_BUFFER_SIZE); + ret = SetForwardFrameCount(handle, FRAME_BUFFER_SIZE, usb_device_desc); if(ret < 0) return ret; // Reset this in case it's high. At around 0xFFFF, reading from the USB DMA seems to fail... - ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_RESTART | IS_NITRO_FORWARD_ENABLE_ENABLE); + ret = UpdateFrameForwardEnable(handle, true, true, usb_device_desc); if(ret < 0) return ret; - ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_ENABLE); + ret = UpdateFrameForwardEnable(handle, true, false, usb_device_desc); if(ret < 0) return ret; @@ -221,12 +236,12 @@ static int StartCapture(libusb_device_handle *handle, uint16_t &out_frame_count, auto clock_start = std::chrono::high_resolution_clock::now(); uint16_t oldFrameCount; uint16_t newFrameCount; - ret = GetFrameCounter(handle, &oldFrameCount); + ret = GetFrameCounter(handle, &oldFrameCount, usb_device_desc); if(ret < 0) return ret; newFrameCount = oldFrameCount; while(newFrameCount == oldFrameCount) { - ret = GetFrameCounter(handle, &newFrameCount); + ret = GetFrameCounter(handle, &newFrameCount, usb_device_desc); if(ret < 0) return ret; const auto curr_time = std::chrono::high_resolution_clock::now(); @@ -240,12 +255,12 @@ static int StartCapture(libusb_device_handle *handle, uint16_t &out_frame_count, // We also do this to measure the time that is needed for each frame... // To do so, a minimum of 4 frames is required (FRAME_BUFFER_SIZE - 1 + 4) clock_start = std::chrono::high_resolution_clock::now(); - ret = GetFrameCounter(handle, &oldFrameCount); + ret = GetFrameCounter(handle, &oldFrameCount, usb_device_desc); if(ret < 0) return ret; uint16_t targetFrameCount = (newFrameCount + FRAME_BUFFER_SIZE + 3) & (~(FRAME_BUFFER_SIZE - 1)); while(oldFrameCount != targetFrameCount) { - ret = GetFrameCounter(handle, &oldFrameCount); + ret = GetFrameCounter(handle, &oldFrameCount, usb_device_desc); if(ret < 0) return ret; // Placing a sleep of some kind here would be much better... @@ -273,23 +288,39 @@ static int StartCapture(libusb_device_handle *handle, uint16_t &out_frame_count, // Start the actual DMA if(single_frame_time > 0) { - ret = StartUsbCaptureDma(handle); + ret = StartUsbCaptureDma(handle, usb_device_desc); if(ret < 0) return ret; } return ret; } -static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) { +static int StartCaptureCapture(libusb_device_handle *handle, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) { + int ret = 0; + ret = set_capture_mode(handle, capture_type, usb_device_desc); + if(ret < 0) + return ret; + ret = SetForwardFramePermanent(handle, usb_device_desc); + if(ret < 0) + return ret; + ret = UpdateFrameForwardEnable(handle, true, true, usb_device_desc); + if(ret < 0) + return ret; + return StartUsbCaptureDma(handle, usb_device_desc); +} + +static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) { + if(usb_device_desc->is_capture) + return StopUsbCaptureDma(handle, usb_device_desc); int ret = 0; if(do_drain_frames) - ret = drain_frames(handle, FRAME_BUFFER_SIZE, start_frames, capture_type); + ret = drain_frames(handle, FRAME_BUFFER_SIZE, start_frames, capture_type, usb_device_desc); if(ret < 0) return ret; - ret = StopUsbCaptureDma(handle); + ret = StopUsbCaptureDma(handle, usb_device_desc); if(ret < 0) return ret; - return UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_DISABLE); + return UpdateFrameForwardEnable(handle, false, false, usb_device_desc); } static void frame_wait(float single_frame_time, std::chrono::time_point clock_last_reset, int curr_frame_counter, int last_frame_counter) { @@ -308,11 +339,11 @@ static void frame_wait(float single_frame_time, std::chrono::time_point &clock_last_reset, CaptureScreensType &curr_capture_type, CaptureScreensType wanted_capture_type, int multiplier) { +static int reset_capture_frames(libusb_device_handle* handle, uint16_t &curr_frame_counter, uint16_t &last_frame_counter, float &single_frame_time, std::chrono::time_point &clock_last_reset, CaptureScreensType &curr_capture_type, CaptureScreensType wanted_capture_type, int multiplier, const is_nitro_usb_device* usb_device_desc) { curr_frame_counter += 1; if(curr_frame_counter == FRAME_BUFFER_SIZE) { - int ret = StopUsbCaptureDma(handle); + int ret = StopUsbCaptureDma(handle, usb_device_desc); if(ret < 0) return ret; @@ -320,7 +351,7 @@ int reset_capture_frames(libusb_device_handle* handle, uint16_t &curr_frame_coun // Though it may lag for a bit... if(wanted_capture_type != curr_capture_type) { curr_capture_type = wanted_capture_type; - ret = set_capture_mode(handle, curr_capture_type); + ret = set_capture_mode(handle, curr_capture_type, usb_device_desc); if(ret < 0) return ret; } @@ -331,7 +362,7 @@ int reset_capture_frames(libusb_device_handle* handle, uint16_t &curr_frame_coun int diff_target = FRAME_BUFFER_SIZE * multiplier; do { // Check how many frames have passed... - ret = GetFrameCounter(handle, &internalFrameCount); + ret = GetFrameCounter(handle, &internalFrameCount, usb_device_desc); full_internalFrameCount = internalFrameCount; // Sometimes the upper 8 bits aren't updated... Use only the lower 8 bits. internalFrameCount &= 0xFF; @@ -368,19 +399,19 @@ int reset_capture_frames(libusb_device_handle* handle, uint16_t &curr_frame_coun // If we're nearing 0xFFFF for the frame counter, reset it. // It's a problematic value for DMA reading if(frame_diff && (full_internalFrameCount >= 0xF000)) { - ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_RESTART | IS_NITRO_FORWARD_ENABLE_ENABLE); + ret = UpdateFrameForwardEnable(handle, true, true, usb_device_desc); if(ret < 0) return ret; clock_last_reset = std::chrono::high_resolution_clock::now(); } - ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_ENABLE); + ret = UpdateFrameForwardEnable(handle, true, false, usb_device_desc); if(ret < 0) return ret; curr_frame_counter = 0; // Start the actual DMA if(single_frame_time > 0) { - ret = StartUsbCaptureDma(handle); + ret = StartUsbCaptureDma(handle, usb_device_desc); if(ret < 0) return ret; } @@ -388,15 +419,64 @@ int reset_capture_frames(libusb_device_handle* handle, uint16_t &curr_frame_coun return LIBUSB_SUCCESS; } -void is_nitro_capture_main_loop(CaptureData* capture_data) { - if(!usb_is_initialized()) - return; +static void is_nitro_capture_capture_main_loop(CaptureData* capture_data) { libusb_device_handle *handle = (libusb_device_handle*)capture_data->handle; + const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor; + CaptureScreensType curr_capture_type = capture_data->status.capture_type; + int ret = StartCaptureCapture(handle, curr_capture_type, usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Capture Start: Failed"); + return; + } + int inner_curr_in = 0; + auto clock_start = std::chrono::high_resolution_clock::now(); + auto clock_last_reset = std::chrono::high_resolution_clock::now(); + + while(capture_data->status.connected && capture_data->status.running) { + ret = ReadFrame(handle, (uint8_t*)&capture_data->capture_buf[inner_curr_in], _is_nitro_emulator_get_video_in_size(curr_capture_type), usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Disconnected: Read error"); + break; + } + // Output to the other threads... + 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->time_in_buf[inner_curr_in] = diff.count(); + capture_data->read[inner_curr_in] = _is_nitro_emulator_get_video_in_size(curr_capture_type); + capture_data->capture_type[inner_curr_in] = curr_capture_type; + + inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS; + if(capture_data->status.cooldown_curr_in) + capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1; + capture_data->status.curr_in = inner_curr_in; + capture_data->status.video_wait.unlock(); + capture_data->status.audio_wait.unlock(); + if(curr_capture_type != capture_data->status.capture_type) { + ret = EndCapture(handle, true, 0, curr_capture_type, usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Capture End: Failed"); + return; + } + curr_capture_type = capture_data->status.capture_type; + ret = StartCaptureCapture(handle, curr_capture_type, usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Capture Restart: Failed"); + return; + } + } + } + EndCapture(handle, true, 0, curr_capture_type, usb_device_desc); +} + +static void is_nitro_emulator_capture_main_loop(CaptureData* capture_data) { + libusb_device_handle *handle = (libusb_device_handle*)capture_data->handle; + const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor; uint16_t last_frame_counter = 0; float single_frame_time = 0; uint16_t curr_frame_counter = 0; CaptureScreensType curr_capture_type = capture_data->status.capture_type; - int ret = StartCapture(handle, last_frame_counter, single_frame_time, curr_capture_type); + int ret = StartEmulatorCapture(handle, last_frame_counter, single_frame_time, curr_capture_type, usb_device_desc); if(ret < 0) { capture_error_print(true, capture_data, "Capture Start: Failed"); return; @@ -409,7 +489,7 @@ void is_nitro_capture_main_loop(CaptureData* capture_data) { frame_wait(single_frame_time, clock_last_reset, curr_frame_counter, last_frame_counter); if(single_frame_time > 0) { - ret = ReadFrame(handle, (uint8_t*)&capture_data->capture_buf[inner_curr_in], _is_nitro_emulator_get_video_in_size(curr_capture_type)); + ret = ReadFrame(handle, (uint8_t*)&capture_data->capture_buf[inner_curr_in], _is_nitro_emulator_get_video_in_size(curr_capture_type), usb_device_desc); if(ret < 0) { capture_error_print(true, capture_data, "Disconnected: Read error"); break; @@ -434,19 +514,28 @@ void is_nitro_capture_main_loop(CaptureData* capture_data) { default_sleep(20); } capture_data->status.curr_delay = last_frame_counter % FRAME_BUFFER_SIZE; - ret = reset_capture_frames(handle, curr_frame_counter, last_frame_counter, single_frame_time, clock_last_reset, curr_capture_type, capture_data->status.capture_type, 1); + ret = reset_capture_frames(handle, curr_frame_counter, last_frame_counter, single_frame_time, clock_last_reset, curr_capture_type, capture_data->status.capture_type, 1, usb_device_desc); if(ret < 0) { capture_error_print(true, capture_data, "Disconnected: Frame counter reset error"); break; } } - EndCapture(handle, true, curr_frame_counter, curr_capture_type); + EndCapture(handle, true, curr_frame_counter, curr_capture_type, usb_device_desc); +} + +void is_nitro_capture_main_loop(CaptureData* capture_data) { + if(!usb_is_initialized()) + return; + if(((const is_nitro_usb_device*)(capture_data->status.device.descriptor))->is_capture) + is_nitro_capture_capture_main_loop(capture_data); + else + is_nitro_emulator_capture_main_loop(capture_data); } void usb_is_nitro_capture_cleanup(CaptureData* capture_data) { if(!usb_is_initialized()) return; - is_nitro_connection_end((libusb_device_handle*)capture_data->handle); + is_nitro_connection_end((libusb_device_handle*)capture_data->handle, (const is_nitro_usb_device*)capture_data->status.device.descriptor); } void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureScreensType capture_type) { diff --git a/source/utils.cpp b/source/utils.cpp index ee8a081..f84d4da 100755 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -40,27 +40,59 @@ bool is_big_endian(void) { return _is_be; } +static uint32_t reverse_endianness(uint32_t value) { + return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); +} + +static uint16_t reverse_endianness(uint16_t value) { + return ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8); +} + uint32_t to_le(uint32_t value) { if(is_big_endian()) - value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); + value = reverse_endianness(value); return value; } uint32_t to_be(uint32_t value) { if(!is_big_endian()) - value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); + value = reverse_endianness(value); return value; } uint16_t to_le(uint16_t value) { if(is_big_endian()) - value = ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8); + value = reverse_endianness(value); return value; } uint16_t to_be(uint16_t value) { if(!is_big_endian()) - value = ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8); + value = reverse_endianness(value); + return value; +} + +uint32_t from_le(uint32_t value) { + if(is_big_endian()) + value = reverse_endianness(value); + return value; +} + +uint32_t from_be(uint32_t value) { + if(!is_big_endian()) + value = reverse_endianness(value); + return value; +} + +uint16_t from_le(uint16_t value) { + if(is_big_endian()) + value = reverse_endianness(value); + return value; +} + +uint16_t from_be(uint16_t value) { + if(!is_big_endian()) + value = reverse_endianness(value); return value; } diff --git a/usb_rules/51-isnitro.rules b/usb_rules/51-isnitro.rules index e7fa7e3..373c56a 100644 --- a/usb_rules/51-isnitro.rules +++ b/usb_rules/51-isnitro.rules @@ -1,3 +1,5 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="0f6e", ATTRS{idProduct}=="0404", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="0f6e", ATTRS{idProduct}=="0403", MODE="0666" +SUBSYSTEM=="usb", ATTRS{idVendor}=="0f6e", ATTRS{idProduct}=="0402", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="0f6e", ATTRS{idProduct}=="0401", MODE="0666" +SUBSYSTEM=="usb", ATTRS{idVendor}=="0f6e", ATTRS{idProduct}=="0400", MODE="0666"