From 5d710d5b704d3e384ca65abc5a752b60d31e216a Mon Sep 17 00:00:00 2001 From: Lorenzooone Date: Fri, 25 Oct 2024 03:47:15 +0200 Subject: [PATCH] Add IS Nitro Driver compatibility --- .gitignore | 2 + CMakeLists.txt | 14 +- .../3dscapture_ftd3.hpp | 0 .../ISNitro/usb_is_nitro_acquisition.hpp} | 10 +- .../usb_is_nitro_acquisition_capture.hpp | 11 + .../usb_is_nitro_acquisition_emulator.hpp | 11 + .../usb_is_nitro_acquisition_general.hpp | 13 + .../ISNitro/usb_is_nitro_communications.hpp | 63 ++ .../ISNitro/usb_is_nitro_is_driver.hpp | 14 + .../ISNitro/usb_is_nitro_libusb.hpp | 14 + .../ISNitro/usb_is_nitro_setup_general.hpp | 12 + .../dscapture_ftd2.hpp | 0 .../usb_ds_3ds_capture.hpp | 0 .../usb_generic.hpp | 0 include/capture_structs.hpp | 6 +- include/usb_is_nitro.hpp | 61 -- .../3dscapture_ftd3.cpp | 0 .../ISNitro/usb_is_nitro_acquisition.cpp | 213 +++++++ .../usb_is_nitro_acquisition_capture.cpp | 109 ++++ .../usb_is_nitro_acquisition_emulator.cpp | 287 +++++++++ .../ISNitro/usb_is_nitro_communications.cpp} | 187 +++--- .../ISNitro/usb_is_nitro_is_driver.cpp | 213 +++++++ .../ISNitro/usb_is_nitro_libusb.cpp | 138 +++++ .../dscapture_ftd2.cpp | 0 .../usb_ds_3ds_capture.cpp | 34 +- .../usb_generic.cpp | 0 source/conversions.cpp | 2 +- source/devicecapture.cpp | 8 +- source/usb_is_nitro_capture.cpp | 567 ------------------ 29 files changed, 1251 insertions(+), 738 deletions(-) rename include/{ => CaptureDeviceSpecific}/3dscapture_ftd3.hpp (100%) mode change 100755 => 100644 rename include/{usb_is_nitro_capture.hpp => CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.hpp} (63%) create mode 100644 include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.hpp create mode 100644 include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.hpp create mode 100644 include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp create mode 100644 include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.hpp create mode 100644 include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_is_driver.hpp create mode 100644 include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.hpp create mode 100644 include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_setup_general.hpp rename include/{ => CaptureDeviceSpecific}/dscapture_ftd2.hpp (100%) mode change 100755 => 100644 rename include/{ => CaptureDeviceSpecific}/usb_ds_3ds_capture.hpp (100%) mode change 100755 => 100644 rename include/{ => CaptureDeviceSpecific}/usb_generic.hpp (100%) delete mode 100644 include/usb_is_nitro.hpp rename source/{ => CaptureDeviceSpecific}/3dscapture_ftd3.cpp (100%) mode change 100755 => 100644 create mode 100644 source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp create mode 100644 source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp create mode 100644 source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp rename source/{usb_is_nitro.cpp => CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.cpp} (53%) create mode 100644 source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_is_driver.cpp create mode 100644 source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.cpp rename source/{ => CaptureDeviceSpecific}/dscapture_ftd2.cpp (100%) mode change 100755 => 100644 rename source/{ => CaptureDeviceSpecific}/usb_ds_3ds_capture.cpp (94%) mode change 100755 => 100644 rename source/{ => CaptureDeviceSpecific}/usb_generic.cpp (100%) delete mode 100644 source/usb_is_nitro_capture.cpp diff --git a/.gitignore b/.gitignore index 9c1fc05..7db0cdd 100755 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ build*/ !build-dockers/ .config/ out/ +.vs/ +CMakeSettings.json \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index a3aa1d1..1291a64 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -269,9 +269,11 @@ endif() set(FETCH_CONTENT_MAKE_AVAILABLE_LIBRARIES "") set(SOURCE_CPP_EXTRA_FILES "") set(EXTRA_DEPENDENCIES "") +set(SOURCE_CPP_DEVICE_FILES_BASE_PATH "source/CaptureDeviceSpecific") +set(SOURCE_CPP_IS_NITRO_FILES_BASE_PATH "${SOURCE_CPP_DEVICE_FILES_BASE_PATH}/ISNitro") if(N3DSXL_LOOPY_SUPPORT) list(APPEND FETCH_CONTENT_MAKE_AVAILABLE_LIBRARIES FTD3XX) - list(APPEND SOURCE_CPP_EXTRA_FILES source/3dscapture_ftd3.cpp) + list(APPEND SOURCE_CPP_EXTRA_FILES ${SOURCE_CPP_DEVICE_FILES_BASE_PATH}/3dscapture_ftd3.cpp) list(APPEND EXTRA_DEPENDENCIES FTD3XX_BUILD_PROJECT) if(MSVC) list(APPEND EXTRA_CXX_FLAGS "/DUSE_FTD3") @@ -281,7 +283,7 @@ if(N3DSXL_LOOPY_SUPPORT) endif() if(NEW_DS_LOOPY_SUPPORT) list(APPEND FETCH_CONTENT_MAKE_AVAILABLE_LIBRARIES FTD2XX) - list(APPEND SOURCE_CPP_EXTRA_FILES source/dscapture_ftd2.cpp) + list(APPEND SOURCE_CPP_EXTRA_FILES ${SOURCE_CPP_DEVICE_FILES_BASE_PATH}/dscapture_ftd2.cpp) list(APPEND EXTRA_DEPENDENCIES FTD2XX_BUILD_PROJECT) if(MSVC) list(APPEND EXTRA_CXX_FLAGS "/DUSE_FTD2") @@ -291,9 +293,9 @@ if(NEW_DS_LOOPY_SUPPORT) endif() if(IS_NITRO_SUPPORT OR OLD_DS_3DS_LOOPY_SUPPORT) list(APPEND FETCH_CONTENT_MAKE_AVAILABLE_LIBRARIES libusb1) - list(APPEND SOURCE_CPP_EXTRA_FILES source/usb_generic.cpp) + list(APPEND SOURCE_CPP_EXTRA_FILES ${SOURCE_CPP_DEVICE_FILES_BASE_PATH}/usb_generic.cpp) if(IS_NITRO_SUPPORT) - list(APPEND SOURCE_CPP_EXTRA_FILES source/usb_is_nitro.cpp source/usb_is_nitro_capture.cpp) + list(APPEND SOURCE_CPP_EXTRA_FILES ${SOURCE_CPP_IS_NITRO_FILES_BASE_PATH}/usb_is_nitro_communications.cpp ${SOURCE_CPP_IS_NITRO_FILES_BASE_PATH}/usb_is_nitro_acquisition.cpp ${SOURCE_CPP_IS_NITRO_FILES_BASE_PATH}/usb_is_nitro_acquisition_capture.cpp ${SOURCE_CPP_IS_NITRO_FILES_BASE_PATH}/usb_is_nitro_acquisition_emulator.cpp ${SOURCE_CPP_IS_NITRO_FILES_BASE_PATH}/usb_is_nitro_is_driver.cpp ${SOURCE_CPP_IS_NITRO_FILES_BASE_PATH}/usb_is_nitro_libusb.cpp) if(MSVC) list(APPEND EXTRA_CXX_FLAGS "/DUSE_IS_NITRO_USB") else() @@ -301,7 +303,7 @@ if(IS_NITRO_SUPPORT OR OLD_DS_3DS_LOOPY_SUPPORT) endif() endif() if(OLD_DS_3DS_LOOPY_SUPPORT) - list(APPEND SOURCE_CPP_EXTRA_FILES source/usb_ds_3ds_capture.cpp) + list(APPEND SOURCE_CPP_EXTRA_FILES ${SOURCE_CPP_DEVICE_FILES_BASE_PATH}/usb_ds_3ds_capture.cpp) if(MSVC) list(APPEND EXTRA_CXX_FLAGS "/DUSE_DS_3DS_USB") else() @@ -406,7 +408,7 @@ endif() if(NEW_DS_LOOPY_SUPPORT) target_link_libraries(${OUTPUT_NAME} PRIVATE ${ftd2xx_BINARY_DIR}/${FTD2XX_SUBFOLDER}/${FTD2XX_LIB}) endif() -target_include_directories(${OUTPUT_NAME} PRIVATE ${EXTRA_INCLUDE_DIRECTORIES} ${TOOLS_DATA_DIR} ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include/Menus) +target_include_directories(${OUTPUT_NAME} PRIVATE ${EXTRA_INCLUDE_DIRECTORIES} ${TOOLS_DATA_DIR} ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include/Menus ${CMAKE_SOURCE_DIR}/include/CaptureDeviceSpecific ${CMAKE_SOURCE_DIR}/include/CaptureDeviceSpecific/ISNitro) target_compile_features(${OUTPUT_NAME} PRIVATE cxx_std_20) target_compile_options(${OUTPUT_NAME} PRIVATE ${EXTRA_CXX_FLAGS}) diff --git a/include/3dscapture_ftd3.hpp b/include/CaptureDeviceSpecific/3dscapture_ftd3.hpp old mode 100755 new mode 100644 similarity index 100% rename from include/3dscapture_ftd3.hpp rename to include/CaptureDeviceSpecific/3dscapture_ftd3.hpp diff --git a/include/usb_is_nitro_capture.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.hpp similarity index 63% rename from include/usb_is_nitro_capture.hpp rename to include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.hpp index af311c9..96632bc 100644 --- a/include/usb_is_nitro_capture.hpp +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.hpp @@ -1,5 +1,5 @@ -#ifndef __USB_IS_NITRO_CAPTURE_HPP -#define __USB_IS_NITRO_CAPTURE_HPP +#ifndef __USB_IS_NITRO_ACQUISITION_HPP +#define __USB_IS_NITRO_ACQUISITION_HPP #include #include "utils.hpp" @@ -9,10 +9,10 @@ void list_devices_is_nitro(std::vector &devices_list); bool is_nitro_connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device); -void is_nitro_capture_main_loop(CaptureData* capture_data); -void usb_is_nitro_capture_cleanup(CaptureData* capture_data); +void is_nitro_acquisition_main_loop(CaptureData* capture_data); +void usb_is_nitro_acquisition_cleanup(CaptureData* capture_data); void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureScreensType capture_type); -uint64_t usb_is_nitro_emulator_get_video_in_size(CaptureData* capture_data); +uint64_t usb_is_nitro_get_video_in_size(CaptureData* capture_data); void usb_is_nitro_init(); void usb_is_nitro_close(); diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.hpp new file mode 100644 index 0000000..226803a --- /dev/null +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.hpp @@ -0,0 +1,11 @@ +#ifndef __USB_IS_NITRO_ACQUISITION_CAPTURE_HPP +#define __USB_IS_NITRO_ACQUISITION_CAPTURE_HPP + +#include "usb_is_nitro_communications.hpp" +#include "capture_structs.hpp" + +int initial_cleanup_capture(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers); +int EndAcquisitionCapture(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc); +void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data); + +#endif diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.hpp new file mode 100644 index 0000000..f503d1c --- /dev/null +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.hpp @@ -0,0 +1,11 @@ +#ifndef __USB_IS_NITRO_ACQUISITION_EMULATOR_HPP +#define __USB_IS_NITRO_ACQUISITION_EMULATOR_HPP + +#include "usb_is_nitro_communications.hpp" +#include "capture_structs.hpp" + +int initial_cleanup_emulator(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers); +int EndAcquisitionEmulator(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); +void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data); + +#endif diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp new file mode 100644 index 0000000..86faaba --- /dev/null +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp @@ -0,0 +1,13 @@ +#ifndef __USB_IS_NITRO_ACQUISITION_GENERAL_HPP +#define __USB_IS_NITRO_ACQUISITION_GENERAL_HPP + +#include "usb_is_nitro_communications.hpp" +#include "capture_structs.hpp" +#include "display_structs.hpp" + +uint64_t _is_nitro_get_video_in_size(CaptureScreensType capture_type); +int set_acquisition_mode(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); +int EndAcquisition(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); +int is_nitro_read_frame_and_output(CaptureData* capture_data, int& inner_curr_in, CaptureScreensType curr_capture_type, std::chrono::time_point &clock_start); + +#endif diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.hpp new file mode 100644 index 0000000..691a0f4 --- /dev/null +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.hpp @@ -0,0 +1,63 @@ +#ifndef __USB_IS_NITRO_COMMUNICATIONS_HPP +#define __USB_IS_NITRO_COMMUNICATIONS_HPP + +#include +#include +#include +#include "utils.hpp" + +#define IS_NITRO_REAL_SERIAL_NUMBER_SIZE 10 + +enum is_nitro_forward_config_values_colors { + 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 = 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, +}; + +struct is_nitro_usb_device { + std::string name; + int vid; + int pid; + int default_config; + int default_interface; + int bulk_timeout; + int ep2_in; + int ep1_out; + int product_id; + int manufacturer_id; + bool is_capture; +}; + +struct is_nitro_device_handlers { + libusb_device_handle* usb_handle; + void* read_handle; + void* write_handle; + void* mutex; +}; + +int GetNumISNitroDesc(void); +const is_nitro_usb_device* GetISNitroDesc(int index); +int DisableLca2(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc); +int StartUsbCaptureDma(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc); +int StopUsbCaptureDma(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc); +int SetForwardFrameCount(is_nitro_device_handlers* handlers, uint16_t count, const is_nitro_usb_device* device_desc); +int SetForwardFramePermanent(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc); +int GetFrameCounter(is_nitro_device_handlers* handlers, uint16_t* out, const is_nitro_usb_device* device_desc); +int GetDeviceSerial(is_nitro_device_handlers* handlers, uint8_t* buf, const is_nitro_usb_device* device_desc); +int UpdateFrameForwardConfig(is_nitro_device_handlers* handlers, 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(is_nitro_device_handlers* handlers, bool enable, bool restart, const is_nitro_usb_device* device_desc); +int ReadLidState(is_nitro_device_handlers* handlers, uint32_t* out, const is_nitro_usb_device* device_desc); +int ReadFrame(is_nitro_device_handlers* handlers, uint8_t* buf, int length, const is_nitro_usb_device* device_desc); + +#endif diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_is_driver.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_is_driver.hpp new file mode 100644 index 0000000..e16dcfe --- /dev/null +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_is_driver.hpp @@ -0,0 +1,14 @@ +#ifndef __USB_IS_NITRO_IS_DRIVER_HPP +#define __USB_IS_NITRO_IS_DRIVER_HPP + +#include +#include "capture_structs.hpp" +#include "usb_is_nitro_communications.hpp" + +void is_driver_list_devices(std::vector& devices_list, bool* not_supported_elems, int* curr_serial_extra_id_is_nitro, const size_t num_is_nitro_desc); +is_nitro_device_handlers* is_driver_serial_reconnection(const is_nitro_usb_device* usb_device_desc, CaptureDevice* device); +void is_driver_end_connection(is_nitro_device_handlers* handlers); +int is_driver_bulk_out(is_nitro_device_handlers* handlers, uint8_t* buf, int length, int* transferred); +int is_driver_bulk_in(is_nitro_device_handlers* handlers, uint8_t* buf, int length, int* transferred); + +#endif \ No newline at end of file diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.hpp new file mode 100644 index 0000000..ae8b16f --- /dev/null +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.hpp @@ -0,0 +1,14 @@ +#ifndef __USB_IS_NITRO_LIBUSB_HPP +#define __USB_IS_NITRO_LIBUSB_HPP + +#include +#include "capture_structs.hpp" +#include "usb_is_nitro_communications.hpp" + +void is_nitro_libusb_list_devices(std::vector& devices_list, bool* no_access_elems, bool* not_supported_elems, int* curr_serial_extra_id_is_nitro, const size_t num_is_nitro_desc); +is_nitro_device_handlers* is_nitro_libusb_serial_reconnection(const is_nitro_usb_device* usb_device_desc, CaptureDevice* device, int& curr_serial_extra_id); +void is_nitro_libusb_end_connection(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc, bool interface_claimed); +int is_nitro_libusb_bulk_out(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred); +int is_nitro_libusb_bulk_in(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred); + +#endif \ No newline at end of file diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_setup_general.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_setup_general.hpp new file mode 100644 index 0000000..adba4c4 --- /dev/null +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_setup_general.hpp @@ -0,0 +1,12 @@ +#ifndef __USB_IS_NITRO_SETUP_GENERAL_HPP +#define __USB_IS_NITRO_SETUP_GENERAL_HPP + +#include +#include "capture_structs.hpp" +#include "usb_is_nitro_communications.hpp" + +std::string get_serial(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers, int& curr_serial_extra_id); +void is_nitro_insert_device(std::vector& devices_list, is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, int& curr_serial_extra_id_is_nitro, std::string path); +void is_nitro_insert_device(std::vector& devices_list, is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, int& curr_serial_extra_id_is_nitro); + +#endif \ No newline at end of file diff --git a/include/dscapture_ftd2.hpp b/include/CaptureDeviceSpecific/dscapture_ftd2.hpp old mode 100755 new mode 100644 similarity index 100% rename from include/dscapture_ftd2.hpp rename to include/CaptureDeviceSpecific/dscapture_ftd2.hpp diff --git a/include/usb_ds_3ds_capture.hpp b/include/CaptureDeviceSpecific/usb_ds_3ds_capture.hpp old mode 100755 new mode 100644 similarity index 100% rename from include/usb_ds_3ds_capture.hpp rename to include/CaptureDeviceSpecific/usb_ds_3ds_capture.hpp diff --git a/include/usb_generic.hpp b/include/CaptureDeviceSpecific/usb_generic.hpp similarity index 100% rename from include/usb_generic.hpp rename to include/CaptureDeviceSpecific/usb_generic.hpp diff --git a/include/capture_structs.hpp b/include/capture_structs.hpp index 16a253f..c04202f 100755 --- a/include/capture_structs.hpp +++ b/include/capture_structs.hpp @@ -102,11 +102,13 @@ union CaptureReceived { }; struct CaptureDevice { - 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) {} + 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), path("") {} + CaptureDevice(std::string serial_number, std::string name, std::string path, 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), path(path) {} + 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), path("") {} std::string serial_number; std::string name; + std::string path; CaptureConnectionType cc_type; const void* descriptor; bool is_3ds; diff --git a/include/usb_is_nitro.hpp b/include/usb_is_nitro.hpp deleted file mode 100644 index 4999c52..0000000 --- a/include/usb_is_nitro.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef __USB_IS_NITRO_HPP -#define __USB_IS_NITRO_HPP - -#include -#include -#include "utils.hpp" - -#define IS_NITRO_REAL_SERIAL_NUMBER_SIZE 10 - -enum is_nitro_forward_config_values_colors { - 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 = 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; - int default_interface; - int bulk_timeout; - int ep2_in; - int ep1_out; - int product_id; - int manufacturer_id; - bool is_capture; -}; - -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/source/3dscapture_ftd3.cpp b/source/CaptureDeviceSpecific/3dscapture_ftd3.cpp old mode 100755 new mode 100644 similarity index 100% rename from source/3dscapture_ftd3.cpp rename to source/CaptureDeviceSpecific/3dscapture_ftd3.cpp diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp new file mode 100644 index 0000000..513226f --- /dev/null +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp @@ -0,0 +1,213 @@ +#include "devicecapture.hpp" +#include "usb_is_nitro_communications.hpp" +#include "usb_is_nitro_acquisition.hpp" +#include "usb_is_nitro_acquisition_general.hpp" +#include "usb_is_nitro_acquisition_capture.hpp" +#include "usb_is_nitro_acquisition_emulator.hpp" +#include "usb_is_nitro_setup_general.hpp" +#include "usb_is_nitro_libusb.hpp" +#include "usb_is_nitro_is_driver.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_NITRO_REAL_SERIAL_NUMBER_SIZE + 1) + +#define FRAME_BUFFER_SIZE 32 + +static bool initial_cleanup(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers) { + if(!usb_device_desc->is_capture) + return initial_cleanup_emulator(usb_device_desc, handlers) != LIBUSB_SUCCESS; + return initial_cleanup_capture(usb_device_desc, handlers) != LIBUSB_SUCCESS; +} + +std::string get_serial(const is_nitro_usb_device* usb_device_desc, is_nitro_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_NITRO_REAL_SERIAL_NUMBER_SIZE] = '\0'; + serial_str = std::string((const char*)data); + } + else + curr_serial_extra_id += 1; + return serial_str; +} + +void is_nitro_insert_device(std::vector& devices_list, is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, int& curr_serial_extra_id_is_nitro, std::string path) { + devices_list.emplace_back(get_serial(usb_device_desc, handlers, curr_serial_extra_id_is_nitro), usb_device_desc->name, path, CAPTURE_CONN_IS_NITRO, (void*)usb_device_desc, false, false, false, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, 0, 0, 0, 0, 0, HEIGHT_DS); +} + +void is_nitro_insert_device(std::vector& devices_list, is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, int& curr_serial_extra_id_is_nitro) { + devices_list.emplace_back(get_serial(usb_device_desc, handlers, curr_serial_extra_id_is_nitro), 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); +} + +static is_nitro_device_handlers* usb_find_by_serial_number(const is_nitro_usb_device* usb_device_desc, CaptureDevice* device) { + is_nitro_device_handlers* final_handlers = NULL; + int curr_serial_extra_id = 0; + final_handlers = is_nitro_libusb_serial_reconnection(usb_device_desc, device, curr_serial_extra_id); + + if (final_handlers == NULL) + final_handlers = is_driver_serial_reconnection(usb_device_desc, device); + return final_handlers; +} + +void list_devices_is_nitro(std::vector &devices_list) { + const size_t num_is_nitro_desc = GetNumISNitroDesc(); + int* curr_serial_extra_id_is_nitro = new int[num_is_nitro_desc]; + bool* no_access_elems = new bool[num_is_nitro_desc]; + bool* not_supported_elems = new bool[num_is_nitro_desc]; + for (int i = 0; i < num_is_nitro_desc; i++) { + no_access_elems[i] = false; + not_supported_elems[i] = false; + curr_serial_extra_id_is_nitro[i] = 0; + } + is_nitro_libusb_list_devices(devices_list, no_access_elems, not_supported_elems, curr_serial_extra_id_is_nitro, num_is_nitro_desc); + + bool any_not_supported = false; + for (int i = 0; i < num_is_nitro_desc; i++) + any_not_supported |= not_supported_elems[i]; + if (any_not_supported) + is_driver_list_devices(devices_list, not_supported_elems, curr_serial_extra_id_is_nitro, num_is_nitro_desc); + + delete[] curr_serial_extra_id_is_nitro; + delete[] no_access_elems; + delete[] not_supported_elems; +} + +static void is_nitro_connection_end(is_nitro_device_handlers* handlers, const is_nitro_usb_device *device_desc, bool interface_claimed = true) { + if (handlers == NULL) + return; + if (handlers->usb_handle) + is_nitro_libusb_end_connection(handlers, device_desc, interface_claimed); + else + is_driver_end_connection(handlers); + delete handlers; +} + +bool is_nitro_connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device) { + const is_nitro_usb_device* usb_device_info = (const is_nitro_usb_device*)device->descriptor; + is_nitro_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 _is_nitro_get_video_in_size(CaptureScreensType capture_type) { + if((capture_type == CAPTURE_SCREENS_TOP) || (capture_type == CAPTURE_SCREENS_BOTTOM)) + return sizeof(ISNitroEmulatorVideoInputData) / 2; + return sizeof(ISNitroEmulatorVideoInputData); +} + +uint64_t usb_is_nitro_get_video_in_size(CaptureData* capture_data) { + return _is_nitro_get_video_in_size(capture_data->status.capture_type); +} + +int set_acquisition_mode(is_nitro_device_handlers* handlers, 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(handlers, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24, capture_mode_flag, IS_NITRO_FORWARD_CONFIG_RATE_FULL, usb_device_desc); +} + +int EndAcquisition(is_nitro_device_handlers* handlers, 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 EndAcquisitionCapture(handlers, usb_device_desc); + return EndAcquisitionEmulator(handlers, do_drain_frames, start_frames, capture_type, usb_device_desc); +} + +int is_nitro_read_frame_and_output(CaptureData* capture_data, int &inner_curr_in, CaptureScreensType curr_capture_type, std::chrono::time_point &clock_start) { + int ret = ReadFrame((is_nitro_device_handlers*)capture_data->handle, (uint8_t*)&capture_data->capture_buf[inner_curr_in], _is_nitro_get_video_in_size(curr_capture_type), (const is_nitro_usb_device*)capture_data->status.device.descriptor); + if (ret < 0) + return ret; + // 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_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(); + return ret; +} + +void is_nitro_acquisition_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_acquisition_capture_main_loop(capture_data); + else + is_nitro_acquisition_emulator_main_loop(capture_data); +} + +void usb_is_nitro_acquisition_cleanup(CaptureData* capture_data) { + if(!usb_is_initialized()) + return; + is_nitro_connection_end((is_nitro_device_handlers*)capture_data->handle, (const is_nitro_usb_device*)capture_data->status.device.descriptor); + capture_data->handle = NULL; +} + +void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureScreensType capture_type) { + if(!usb_is_initialized()) + return; + int num_pixels = _is_nitro_get_video_in_size(capture_type) / 3; + int out_start_pos = 0; + int out_clear_pos = num_pixels; + if(capture_type == CAPTURE_SCREENS_BOTTOM) { + out_start_pos = num_pixels; + out_clear_pos = 0; + } + if((capture_type == CAPTURE_SCREENS_BOTTOM) || (capture_type == CAPTURE_SCREENS_TOP)) + memset(p_out->screen_data[out_clear_pos], 0, num_pixels * 3); + for(int i = 0; i < num_pixels; i++) { + p_out->screen_data[i + out_start_pos][0] = p_in->is_nitro_capture_received.video_in.screen_data[i][2]; + p_out->screen_data[i + out_start_pos][1] = p_in->is_nitro_capture_received.video_in.screen_data[i][1]; + p_out->screen_data[i + out_start_pos][2] = p_in->is_nitro_capture_received.video_in.screen_data[i][0]; + } +} + +void usb_is_nitro_init() { + return usb_init(); +} + +void usb_is_nitro_close() { + usb_close(); +} + diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp new file mode 100644 index 0000000..58f2e5c --- /dev/null +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp @@ -0,0 +1,109 @@ +#include "devicecapture.hpp" +#include "usb_is_nitro_communications.hpp" +#include "usb_is_nitro_acquisition_general.hpp" +#include "usb_is_nitro_acquisition_capture.hpp" + +// Code created by analyzing the USB packets sent and received by the IS Nitro Capture device. + +// The code for video capture of the IS Nitro Emulator and of the IS Nitro Capture is wildly different. +// For this reason, the code was split into two device-specific files. + +#define CAPTURE_SKIP_LID_REOPEN_FRAMES 32 + +static int StartAcquisitionCapture(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, uint32_t* lid_state, const is_nitro_usb_device* usb_device_desc) { + int ret = 0; + ret = ReadLidState(handlers, lid_state, usb_device_desc); + if(ret < 0) + return ret; + if((*lid_state) & 1) + return LIBUSB_SUCCESS; + ret = set_acquisition_mode(handlers, capture_type, usb_device_desc); + if(ret < 0) + return ret; + ret = SetForwardFramePermanent(handlers, usb_device_desc); + if(ret < 0) + return ret; + ret = UpdateFrameForwardEnable(handlers, true, true, usb_device_desc); + if(ret < 0) + return ret; + return StartUsbCaptureDma(handlers, usb_device_desc); +} + +static int LidReopenCaptureCheck(CaptureData* capture_data, CaptureScreensType &curr_capture_type, uint32_t* lid_state) { + is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle; + const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor; + curr_capture_type = capture_data->status.capture_type; + int ret = StartAcquisitionCapture(handlers, curr_capture_type, lid_state, usb_device_desc); + if(ret < 0) + return ret; + if(!((*lid_state) & 1)) + capture_data->status.cooldown_curr_in = CAPTURE_SKIP_LID_REOPEN_FRAMES; + return ret; +} + +int initial_cleanup_capture(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers) { + //EndAcquisition(handlers, false, 0, CAPTURE_SCREENS_BOTH, usb_device_desc); + return LIBUSB_SUCCESS; +} + +int EndAcquisitionCapture(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc) { + return StopUsbCaptureDma(handlers, usb_device_desc); +} + +void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data) { + is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle; + const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor; + uint32_t lid_state = 0; + CaptureScreensType curr_capture_type = capture_data->status.capture_type; + int ret = StartAcquisitionCapture(handlers, curr_capture_type, &lid_state, 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) { + if(lid_state & 1) { + default_sleep(20); + ret = LidReopenCaptureCheck(capture_data, curr_capture_type, &lid_state); + if(ret < 0) { + capture_error_print(true, capture_data, "Lid Reopen: Failed"); + return; + } + } + if(lid_state & 1) + continue; + ret = is_nitro_read_frame_and_output(capture_data, inner_curr_in, curr_capture_type, clock_start); + if(ret < 0) { + ret = EndAcquisition(handlers, true, 0, curr_capture_type, usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Disconnected: Read error"); + return; + } + ret = LidReopenCaptureCheck(capture_data, curr_capture_type, &lid_state); + if(ret < 0) { + capture_error_print(true, capture_data, "Lid Reopen: Failed"); + return; + } + continue; + } + + if(curr_capture_type != capture_data->status.capture_type) { + ret = EndAcquisition(handlers, 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 = StartAcquisitionCapture(handlers, curr_capture_type, &lid_state, usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Capture Restart: Failed"); + return; + } + } + } + if(!(lid_state & 1)) + EndAcquisition(handlers, true, 0, curr_capture_type, usb_device_desc); +} diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp new file mode 100644 index 0000000..9a43f81 --- /dev/null +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp @@ -0,0 +1,287 @@ +#include "devicecapture.hpp" +#include "usb_is_nitro_communications.hpp" +#include "usb_is_nitro_acquisition_general.hpp" +#include "usb_is_nitro_acquisition_emulator.hpp" + +#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. + +// The code for video capture of the IS Nitro Emulator and of the IS Nitro Capture is wildly different. +// For this reason, the code was split into two device-specific files. + +#define FRAME_BUFFER_SIZE 32 + +static int drain_frames(is_nitro_device_handlers* handlers, int num_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) { + ISNitroEmulatorVideoInputData* video_in_buffer = new ISNitroEmulatorVideoInputData; + for (int i = start_frames; i < num_frames; i++) { + int ret = ReadFrame(handlers, (uint8_t*)video_in_buffer, _is_nitro_get_video_in_size(capture_type), usb_device_desc); + if(ret < 0) { + delete video_in_buffer; + return ret; + } + } + delete video_in_buffer; + return LIBUSB_SUCCESS; +} + +static int StartAcquisitionEmulator(is_nitro_device_handlers* handlers, 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(handlers, usb_device_desc); + if(ret < 0) + return ret; + ret = set_acquisition_mode(handlers, capture_type, usb_device_desc); + if(ret < 0) + return ret; + ret = SetForwardFrameCount(handlers, 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(handlers, true, true, usb_device_desc); + if(ret < 0) + return ret; + ret = UpdateFrameForwardEnable(handlers, true, false, usb_device_desc); + if(ret < 0) + return ret; + + // Get to the closest next frame + auto clock_start = std::chrono::high_resolution_clock::now(); + uint16_t oldFrameCount; + uint16_t newFrameCount; + ret = GetFrameCounter(handlers, &oldFrameCount, usb_device_desc); + if(ret < 0) + return ret; + newFrameCount = oldFrameCount; + while(newFrameCount == oldFrameCount) { + ret = GetFrameCounter(handlers, &newFrameCount, usb_device_desc); + if(ret < 0) + return ret; + const auto curr_time = std::chrono::high_resolution_clock::now(); + const std::chrono::duration diff = curr_time - clock_start; + // If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up + if(diff.count() > 0.2) + break; + } + + // Get to the next modulo 32 frame. + // 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(handlers, &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(handlers, &oldFrameCount, usb_device_desc); + if(ret < 0) + return ret; + // Placing a sleep of some kind here would be much better... + // Though this is only executed for a small time when first connecting... + const auto curr_time = std::chrono::high_resolution_clock::now(); + const std::chrono::duration diff = curr_time - clock_start; + // If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up + if(diff.count() > 1.0) + break; + } + const auto curr_time = std::chrono::high_resolution_clock::now(); + const std::chrono::duration diff = curr_time - clock_start; + // Sometimes the upper 8 bits aren't updated... Use only the lower 8 bits. + newFrameCount &= 0xFF; + oldFrameCount &= 0xFF; + int frame_diff = ((int)oldFrameCount) - ((int)newFrameCount); + if(frame_diff < 0) + frame_diff += 1 << 8; + out_frame_count = oldFrameCount; + // Determine how much time a single frame takes. We'll use it for sleeps + if(frame_diff == 0) + single_frame_time = 0; + else + single_frame_time = diff.count() / frame_diff; + + // Start the actual DMA + if(single_frame_time > 0) { + ret = StartUsbCaptureDma(handlers, usb_device_desc); + if(ret < 0) + return ret; + } + return ret; +} + +static bool do_sleep(float single_frame_time, std::chrono::time_point clock_last_reset, int curr_frame_counter, int last_frame_counter, float* out_time) { + auto curr_time = std::chrono::high_resolution_clock::now(); + std::chrono::duration diff = curr_time - clock_last_reset; + float expected_time = single_frame_time * curr_frame_counter; + float low_single_frame_time = 1.0 / ((int)((1.0 / single_frame_time) + 1)); + float low_expected_time = low_single_frame_time * curr_frame_counter; + // If the current time is too low, sleep a bit to make sure we don't overrun the framerate counter + // Don't do it regardless of the situation, and only in small increments... + // Otherwise there is the risk of sleeping too much + bool result = (diff.count() < expected_time) && ((expected_time - diff.count()) > (single_frame_time / 4)) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1))); + *out_time = (expected_time - diff.count()) / 4; + if(last_frame_counter & (FRAME_BUFFER_SIZE - 1)) { + result = (diff.count() < low_expected_time) && ((low_expected_time - diff.count()) > (low_single_frame_time / 4)); + *out_time = (low_expected_time - diff.count()) / 4; + } + return result; +} + +static void frame_wait(float single_frame_time, std::chrono::time_point clock_last_reset, int curr_frame_counter, int last_frame_counter) { + if(curr_frame_counter == 0) + return; + float sleep_time = 0; + while(do_sleep(single_frame_time, clock_last_reset, curr_frame_counter, last_frame_counter, &sleep_time)) + default_sleep(sleep_time); +} + +static int reset_acquisition_frames(is_nitro_device_handlers* handlers, 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(handlers, usb_device_desc); + if(ret < 0) + return ret; + + // If the user requests a mode change, accomodate them. + // Though it may lag for a bit... + if(wanted_capture_type != curr_capture_type) { + curr_capture_type = wanted_capture_type; + ret = set_acquisition_mode(handlers, curr_capture_type, usb_device_desc); + if(ret < 0) + return ret; + } + + uint16_t internalFrameCount = 0; + uint16_t full_internalFrameCount = 0; + int frame_diff = 0; + int diff_target = FRAME_BUFFER_SIZE * multiplier; + do { + // Check how many frames have passed... + ret = GetFrameCounter(handlers, &internalFrameCount, usb_device_desc); + full_internalFrameCount = internalFrameCount; + // Sometimes the upper 8 bits aren't updated... Use only the lower 8 bits. + internalFrameCount &= 0xFF; + if(ret < 0) + return ret; + frame_diff = internalFrameCount - last_frame_counter; + if(frame_diff < 0) + frame_diff += 1 << 8; + // If the frames haven't advanced, the DS is either turned off or sleeping. If so, avoid locking up + if(frame_diff == 0) + break; + const auto curr_time = std::chrono::high_resolution_clock::now(); + const std::chrono::duration diff = curr_time - clock_last_reset; + // If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up + if(diff.count() > (1.0 * multiplier)) { + frame_diff = 0; + break; + } + // Exit if enough frames have passed, or if there currently is some delay. + // Exiting early makes it possible to catch up to the DMA, if we're behind. + } while((frame_diff < diff_target) && ((!(last_frame_counter & (FRAME_BUFFER_SIZE - 1))) || ((internalFrameCount & (FRAME_BUFFER_SIZE - 1)) >= (FRAME_BUFFER_SIZE - 2)))); + // Determine how much time a single frame takes. We'll use it for sleeps + const auto curr_time = std::chrono::high_resolution_clock::now(); + const std::chrono::duration diff = curr_time - clock_last_reset; + if(frame_diff == 0) + single_frame_time = 0; + else + single_frame_time = diff.count() / (frame_diff / ((float)multiplier)); + clock_last_reset = curr_time; + + // Save the current frame counter's 8 LSB + last_frame_counter = internalFrameCount; + + // 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(handlers, true, true, usb_device_desc); + if(ret < 0) + return ret; + clock_last_reset = std::chrono::high_resolution_clock::now(); + } + ret = UpdateFrameForwardEnable(handlers, 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(handlers, usb_device_desc); + if(ret < 0) + return ret; + } + } + return LIBUSB_SUCCESS; +} + +int initial_cleanup_emulator(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers) { + return EndAcquisition(handlers, false, 0, CAPTURE_SCREENS_BOTH, usb_device_desc); +} + +int EndAcquisitionEmulator(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) { + int ret = 0; + if (do_drain_frames) + ret = drain_frames(handlers, FRAME_BUFFER_SIZE, start_frames, capture_type, usb_device_desc); + if (ret < 0) + return ret; + ret = StopUsbCaptureDma(handlers, usb_device_desc); + if (ret < 0) + return ret; + return UpdateFrameForwardEnable(handlers, false, false, usb_device_desc); +} + +void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data) { + is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)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 = StartAcquisitionEmulator(handlers, 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; + } + 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) { + frame_wait(single_frame_time, clock_last_reset, curr_frame_counter, last_frame_counter); + + if (single_frame_time > 0) { + ret = is_nitro_read_frame_and_output(capture_data, inner_curr_in, curr_capture_type, clock_start); + if (ret < 0) { + capture_error_print(true, capture_data, "Disconnected: Read error"); + return; + } + } + else { + capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM; + default_sleep(20); + } + capture_data->status.curr_delay = last_frame_counter % FRAME_BUFFER_SIZE; + ret = reset_acquisition_frames(handlers, 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; + } + } + EndAcquisition(handlers, true, curr_frame_counter, curr_capture_type, usb_device_desc); +} diff --git a/source/usb_is_nitro.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.cpp similarity index 53% rename from source/usb_is_nitro.cpp rename to source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.cpp index 8b5b60b..1b3b247 100644 --- a/source/usb_is_nitro.cpp +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_communications.cpp @@ -1,5 +1,7 @@ #include "frontend.hpp" -#include "usb_is_nitro.hpp" +#include "usb_is_nitro_communications.hpp" +#include "usb_is_nitro_libusb.hpp" +#include "usb_is_nitro_is_driver.hpp" #include #include @@ -66,6 +68,7 @@ enum is_nitro_capture_command { 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_GET_LID_STATE = 0x19, IS_NITRO_CAP_CMD_SET_FWD_RESTART = 0x1C, }; @@ -96,7 +99,7 @@ struct PACKED is_nitro_packet_header { }; #pragma pack(pop) -const is_nitro_usb_device usb_is_nitro_emu_rare_desc = { +static 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, @@ -105,7 +108,7 @@ const is_nitro_usb_device usb_is_nitro_emu_rare_desc = { .product_id = 2, .manufacturer_id = 1, .is_capture = false }; -const is_nitro_usb_device usb_is_nitro_emu_common_desc = { +static 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, @@ -114,7 +117,7 @@ const is_nitro_usb_device usb_is_nitro_emu_common_desc = { .product_id = 2, .manufacturer_id = 1, .is_capture = false }; -const is_nitro_usb_device usb_is_nitro_cap_desc = { +static const is_nitro_usb_device usb_is_nitro_cap_desc = { .name = "IS Nitro Capture", .vid = 0x0f6e, .pid = 0x0403, .default_config = 1, .default_interface = 0, @@ -123,17 +126,20 @@ const is_nitro_usb_device usb_is_nitro_cap_desc = { .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 const is_nitro_usb_device* all_usb_is_nitro_devices_desc[] = { + &usb_is_nitro_emu_rare_desc, + &usb_is_nitro_emu_common_desc, + &usb_is_nitro_cap_desc, +}; + +int GetNumISNitroDesc() { + return sizeof(all_usb_is_nitro_devices_desc) / sizeof(all_usb_is_nitro_devices_desc[0]); +} + +const is_nitro_usb_device* GetISNitroDesc(int index) { + if((index < 0) || (index >= GetNumISNitroDesc())) + index = 0; + return all_usb_is_nitro_devices_desc[index]; } static void fix_endianness_header(is_nitro_packet_header* header) { @@ -148,16 +154,20 @@ static void fix_endianness_header(is_nitro_nec_packet_header* header) { } // Write to bulk endpoint. Returns libusb error code -static int bulk_out(libusb_device_handle *handle, const is_nitro_usb_device* usb_device_desc, uint8_t *buf, int length, int *transferred) { - return libusb_bulk_transfer(handle, usb_device_desc->ep1_out, buf, length, transferred, usb_device_desc->bulk_timeout); +static int bulk_out(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t *buf, int length, int *transferred) { + if(handlers->usb_handle) + return is_nitro_libusb_bulk_out(handlers, usb_device_desc, buf, length, transferred); + return is_driver_bulk_out(handlers, buf, length, transferred); } // Read from bulk endpoint. Returns libusb error code -static int bulk_in(libusb_device_handle *handle, const is_nitro_usb_device* usb_device_desc, uint8_t *buf, int length, int *transferred) { - return libusb_bulk_transfer(handle, usb_device_desc->ep2_in, buf, length, transferred, usb_device_desc->bulk_timeout); +static int bulk_in(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t *buf, int length, int *transferred) { + if(handlers->usb_handle) + return is_nitro_libusb_bulk_in(handlers, usb_device_desc, buf, length, transferred); + return is_driver_bulk_in(handlers, buf, length, transferred); } -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) { +static int SendWritePacket(is_nitro_device_handlers* handlers, 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) @@ -192,17 +202,17 @@ static int SendWritePacket(libusb_device_handle *handle, uint16_t command, is_ni 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); + ret = bulk_out(handlers, 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); + ret = bulk_out(handlers, 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); + ret = bulk_out(handlers, device_desc, &buf[(i * single_packet_covered_size)], transfer_size, &num_bytes); num_bytes += header_bytes; } if(ret < 0) @@ -213,7 +223,7 @@ static int SendWritePacket(libusb_device_handle *handle, uint16_t command, is_ni 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); + int ret = bulk_in(handlers, device_desc, status, sizeof(status), &status_bytes); if(ret < 0) return ret; if(status_bytes != sizeof(status)) @@ -224,7 +234,7 @@ static int SendWritePacket(libusb_device_handle *handle, uint16_t command, is_ni 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, const is_nitro_usb_device* device_desc, bool expect_result = true) { +static int SendReadPacket(is_nitro_device_handlers* handlers, 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; @@ -247,13 +257,13 @@ static int SendReadPacket(libusb_device_handle *handle, uint16_t command, is_nit header.padding = 0; fix_endianness_header(&header); int num_bytes = 0; - int ret = bulk_out(handle, device_desc, (uint8_t*)&header, sizeof(is_nitro_packet_header), &num_bytes); + int ret = bulk_out(handlers, 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; if(buf != NULL) { - ret = bulk_in(handle, device_desc, buf + (i * single_packet_covered_size), transfer_size, &num_bytes); + ret = bulk_in(handlers, device_desc, buf + (i * single_packet_covered_size), transfer_size, &num_bytes); if(ret < 0) return ret; if(num_bytes != transfer_size) @@ -263,7 +273,7 @@ static int SendReadPacket(libusb_device_handle *handle, uint16_t command, is_nit 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); + int ret = bulk_in(handlers, device_desc, status, sizeof(status), &status_bytes); if(ret < 0) return ret; if(status_bytes != sizeof(status)) @@ -274,66 +284,66 @@ static int SendReadPacket(libusb_device_handle *handle, uint16_t command, is_nit return LIBUSB_SUCCESS; } -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 SendReadCommand(is_nitro_device_handlers* handlers, uint16_t command, uint8_t* buf, int length, const is_nitro_usb_device* device_desc) { + return SendReadPacket(handlers, 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, const is_nitro_usb_device* device_desc) { - return SendWritePacket(handle, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length, device_desc); +int SendWriteCommand(is_nitro_device_handlers* handlers, uint16_t command, uint8_t* buf, int length, const is_nitro_usb_device* device_desc) { + return SendWritePacket(handlers, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length, device_desc); } -int SendReadCommandU32(libusb_device_handle *handle, uint16_t command, uint32_t* out, const is_nitro_usb_device* device_desc) { +int SendReadCommandU32(is_nitro_device_handlers* handlers, 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); + int ret = SendReadCommand(handlers, command, (uint8_t*)&buffer, sizeof(uint32_t), device_desc); if(ret < 0) return ret; *out = from_le(buffer); return 0; } -int SendWriteCommandU32(libusb_device_handle *handle, uint16_t command, uint32_t value, const is_nitro_usb_device* device_desc) { +int SendWriteCommandU32(is_nitro_device_handlers* handlers, 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); + return SendWriteCommand(handlers, 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) { +int GetDeviceSerial(is_nitro_device_handlers* handlers, 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); + return SendReadCommand(handlers, IS_NITRO_CAP_CMD_GET_SERIAL, buf, IS_NITRO_REAL_SERIAL_NUMBER_SIZE, device_desc); + return SendReadCommand(handlers, 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) { +int ReadNecMem(is_nitro_device_handlers* handlers, 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_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), device_desc); + int ret = SendWriteCommand(handlers, header.command, (uint8_t*)&header, sizeof(is_nitro_nec_packet_header), device_desc); if(ret < 0) return ret; - return SendReadCommand(handle, IS_NITRO_EMU_CMD_READ_NEC_MEM, buf, count * unit_size, device_desc); + return SendReadCommand(handlers, 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, const is_nitro_usb_device* device_desc) { +int ReadNecMemU16(is_nitro_device_handlers* handlers, 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); + int ret = ReadNecMem(handlers, address, 2, (uint8_t*)&buffer, 1, device_desc); if(ret < 0) return ret; *out = from_le(buffer); return 0; } -int ReadNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t* out, const is_nitro_usb_device* device_desc) { +int ReadNecMemU32(is_nitro_device_handlers* handlers, 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); + int ret = ReadNecMem(handlers, address, 2, (uint8_t*)&buffer, 2, device_desc); if(ret < 0) return ret; *out = from_le(buffer); return 0; } -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) { +int WriteNecMem(is_nitro_device_handlers* handlers, 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_EMU_CMD_WRITE_NEC_MEM; @@ -345,106 +355,121 @@ 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), device_desc); + int ret = SendWriteCommand(handlers, 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, const is_nitro_usb_device* device_desc) { +int WriteNecMemU16(is_nitro_device_handlers* handlers, 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); + return WriteNecMem(handlers, address, 2, (uint8_t*)&buffer, 1, device_desc); } -int WriteNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t value, const is_nitro_usb_device* device_desc) { +int WriteNecMemU32(is_nitro_device_handlers* handlers, 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); + return WriteNecMem(handlers, address, 2, (uint8_t*)&buffer, 2, device_desc); } -int DisableLca2(libusb_device_handle *handle, const is_nitro_usb_device* device_desc) { +int DisableLca2(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc) { if(device_desc->is_capture) return LIBUSB_SUCCESS; - int ret = WriteNecMemU16(handle, 0x00805180, 0, device_desc); + int ret = WriteNecMemU16(handlers, 0x00805180, 0, device_desc); if(ret < 0) return ret; - ret = WriteNecMemU16(handle, 0x0F84000A, 1, device_desc); + ret = WriteNecMemU16(handlers, 0x0F84000A, 1, device_desc); if(ret < 0) return ret; //default_sleep(2000); - return WriteNecMemU16(handle, 0x0F84000A, 0, device_desc); + return WriteNecMemU16(handlers, 0x0F84000A, 0, device_desc); } -int StartUsbCaptureDma(libusb_device_handle *handle, const is_nitro_usb_device* device_desc) { +int StartUsbCaptureDma(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc) { if(!device_desc->is_capture) { - int ret = WriteNecMemU16(handle, REG_USB_DMA_CONTROL_2, 2, device_desc); + int ret = WriteNecMemU16(handlers, 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 WriteNecMemU16(handlers, 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); + return SendReadPacket(handlers, 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, const is_nitro_usb_device* device_desc) { +int StopUsbCaptureDma(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc) { if(!device_desc->is_capture) { - int ret = WriteNecMemU16(handle, REG_USB_DMA_CONTROL_2, 0, device_desc); + int ret = WriteNecMemU16(handlers, 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 WriteNecMemU16(handlers, 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); + return SendReadPacket(handlers, 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, const is_nitro_usb_device* device_desc) { +int SetForwardFrameCount(is_nitro_device_handlers* handlers, uint16_t count, const is_nitro_usb_device* device_desc) { if(!count) return LIBUSB_ERROR_INTERRUPTED; count -= 1; 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); + return WriteNecMemU32(handlers, 0x0800000C, (count >> 8) | ((count & 0xFF) << 16), device_desc); + return SendWriteCommandU32(handlers, IS_NITRO_CAP_CMD_SET_FWD_FRAMES, count, device_desc); } -int SetForwardFramePermanent(libusb_device_handle *handle, const is_nitro_usb_device* device_desc) { - return SetForwardFrameCount(handle, 0x4001, device_desc); +int SetForwardFramePermanent(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc) { + return SetForwardFrameCount(handlers, 0x4001, device_desc); } -int GetFrameCounter(libusb_device_handle *handle, uint16_t* out, const is_nitro_usb_device* device_desc) { +int GetFrameCounter(is_nitro_device_handlers* handlers, uint16_t* out, const is_nitro_usb_device* device_desc) { + if(device_desc->is_capture) { + *out = 0; + return LIBUSB_SUCCESS; + } uint32_t counter = 0; - int ret = ReadNecMemU32(handle, 0x08000028, &counter, device_desc); + int ret = ReadNecMemU32(handlers, 0x08000028, &counter, device_desc); if(ret < 0) return ret; *out = (counter & 0xFF) | ((counter & 0xFF0000) >> 8); return ret; } -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 UpdateFrameForwardConfig(is_nitro_device_handlers* handlers, 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); + return WriteNecMemU16(handlers, 0x0800000A, ((colors & 1) << 4) | ((screens & 3) << 2) | ((rate & 3) << 0), device_desc); + int ret = SendWriteCommandU32(handlers, 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); + ret = SendWriteCommandU32(handlers, 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); + return SendWriteCommandU32(handlers, IS_NITRO_CAP_CMD_SET_FWD_MODE, screens & 3, device_desc); } -int UpdateFrameForwardEnable(libusb_device_handle *handle, bool enable, bool restart, const is_nitro_usb_device* device_desc) { +int UpdateFrameForwardEnable(is_nitro_device_handlers* handlers, 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); + return WriteNecMemU16(handlers, 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); + return SendWriteCommandU32(handlers, IS_NITRO_CAP_CMD_SET_FWD_RESTART, value, device_desc); } -int ReadFrame(libusb_device_handle *handle, uint8_t* buf, int length, const is_nitro_usb_device* device_desc) { +int ReadLidState(is_nitro_device_handlers* handlers, uint32_t* out, const is_nitro_usb_device* device_desc) { + if(device_desc->is_capture) + return SendReadCommandU32(handlers, IS_NITRO_CAP_CMD_GET_LID_STATE, out, device_desc); + uint16_t flags = 0; + int ret = ReadNecMemU16(handlers, 0x08000000, &flags, device_desc); + if(ret < 0) + return ret; + *out = (flags & 2) >> 1; + return ret; +} + +int ReadFrame(is_nitro_device_handlers* handlers, 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, device_desc, buf, length, &num_bytes); + int ret = bulk_in(handlers, device_desc, buf, length, &num_bytes); if(num_bytes != length) return LIBUSB_ERROR_INTERRUPTED; return ret; diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_is_driver.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_is_driver.cpp new file mode 100644 index 0000000..ebded22 --- /dev/null +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_is_driver.cpp @@ -0,0 +1,213 @@ +#include "usb_is_nitro_communications.hpp" +#include "usb_is_nitro_setup_general.hpp" +#include "usb_is_nitro_is_driver.hpp" + +#include +#include +#ifdef _WIN32 +#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. + +const GUID is_nitro_driver_guid = { .Data1 = 0xB78D7ADA, .Data2 = 0xDDF4, .Data3 = 0x418F, .Data4 = {0x8C, 0x7C, 0x4A, 0xC8, 0x80, 0x30, 0xF5, 0x42} }; + +static bool read_is_driver_device_info(HANDLE handle, uint8_t* buffer, size_t size, DWORD* num_read) { + return DeviceIoControl(handle, 0x22000C, buffer, size, buffer, size, num_read, NULL); +} + +static bool is_driver_device_reset(HANDLE handle) { + DWORD num_read; + return DeviceIoControl(handle, 0x220004, NULL, 0, NULL, 0, &num_read, NULL); +} + +static bool is_driver_pipe_reset(HANDLE handle) { + DWORD num_read; + return DeviceIoControl(handle, 0x220008, NULL, 0, NULL, 0, &num_read, NULL); +} + +static std::string is_driver_get_device_path(HDEVINFO DeviceInfoSet, SP_DEVICE_INTERFACE_DATA* DeviceInterfaceData) { + std::string result = ""; + // Call this with an empty buffer to the required size of the structure. + ULONG requiredSize; + SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, DeviceInterfaceData, NULL, 0, &requiredSize, NULL); + if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return result; + + PSP_DEVICE_INTERFACE_DETAIL_DATA devInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new uint8_t[requiredSize]); + if(!devInterfaceDetailData) + return result; + + devInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + if(SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, DeviceInterfaceData, devInterfaceDetailData, requiredSize, &requiredSize, NULL)) + result = (std::string)(devInterfaceDetailData->DevicePath); + + delete []((uint8_t*)devInterfaceDetailData); + return result; +} + +static bool is_driver_get_device_pid_vid(std::string path, uint16_t& out_vid, uint16_t& out_pid) { + HANDLE handle = CreateFile(path.c_str(), (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, 0, NULL); + if (handle == INVALID_HANDLE_VALUE) + return false; + uint8_t buffer[0x412]; + memset(buffer, 0, sizeof(buffer)); + DWORD num_read = 0; + bool result = read_is_driver_device_info(handle, buffer, sizeof(buffer), &num_read); + CloseHandle(handle); + if(!result) + return false; + if (num_read < 11) + return false; + out_vid = buffer[8] + (buffer[9] << 8); + out_pid = buffer[10] + (buffer[11] << 8); + return true; +} + +static void set_handle_timeout(HANDLE handle, int timeout) { + COMMTIMEOUTS timeouts; + if (GetCommTimeouts(handle, &timeouts)) { + timeouts.ReadTotalTimeoutConstant = timeout; + timeouts.ReadTotalTimeoutMultiplier = 0; + SetCommTimeouts(handle, &timeouts); + } +} + +static bool is_driver_setup_connection(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, std::string path) { + handlers->usb_handle = NULL; + handlers->mutex = NULL; + handlers->write_handle = INVALID_HANDLE_VALUE; + handlers->read_handle = INVALID_HANDLE_VALUE; + std::string mutex_name = "Global\\ISU_" + path.substr(4); + std::replace(mutex_name.begin(), mutex_name.end(), '\\', '@'); + handlers->mutex = CreateMutex(NULL, true, mutex_name.c_str()); + if ((handlers->mutex != NULL) && (GetLastError() == ERROR_ALREADY_EXISTS)) { + CloseHandle(handlers->mutex); + handlers->mutex = NULL; + } + if (handlers->mutex == NULL) + return false; + handlers->write_handle = CreateFile((path + (char)(std::filesystem::path::preferred_separator)+"Pipe00").c_str(), (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, 0, NULL); + handlers->read_handle = CreateFile((path + (char)(std::filesystem::path::preferred_separator)+"Pipe01").c_str(), (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, 0, NULL); + if ((handlers->write_handle == INVALID_HANDLE_VALUE) || (handlers->read_handle == INVALID_HANDLE_VALUE)) + return false; + if (!is_driver_device_reset(handlers->write_handle)) + return false; + if (!is_driver_pipe_reset(handlers->write_handle)) + return false; + if (!is_driver_pipe_reset(handlers->read_handle)) + return false; + set_handle_timeout(handlers->read_handle, usb_device_desc->bulk_timeout); + set_handle_timeout(handlers->write_handle, usb_device_desc->bulk_timeout); + return true; +} +#endif + +is_nitro_device_handlers* is_driver_serial_reconnection(const is_nitro_usb_device* usb_device_desc, CaptureDevice* device) { + is_nitro_device_handlers* final_handlers = NULL; + #ifdef _WIN32 + if (device->path != "") { + is_nitro_device_handlers handlers; + if (is_driver_setup_connection(&handlers, usb_device_desc, device->path)) { + final_handlers = new is_nitro_device_handlers; + final_handlers->usb_handle = NULL; + final_handlers->mutex = handlers.mutex; + final_handlers->read_handle = handlers.read_handle; + final_handlers->write_handle = handlers.write_handle; + } + else + is_driver_end_connection(&handlers); + } + #endif + return final_handlers; +} + +void is_driver_end_connection(is_nitro_device_handlers* handlers) { + #ifdef _WIN32 + if (handlers->write_handle != INVALID_HANDLE_VALUE) + CloseHandle(handlers->write_handle); + handlers->write_handle = INVALID_HANDLE_VALUE; + if (handlers->read_handle != INVALID_HANDLE_VALUE) + CloseHandle(handlers->read_handle); + handlers->read_handle = INVALID_HANDLE_VALUE; + if(handlers->mutex != NULL) + CloseHandle(handlers->mutex); + handlers->mutex = NULL; + #endif +} + +void is_driver_list_devices(std::vector &devices_list, bool* not_supported_elems, int *curr_serial_extra_id_is_nitro, const size_t num_is_nitro_desc) { + #ifdef _WIN32 + HDEVINFO DeviceInfoSet = SetupDiGetClassDevs( + &is_nitro_driver_guid, + NULL, + NULL, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; + ZeroMemory(&DeviceInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA)); + DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + uint32_t i = 0; + while (SetupDiEnumDeviceInterfaces(DeviceInfoSet, NULL, &is_nitro_driver_guid, i++, &DeviceInterfaceData)) { + std::string path = is_driver_get_device_path(DeviceInfoSet, &DeviceInterfaceData); + if (path == "") + continue; + uint16_t vid = 0; + uint16_t pid = 0; + if(!is_driver_get_device_pid_vid(path, vid, pid)) + continue; + for (int j = 0; j < num_is_nitro_desc; j++) { + const is_nitro_usb_device* usb_device_desc = GetISNitroDesc(j); + if(not_supported_elems[j] && (usb_device_desc->vid == vid) && (usb_device_desc->pid == pid)) { + is_nitro_device_handlers handlers; + if(is_driver_setup_connection(&handlers, usb_device_desc, path)) + is_nitro_insert_device(devices_list, &handlers, usb_device_desc, curr_serial_extra_id_is_nitro[j], path); + is_driver_end_connection(&handlers); + break; + } + } + } + + if (DeviceInfoSet) { + SetupDiDestroyDeviceInfoList(DeviceInfoSet); + } + #endif +} + +// Write to bulk +int is_driver_bulk_out(is_nitro_device_handlers* handlers, uint8_t* buf, int length, int* transferred) { + bool result = true; + #ifdef _WIN32 + DWORD num_bytes = 0; + result = WriteFile(handlers->write_handle, buf, length, &num_bytes, NULL); + *transferred = num_bytes; + #endif + return result ? LIBUSB_SUCCESS : LIBUSB_ERROR_OTHER; +} + +// Read from bulk +int is_driver_bulk_in(is_nitro_device_handlers* handlers, uint8_t* buf, int length, int* transferred) { + bool result = true; + #ifdef _WIN32 + DWORD num_bytes = 0; + result = ReadFile(handlers->read_handle, buf, length, &num_bytes, NULL); + *transferred = num_bytes; + #endif + return result ? LIBUSB_SUCCESS : LIBUSB_ERROR_OTHER; +} \ No newline at end of file diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.cpp new file mode 100644 index 0000000..a82fddc --- /dev/null +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_libusb.cpp @@ -0,0 +1,138 @@ +#include "usb_is_nitro_communications.hpp" +#include "usb_is_nitro_setup_general.hpp" +#include "usb_is_nitro_libusb.hpp" +#include "usb_generic.hpp" + +#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. + +static bool is_nitro_libusb_setup_connection(libusb_device_handle* handle, const is_nitro_usb_device* usb_device_desc) { + if (libusb_set_configuration(handle, usb_device_desc->default_config) != LIBUSB_SUCCESS) + return false; + if(libusb_claim_interface(handle, usb_device_desc->default_interface) != LIBUSB_SUCCESS) + return false; + if(libusb_clear_halt(handle, usb_device_desc->ep1_out) != LIBUSB_SUCCESS) + return false; + if(libusb_clear_halt(handle, usb_device_desc->ep2_in) != LIBUSB_SUCCESS) + return false; + return true; +} + +static int is_nitro_libusb_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 LIBUSB_ERROR_NOT_FOUND; + if((usb_descriptor->iManufacturer != usb_device_desc->manufacturer_id) || (usb_descriptor->iProduct != usb_device_desc->product_id)) + return LIBUSB_ERROR_NOT_FOUND; + int result = libusb_open(usb_device, &handle); + if((result < 0) || (handle == NULL)) + return result; + if(is_nitro_libusb_setup_connection(handle, usb_device_desc)) { + is_nitro_device_handlers handlers; + handlers.usb_handle = handle; + is_nitro_insert_device(devices_list, &handlers, usb_device_desc, curr_serial_extra_id); + libusb_release_interface(handle, usb_device_desc->default_interface); + } + libusb_close(handle); + return result; +} + +is_nitro_device_handlers* is_nitro_libusb_serial_reconnection(const is_nitro_usb_device* usb_device_desc, CaptureDevice* device, int &curr_serial_extra_id) { + if(!usb_is_initialized()) + return NULL; + libusb_device **usb_devices; + int num_devices = libusb_get_device_list(get_usb_ctx(), &usb_devices); + libusb_device_descriptor usb_descriptor{}; + is_nitro_device_handlers* final_handlers = NULL; + + for(int i = 0; i < num_devices; i++) { + is_nitro_device_handlers handlers; + int result = libusb_get_device_descriptor(usb_devices[i], &usb_descriptor); + if(result < 0) + continue; + if((usb_descriptor.idVendor != usb_device_desc->vid) || (usb_descriptor.idProduct != usb_device_desc->pid)) + continue; + if((usb_descriptor.iManufacturer != usb_device_desc->manufacturer_id) || (usb_descriptor.iProduct != usb_device_desc->product_id)) + continue; + result = libusb_open(usb_devices[i], &handlers.usb_handle); + if(result || (handlers.usb_handle == NULL)) + continue; + if (is_nitro_libusb_setup_connection(handlers.usb_handle, usb_device_desc)) { + std::string device_serial_number = get_serial(usb_device_desc, &handlers, curr_serial_extra_id); + if (device->serial_number == device_serial_number) { + final_handlers = new is_nitro_device_handlers; + final_handlers->usb_handle = handlers.usb_handle; + break; + } + libusb_release_interface(handlers.usb_handle, usb_device_desc->default_interface); + } + libusb_close(handlers.usb_handle); + } + + if(num_devices >= 0) + libusb_free_device_list(usb_devices, 1); + + return final_handlers; +} + +void is_nitro_libusb_end_connection(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc, bool interface_claimed) { + if (interface_claimed) + libusb_release_interface(handlers->usb_handle, device_desc->default_interface); + libusb_close(handlers->usb_handle); + handlers->usb_handle = NULL; +} + +void is_nitro_libusb_list_devices(std::vector &devices_list, bool* no_access_elems, bool* not_supported_elems, int* curr_serial_extra_id_is_nitro, const size_t num_is_nitro_desc) { + if(!usb_is_initialized()) + return; + libusb_device **usb_devices; + int num_devices = libusb_get_device_list(get_usb_ctx(), &usb_devices); + libusb_device_descriptor usb_descriptor{}; + + for(int i = 0; i < num_devices; i++) { + int result = libusb_get_device_descriptor(usb_devices[i], &usb_descriptor); + if(result < 0) + continue; + for (int j = 0; j < num_is_nitro_desc; j++) { + result = is_nitro_libusb_insert_device(devices_list, GetISNitroDesc(j), usb_devices[i], &usb_descriptor, curr_serial_extra_id_is_nitro[j]); + if (result != LIBUSB_ERROR_NOT_FOUND) { + if (result == LIBUSB_ERROR_ACCESS) + no_access_elems[j] = true; + if (result == LIBUSB_ERROR_NOT_SUPPORTED) + not_supported_elems[j] = true; + break; + } + } + } + + if(num_devices >= 0) + libusb_free_device_list(usb_devices, 1); +} + +// Write to bulk +int is_nitro_libusb_bulk_out(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred) { + return libusb_bulk_transfer(handlers->usb_handle, usb_device_desc->ep1_out, buf, length, transferred, usb_device_desc->bulk_timeout); +} + +// Read from bulk +int is_nitro_libusb_bulk_in(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred) { + return libusb_bulk_transfer(handlers->usb_handle, usb_device_desc->ep2_in, buf, length, transferred, usb_device_desc->bulk_timeout); +} diff --git a/source/dscapture_ftd2.cpp b/source/CaptureDeviceSpecific/dscapture_ftd2.cpp old mode 100755 new mode 100644 similarity index 100% rename from source/dscapture_ftd2.cpp rename to source/CaptureDeviceSpecific/dscapture_ftd2.cpp diff --git a/source/usb_ds_3ds_capture.cpp b/source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp old mode 100755 new mode 100644 similarity index 94% rename from source/usb_ds_3ds_capture.cpp rename to source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp index ec1f0c7..7c8b31f --- a/source/usb_ds_3ds_capture.cpp +++ b/source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp @@ -73,6 +73,11 @@ static const usb_device usb_old_ds_desc = { .i2caddr_3dsconfig = 0, .bitstream_3dscfg_ver = 0 }; +static const usb_device* usb_devices_desc_list[] = { + &usb_3ds_desc, + &usb_old_ds_desc, +}; + // Read vendor request from control endpoint. Returns bytes transferred (<0 = libusb error) static int vend_in(libusb_device_handle *handle, const usb_device* usb_device_desc, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t *buf) { return libusb_control_transfer(handle, ((uint8_t)LIBUSB_REQUEST_TYPE_VENDOR | (uint8_t)LIBUSB_ENDPOINT_IN), bRequest, wValue, wIndex, buf, wLength, usb_device_desc->control_timeout); @@ -133,21 +138,21 @@ static std::string get_serial(libusb_device_handle *handle, libusb_device_descri return serial_str; } -static bool insert_device(std::vector &devices_list, const 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 usb_device* usb_device_desc, libusb_device *usb_device, libusb_device_descriptor *usb_descriptor, int &curr_serial_extra_id) { libusb_device_handle *handle = NULL; uint8_t data[SERIAL_NUMBER_SIZE]; if((usb_descriptor->idVendor != usb_device_desc->vid) || (usb_descriptor->idProduct != usb_device_desc->pid)) - return false; + return LIBUSB_ERROR_NOT_FOUND; int result = libusb_open(usb_device, &handle); if(result || (handle == NULL)) - return true; + return result; 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, (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; + return result; } static libusb_device_handle* usb_find_by_serial_number(const usb_device* usb_device_desc, std::string &serial_number) { @@ -309,17 +314,24 @@ void list_devices_usb_ds_3ds(std::vector &devices_list) { libusb_device **usb_devices; int num_devices = libusb_get_device_list(get_usb_ctx(), &usb_devices); libusb_device_descriptor usb_descriptor{}; - - int curr_serial_extra_id_3ds = 0; - int curr_serial_extra_id_old_ds = 0; + + const size_t num_usb_desc = sizeof(usb_devices_desc_list) / sizeof(usb_devices_desc_list[0]); + bool no_access_elems[num_usb_desc]; + int curr_serial_extra_ids[num_usb_desc]; + for (int i = 0; i < num_usb_desc; i++) { + no_access_elems[i] = false; + curr_serial_extra_ids[i] = 0; + } for(int i = 0; i < num_devices; i++) { int result = libusb_get_device_descriptor(usb_devices[i], &usb_descriptor); if(result < 0) continue; - if(insert_device(devices_list, &usb_3ds_desc, usb_devices[i], &usb_descriptor, curr_serial_extra_id_3ds)) - continue; - if(insert_device(devices_list, &usb_old_ds_desc, usb_devices[i], &usb_descriptor, curr_serial_extra_id_old_ds)) - continue; + for(int j = 0; j < num_usb_desc; j++) + if(insert_device(devices_list, usb_devices_desc_list[j], usb_devices[i], &usb_descriptor, curr_serial_extra_ids[j]) != LIBUSB_ERROR_NOT_FOUND) { + if (result == LIBUSB_ERROR_ACCESS) + no_access_elems[j] = true; + break; + } } if(num_devices >= 0) diff --git a/source/usb_generic.cpp b/source/CaptureDeviceSpecific/usb_generic.cpp similarity index 100% rename from source/usb_generic.cpp rename to source/CaptureDeviceSpecific/usb_generic.cpp diff --git a/source/conversions.cpp b/source/conversions.cpp index 67f3d63..928bcce 100755 --- a/source/conversions.cpp +++ b/source/conversions.cpp @@ -3,7 +3,7 @@ #include "3dscapture_ftd3.hpp" #include "dscapture_ftd2.hpp" #include "usb_ds_3ds_capture.hpp" -#include "usb_is_nitro_capture.hpp" +#include "usb_is_nitro_acquisition.hpp" #include diff --git a/source/devicecapture.cpp b/source/devicecapture.cpp index 99d13ea..900fc3f 100755 --- a/source/devicecapture.cpp +++ b/source/devicecapture.cpp @@ -2,7 +2,7 @@ #include "3dscapture_ftd3.hpp" #include "dscapture_ftd2.hpp" #include "usb_ds_3ds_capture.hpp" -#include "usb_is_nitro_capture.hpp" +#include "usb_is_nitro_acquisition.hpp" #include #include @@ -147,7 +147,7 @@ void captureCall(CaptureData* capture_data) { #endif #ifdef USE_IS_NITRO_USB if(capture_data->status.device.cc_type == CAPTURE_CONN_IS_NITRO) - is_nitro_capture_main_loop(capture_data); + is_nitro_acquisition_main_loop(capture_data); #endif capture_data->status.close_success = false; @@ -173,7 +173,7 @@ void captureCall(CaptureData* capture_data) { #endif #ifdef USE_IS_NITRO_USB if(capture_data->status.device.cc_type == CAPTURE_CONN_IS_NITRO) - usb_is_nitro_capture_cleanup(capture_data); + usb_is_nitro_acquisition_cleanup(capture_data); #endif capture_data->status.close_success = false; @@ -207,7 +207,7 @@ uint64_t get_video_in_size(CaptureData* capture_data) { #endif #ifdef USE_IS_NITRO_USB if(capture_data->status.device.cc_type == CAPTURE_CONN_IS_NITRO) - return usb_is_nitro_emulator_get_video_in_size(capture_data); + return usb_is_nitro_get_video_in_size(capture_data); #endif return 0; } diff --git a/source/usb_is_nitro_capture.cpp b/source/usb_is_nitro_capture.cpp deleted file mode 100644 index aa6b803..0000000 --- a/source/usb_is_nitro_capture.cpp +++ /dev/null @@ -1,567 +0,0 @@ -#include "devicecapture.hpp" -#include "usb_is_nitro.hpp" -#include "usb_is_nitro_capture.hpp" -#include "usb_generic.hpp" - -#include -#include -#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_NITRO_REAL_SERIAL_NUMBER_SIZE + 1) - -#define FRAME_BUFFER_SIZE 32 - -enum usb_capture_status { - USB_CAPTURE_SUCCESS = 0, - USB_CAPTURE_SKIP, - USB_CAPTURE_PIPE_ERROR, - USB_CAPTURE_FRAMEINFO_ERROR, - USB_CAPTURE_ERROR -}; - -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]; - std::string serial_str = std::to_string(curr_serial_extra_id); - bool conn_success = true; - if(libusb_set_configuration(handle, usb_device_desc->default_config) != LIBUSB_SUCCESS) - conn_success = false; - if(conn_success && libusb_claim_interface(handle, usb_device_desc->default_interface) != LIBUSB_SUCCESS) - conn_success = false; - 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, usb_device_desc) != LIBUSB_SUCCESS)) { - int ret = 0; - while(ret >= 0) - 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) { - data[IS_NITRO_REAL_SERIAL_NUMBER_SIZE] = '\0'; - serial_str = std::string((const char*)data); - } - else - curr_serial_extra_id += 1; - if(conn_success) - libusb_release_interface(handle, usb_device_desc->default_interface); - return serial_str; -} - -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 LIBUSB_ERROR_NOT_FOUND; - if((usb_descriptor->iManufacturer != usb_device_desc->manufacturer_id) || (usb_descriptor->iProduct != usb_device_desc->product_id)) - return LIBUSB_ERROR_NOT_FOUND; - int result = libusb_open(usb_device, &handle); - 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 result; -} - -static libusb_device_handle* usb_find_by_serial_number(const is_nitro_usb_device* usb_device_desc, std::string &serial_number) { - if(!usb_is_initialized()) - return NULL; - libusb_device **usb_devices; - int num_devices = libusb_get_device_list(get_usb_ctx(), &usb_devices); - libusb_device_descriptor usb_descriptor{}; - libusb_device_handle *final_handle = NULL; - - int curr_serial_extra_id = 0; - for(int i = 0; i < num_devices; i++) { - libusb_device_handle *handle = NULL; - uint8_t data[SERIAL_NUMBER_SIZE]; - int result = libusb_get_device_descriptor(usb_devices[i], &usb_descriptor); - if(result < 0) - continue; - if((usb_descriptor.idVendor != usb_device_desc->vid) || (usb_descriptor.idProduct != usb_device_desc->pid)) - continue; - if((usb_descriptor.iManufacturer != usb_device_desc->manufacturer_id) || (usb_descriptor.iProduct != usb_device_desc->product_id)) - continue; - result = libusb_open(usb_devices[i], &handle); - if(result || (handle == NULL)) - continue; - std::string device_serial_number = get_serial(usb_device_desc, handle, curr_serial_extra_id); - if(serial_number == device_serial_number) { - final_handle = handle; - break; - } - libusb_close(handle); - } - - if(num_devices >= 0) - libusb_free_device_list(usb_devices, 1); - return final_handle; -} - -void list_devices_is_nitro(std::vector &devices_list) { - if(!usb_is_initialized()) - return; - libusb_device **usb_devices; - int num_devices = libusb_get_device_list(get_usb_ctx(), &usb_devices); - libusb_device_descriptor usb_descriptor{}; - - int curr_serial_extra_id_is_nitro_emulator = 0; - for(int i = 0; i < num_devices; i++) { - int result = libusb_get_device_descriptor(usb_devices[i], &usb_descriptor); - if(result < 0) - continue; - 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, const is_nitro_usb_device *device_desc, bool interface_claimed = true) { - if(interface_claimed) - 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; - 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_device_info->default_config) != LIBUSB_SUCCESS) { - capture_error_print(true, capture_data, "Configuration failed"); - is_nitro_connection_end(dev, usb_device_info, false); - return false; - } - 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, usb_device_info, false); - return false; - } - capture_data->handle = (void*)dev; - - return true; -} - -static uint64_t _is_nitro_emulator_get_video_in_size(CaptureScreensType capture_type) { - if((capture_type == CAPTURE_SCREENS_TOP) || (capture_type == CAPTURE_SCREENS_BOTTOM)) - return sizeof(ISNitroEmulatorVideoInputData) / 2; - return sizeof(ISNitroEmulatorVideoInputData); -} - -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, 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), usb_device_desc); - if(ret < 0) - return ret; - } - return LIBUSB_SUCCESS; -} - -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, usb_device_desc); -} - -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, usb_device_desc); - if(ret < 0) - return ret; - 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, true, true, usb_device_desc); - if(ret < 0) - return ret; - ret = UpdateFrameForwardEnable(handle, true, false, usb_device_desc); - if(ret < 0) - return ret; - - // Get to the closest next frame - auto clock_start = std::chrono::high_resolution_clock::now(); - uint16_t oldFrameCount; - uint16_t newFrameCount; - ret = GetFrameCounter(handle, &oldFrameCount, usb_device_desc); - if(ret < 0) - return ret; - newFrameCount = oldFrameCount; - while(newFrameCount == oldFrameCount) { - ret = GetFrameCounter(handle, &newFrameCount, usb_device_desc); - if(ret < 0) - return ret; - const auto curr_time = std::chrono::high_resolution_clock::now(); - const std::chrono::duration diff = curr_time - clock_start; - // If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up - if(diff.count() > 0.2) - break; - } - - // Get to the next modulo 32 frame. - // 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, 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, usb_device_desc); - if(ret < 0) - return ret; - // Placing a sleep of some kind here would be much better... - // Though this is only executed for a small time when first connecting... - const auto curr_time = std::chrono::high_resolution_clock::now(); - const std::chrono::duration diff = curr_time - clock_start; - // If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up - if(diff.count() > 1.0) - break; - } - const auto curr_time = std::chrono::high_resolution_clock::now(); - const std::chrono::duration diff = curr_time - clock_start; - // Sometimes the upper 8 bits aren't updated... Use only the lower 8 bits. - newFrameCount &= 0xFF; - oldFrameCount &= 0xFF; - int frame_diff = ((int)oldFrameCount) - ((int)newFrameCount); - if(frame_diff < 0) - frame_diff += 1 << 8; - out_frame_count = oldFrameCount; - // Determine how much time a single frame takes. We'll use it for sleeps - if(frame_diff == 0) - single_frame_time = 0; - else - single_frame_time = diff.count() / frame_diff; - - // Start the actual DMA - if(single_frame_time > 0) { - ret = StartUsbCaptureDma(handle, usb_device_desc); - if(ret < 0) - return ret; - } - return ret; -} - -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, usb_device_desc); - if(ret < 0) - return ret; - ret = StopUsbCaptureDma(handle, usb_device_desc); - if(ret < 0) - return ret; - 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) { - if(curr_frame_counter == 0) - return; - auto curr_time = std::chrono::high_resolution_clock::now(); - std::chrono::duration diff = curr_time - clock_last_reset; - float expected_time = single_frame_time * curr_frame_counter; - // If the current time is too low, sleep a bit to make sure we don't overrun the framerate counter - // Don't do it regardless of the situation, and only in small increments... - // Otherwise there is the risk of sleeping too much - while((diff.count() < expected_time) && ((expected_time - diff.count()) > (single_frame_time / 4)) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1)))) { - default_sleep((expected_time - diff.count()) / 4); - curr_time = std::chrono::high_resolution_clock::now(); - diff = curr_time - clock_last_reset; - } -} - -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, usb_device_desc); - if(ret < 0) - return ret; - - // If the user requests a mode change, accomodate them. - // 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, usb_device_desc); - if(ret < 0) - return ret; - } - - uint16_t internalFrameCount = 0; - uint16_t full_internalFrameCount = 0; - int frame_diff = 0; - int diff_target = FRAME_BUFFER_SIZE * multiplier; - do { - // Check how many frames have passed... - 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; - if(ret < 0) - return ret; - frame_diff = internalFrameCount - last_frame_counter; - if(frame_diff < 0) - frame_diff += 1 << 8; - // If the frames haven't advanced, the DS is either turned off or sleeping. If so, avoid locking up - if(frame_diff == 0) - break; - const auto curr_time = std::chrono::high_resolution_clock::now(); - const std::chrono::duration diff = curr_time - clock_last_reset; - // If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up - if(diff.count() > (1.0 * multiplier)) { - frame_diff = 0; - break; - } - // Exit if enough frames have passed, or if there currently is some delay. - // Exiting early makes it possible to catch up to the DMA, if we're behind. - } while((frame_diff < diff_target) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1)))); - // Determine how much time a single frame takes. We'll use it for sleeps - const auto curr_time = std::chrono::high_resolution_clock::now(); - const std::chrono::duration diff = curr_time - clock_last_reset; - if(frame_diff == 0) - single_frame_time = 0; - else - single_frame_time = diff.count() / (frame_diff / ((float)multiplier)); - clock_last_reset = curr_time; - - // Save the current frame counter's 8 LSB - last_frame_counter = internalFrameCount; - - // 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, true, true, usb_device_desc); - if(ret < 0) - return ret; - clock_last_reset = std::chrono::high_resolution_clock::now(); - } - 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, usb_device_desc); - if(ret < 0) - return ret; - } - } - return LIBUSB_SUCCESS; -} - -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 = 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; - } - 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) { - 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), 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(); - } - else { - capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM; - 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, 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, 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, (const is_nitro_usb_device*)capture_data->status.device.descriptor); -} - -void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureScreensType capture_type) { - if(!usb_is_initialized()) - return; - int num_pixels = _is_nitro_emulator_get_video_in_size(capture_type) / 3; - int out_start_pos = 0; - int out_clear_pos = num_pixels; - if(capture_type == CAPTURE_SCREENS_BOTTOM) { - out_start_pos = num_pixels; - out_clear_pos = 0; - } - if((capture_type == CAPTURE_SCREENS_BOTTOM) || (capture_type == CAPTURE_SCREENS_TOP)) - memset(p_out->screen_data[out_clear_pos], 0, num_pixels * 3); - for(int i = 0; i < num_pixels; i++) { - p_out->screen_data[i + out_start_pos][0] = p_in->is_nitro_capture_received.video_in.screen_data[i][2]; - p_out->screen_data[i + out_start_pos][1] = p_in->is_nitro_capture_received.video_in.screen_data[i][1]; - p_out->screen_data[i + out_start_pos][2] = p_in->is_nitro_capture_received.video_in.screen_data[i][0]; - } -} - -void usb_is_nitro_init() { - return usb_init(); -} - -void usb_is_nitro_close() { - usb_close(); -} -