diff --git a/CMakeLists.txt b/CMakeLists.txt index de4e44b..0a28775 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -313,7 +313,7 @@ if(USE_LIBUSB_SUPPORT) endif() endif() if(IS_DEVICES_SUPPORT) - list(APPEND SOURCE_CPP_EXTRA_FILES ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_device_communications.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_device_acquisition.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_nitro_acquisition_capture.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_nitro_acquisition_emulator.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_device_is_driver.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_device_libusb.cpp) + list(APPEND SOURCE_CPP_EXTRA_FILES ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_device_communications.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_device_acquisition.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_nitro_acquisition_capture.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_twl_acquisition_capture.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_nitro_acquisition_emulator.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_device_is_driver.cpp ${SOURCE_CPP_IS_DEVICES_FILES_BASE_PATH}/usb_is_device_libusb.cpp ${TOOLS_DATA_DIR}/is_twl_0x81_packet.cpp) if(MSVC) list(APPEND EXTRA_CXX_FLAGS "/DUSE_IS_DEVICES_USB") else() @@ -516,6 +516,13 @@ add_custom_command( DEPENDS ${CMAKE_SOURCE_DIR}/bin/ftd2_ds2_fw_1.bin ${TOOLS_DATA_DIR}/CMakeBin2C${HOST_FINAL_EXTENSION} ) +add_custom_command( + OUTPUT ${TOOLS_DATA_DIR}/is_twl_0x81_packet.cpp + COMMENT "Convert binary to C - IS TWL Capture 0x81 Packet" + COMMAND ${TOOLS_DATA_DIR}/CMakeBin2C${HOST_FINAL_EXTENSION} ${CMAKE_SOURCE_DIR}/bin/is_twl_0x81_packet.bin ${TOOLS_DATA_DIR} is_twl_0x81_packet is_twl_0x81_packet + DEPENDS ${CMAKE_SOURCE_DIR}/bin/is_twl_0x81_packet.bin ${TOOLS_DATA_DIR}/CMakeBin2C${HOST_FINAL_EXTENSION} +) + add_custom_command( OUTPUT ${TOOLS_DATA_DIR}/ftd2_ds2_fw_2.cpp COMMENT "Convert fw binary to C - FTD2 NDS 2" diff --git a/bin/is_twl_0x81_packet.bin b/bin/is_twl_0x81_packet.bin new file mode 100644 index 0000000..832ce99 --- /dev/null +++ b/bin/is_twl_0x81_packet.bin @@ -0,0 +1 @@ +"RJV1iTE$b.L7mf֫rV&65'EeY@Bx?;}ۺһ:s$1xGaݴ]\˸c @ !?H_O_?mީ~+>ѽHѧ}AeYH<O_N  \ No newline at end of file diff --git a/include/CaptureDeviceSpecific/ISDevices/usb_is_device_acquisition_general.hpp b/include/CaptureDeviceSpecific/ISDevices/usb_is_device_acquisition_general.hpp index 8029a75..d1c91b1 100644 --- a/include/CaptureDeviceSpecific/ISDevices/usb_is_device_acquisition_general.hpp +++ b/include/CaptureDeviceSpecific/ISDevices/usb_is_device_acquisition_general.hpp @@ -27,6 +27,7 @@ int EndAcquisition(const is_device_usb_device* usb_device_desc, is_device_device int is_device_read_frame_and_output(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point &clock_start); void is_device_read_frame_request(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data, CaptureScreensType curr_capture_type, uint32_t index); uint64_t usb_is_device_get_video_in_size(CaptureScreensType capture_type, is_device_type device_type); +void output_to_thread(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point* clock_start, size_t read_size); ISDeviceCaptureReceivedData* is_device_get_free_buffer(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data); bool is_device_are_buffers_all_free(ISDeviceCaptureReceivedData* is_device_capture_recv_data); diff --git a/include/CaptureDeviceSpecific/ISDevices/usb_is_device_communications.hpp b/include/CaptureDeviceSpecific/ISDevices/usb_is_device_communications.hpp index 73ff553..4671930 100644 --- a/include/CaptureDeviceSpecific/ISDevices/usb_is_device_communications.hpp +++ b/include/CaptureDeviceSpecific/ISDevices/usb_is_device_communications.hpp @@ -70,6 +70,8 @@ struct is_device_usb_device { InputVideoDataType video_data_type; size_t max_usb_packet_size; bool do_pipe_clear_reset; + bool audio_enabled; + size_t max_audio_samples_size; }; struct is_device_device_handlers { @@ -96,6 +98,9 @@ int ReadPowerButtonState(is_device_device_handlers* handlers, bool* out, const i int ResetCPUStart(is_device_device_handlers* handlers, const is_device_usb_device* device_desc); int ResetCPUEnd(is_device_device_handlers* handlers, const is_device_usb_device* device_desc); int ResetFullHardware(is_device_device_handlers* handlers, const is_device_usb_device* device_desc); +int AskFrameLengthPos(is_device_device_handlers* handlers, uint32_t* video_address, uint32_t* video_length, bool video_enabled, uint32_t* audio_address, uint32_t* audio_length, bool audio_enabled, const is_device_usb_device* device_desc); +int SetLastFrameInfo(is_device_device_handlers* handlers, uint32_t video_address, uint32_t video_length, uint32_t audio_address, uint32_t audio_length, const is_device_usb_device* device_desc); +int ReadFrame(is_device_device_handlers* handlers, uint8_t* buf, uint32_t address, uint32_t length, const is_device_usb_device* device_desc); int ReadFrame(is_device_device_handlers* handlers, uint8_t* buf, int length, const is_device_usb_device* device_desc); void ReadFrameAsync(is_device_device_handlers* handlers, uint8_t* buf, int length, const is_device_usb_device* device_desc, isd_async_callback_data* cb_data); void CloseAsyncRead(is_device_device_handlers* handlers, isd_async_callback_data* cb_data); diff --git a/include/CaptureDeviceSpecific/ISDevices/usb_is_twl_acquisition_capture.hpp b/include/CaptureDeviceSpecific/ISDevices/usb_is_twl_acquisition_capture.hpp new file mode 100644 index 0000000..d957bee --- /dev/null +++ b/include/CaptureDeviceSpecific/ISDevices/usb_is_twl_acquisition_capture.hpp @@ -0,0 +1,12 @@ +#ifndef __USB_IS_TWL_ACQUISITION_CAPTURE_HPP +#define __USB_IS_TWL_ACQUISITION_CAPTURE_HPP + +#include "usb_is_device_communications.hpp" +#include "capture_structs.hpp" + +int initial_cleanup_twl_capture(const is_device_usb_device* usb_device_desc, is_device_device_handlers* handlers); +int EndAcquisitionTWLCapture(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_twl_capture_recv_data); +int EndAcquisitionTWLCapture(const is_device_usb_device* usb_device_desc, is_device_device_handlers* handlers); +void is_twl_acquisition_capture_main_loop(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_twl_capture_recv_data); + +#endif diff --git a/include/capture_structs.hpp b/include/capture_structs.hpp index e85743c..49fcd48 100755 --- a/include/capture_structs.hpp +++ b/include/capture_structs.hpp @@ -146,6 +146,7 @@ union CaptureReceived { FTD2OldDSCaptureReceived ftd2_received_old_ds; FTD2OldDSCaptureReceivedRaw ftd2_received_old_ds_raw; ISNitroCaptureReceived is_nitro_capture_received; + ISTWLCaptureReceived is_twl_capture_received; }; struct CaptureDevice { diff --git a/source/CaptureDeviceSpecific/ISDevices/usb_is_device_acquisition.cpp b/source/CaptureDeviceSpecific/ISDevices/usb_is_device_acquisition.cpp index 189482a..c228152 100644 --- a/source/CaptureDeviceSpecific/ISDevices/usb_is_device_acquisition.cpp +++ b/source/CaptureDeviceSpecific/ISDevices/usb_is_device_acquisition.cpp @@ -7,6 +7,7 @@ #include "usb_is_device_acquisition_general.hpp" #include "usb_is_nitro_acquisition_capture.hpp" #include "usb_is_nitro_acquisition_emulator.hpp" +#include "usb_is_twl_acquisition_capture.hpp" #include "usb_generic.hpp" #include @@ -44,6 +45,8 @@ static bool initial_cleanup(const is_device_usb_device* usb_device_desc, is_devi return initial_cleanup_emulator(usb_device_desc, handlers) != LIBUSB_SUCCESS; case IS_NITRO_CAPTURE_DEVICE: return initial_cleanup_capture(usb_device_desc, handlers) != LIBUSB_SUCCESS; + case IS_TWL_CAPTURE_DEVICE: + return initial_cleanup_twl_capture(usb_device_desc, handlers) != LIBUSB_SUCCESS; default: return false; } @@ -67,11 +70,11 @@ std::string get_serial(const is_device_usb_device* usb_device_desc, is_device_de } void is_device_insert_device(std::vector& devices_list, is_device_device_handlers* handlers, const is_device_usb_device* usb_device_desc, int& curr_serial_extra_id_is_device, std::string path) { - devices_list.emplace_back(get_serial(usb_device_desc, handlers, curr_serial_extra_id_is_device), usb_device_desc->name, usb_device_desc->long_name, path, CAPTURE_CONN_IS_NITRO, (void*)usb_device_desc, false, false, false, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, 0, 0, 0, 0, 0, HEIGHT_DS, usb_device_desc->video_data_type); + devices_list.emplace_back(get_serial(usb_device_desc, handlers, curr_serial_extra_id_is_device), usb_device_desc->name, usb_device_desc->long_name, path, CAPTURE_CONN_IS_NITRO, (void*)usb_device_desc, false, false, usb_device_desc->audio_enabled, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, usb_device_desc->max_audio_samples_size, 0, 0, 0, 0, HEIGHT_DS, usb_device_desc->video_data_type); } void is_device_insert_device(std::vector& devices_list, is_device_device_handlers* handlers, const is_device_usb_device* usb_device_desc, int& curr_serial_extra_id_is_device) { - devices_list.emplace_back(get_serial(usb_device_desc, handlers, curr_serial_extra_id_is_device), usb_device_desc->name, usb_device_desc->long_name, CAPTURE_CONN_IS_NITRO, (void*)usb_device_desc, false, false, false, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, 0, 0, 0, 0, 0, HEIGHT_DS, usb_device_desc->video_data_type); + devices_list.emplace_back(get_serial(usb_device_desc, handlers, curr_serial_extra_id_is_device), usb_device_desc->name, usb_device_desc->long_name, CAPTURE_CONN_IS_NITRO, (void*)usb_device_desc, false, false, usb_device_desc->audio_enabled, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, usb_device_desc->max_audio_samples_size, 0, 0, 0, 0, HEIGHT_DS, usb_device_desc->video_data_type); } static is_device_device_handlers* usb_find_by_serial_number(const is_device_usb_device* usb_device_desc, CaptureDevice* device) { @@ -135,6 +138,8 @@ bool is_device_connect_usb(bool print_failed, CaptureData* capture_data, Capture } uint64_t usb_is_device_get_video_in_size(CaptureScreensType capture_type, is_device_type device_type) { + if(device_type == IS_TWL_CAPTURE_DEVICE) + return sizeof(ISTWLCaptureVideoInputData) + (sizeof(uint32_t) * 2); if((capture_type == CAPTURE_SCREENS_TOP) || (capture_type == CAPTURE_SCREENS_BOTTOM)) return sizeof(ISNitroEmulatorVideoInputData) / 2; return sizeof(ISNitroEmulatorVideoInputData); @@ -185,6 +190,8 @@ int EndAcquisition(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_de return EndAcquisitionEmulator(capture_data, is_device_capture_recv_data, do_drain_frames, start_frames, capture_type); case IS_NITRO_CAPTURE_DEVICE: return EndAcquisitionCapture(capture_data, is_device_capture_recv_data); + case IS_TWL_CAPTURE_DEVICE: + return EndAcquisitionTWLCapture(capture_data, is_device_capture_recv_data); default: return 0; } @@ -196,18 +203,20 @@ int EndAcquisition(const is_device_usb_device* usb_device_desc, is_device_device return EndAcquisitionEmulator(usb_device_desc, handlers, do_drain_frames, start_frames, capture_type); case IS_NITRO_CAPTURE_DEVICE: return EndAcquisitionCapture(usb_device_desc, handlers); + case IS_TWL_CAPTURE_DEVICE: + return EndAcquisitionTWLCapture(usb_device_desc, handlers); default: return 0; } } -static void output_to_thread(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point* clock_start) { +void output_to_thread(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point* clock_start, size_t read_size) { // Output to the other threads... const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor; const auto curr_time = std::chrono::high_resolution_clock::now(); const std::chrono::duration diff = curr_time - (*clock_start); *clock_start = curr_time; - capture_data->data_buffers.WriteToBuffer(capture_buf, usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type), diff.count(), &capture_data->status.device, curr_capture_type); + capture_data->data_buffers.WriteToBuffer(capture_buf, read_size, diff.count(), &capture_data->status.device, curr_capture_type); if (capture_data->status.cooldown_curr_in) capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1; @@ -215,6 +224,11 @@ static void output_to_thread(CaptureData* capture_data, CaptureReceived* capture capture_data->status.audio_wait.unlock(); } +static void output_to_thread(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point* clock_start) { + const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor; + output_to_thread(capture_data, capture_buf, curr_capture_type, clock_start, usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type)); +} + int is_device_read_frame_and_output(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point &clock_start) { const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor; int ret = ReadFrame((is_device_device_handlers*)capture_data->handle, (uint8_t*)capture_buf, usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type), usb_device_info); @@ -428,6 +442,9 @@ void is_device_acquisition_main_loop(CaptureData* capture_data) { case IS_NITRO_CAPTURE_DEVICE: is_nitro_acquisition_capture_main_loop(capture_data, is_device_capture_recv_data); break; + case IS_TWL_CAPTURE_DEVICE: + is_twl_acquisition_capture_main_loop(capture_data, is_device_capture_recv_data); + break; default: break; } diff --git a/source/CaptureDeviceSpecific/ISDevices/usb_is_device_communications.cpp b/source/CaptureDeviceSpecific/ISDevices/usb_is_device_communications.cpp index 45418a5..c25b570 100644 --- a/source/CaptureDeviceSpecific/ISDevices/usb_is_device_communications.cpp +++ b/source/CaptureDeviceSpecific/ISDevices/usb_is_device_communications.cpp @@ -2,6 +2,7 @@ #include "usb_is_device_communications.hpp" #include "usb_is_device_libusb.hpp" #include "usb_is_device_is_driver.hpp" +#include "is_twl_0x81_packet.h" #include #include @@ -36,6 +37,9 @@ #define REG_USB_DMA_CONTROL_2 0x0C000028 #define REG_USB_BIU_CONTROL_2 0x0C0000A4 +#define DEFAULT_MAX_LENGTH_IS_TWL_VIDEO 0x00040000 +#define DEFAULT_MAX_LENGTH_IS_TWL_AUDIO 0x00007800 + enum is_nitro_packet_emulator_dir { IS_NITRO_PACKET_EMU_DIR_WRITE = 0x10, IS_NITRO_PACKET_EMU_DIR_READ = 0x11 @@ -59,7 +63,8 @@ enum is_device_packet_type { IS_NITRO_CAPTURE_PACKET_TYPE_DMA_CONTROL = 2, IS_NITRO_PACKET_TYPE_CAPTURE = 3, IS_NITRO_PACKET_TYPE_AGB_CART_ROM = 4, - IS_NITRO_PACKET_TYPE_AGB_BUS2 = 5 + IS_NITRO_PACKET_TYPE_AGB_BUS2 = 5, + IS_TWL_PACKET_TYPE_FRAME = 7, }; enum is_nitro_emulator_command { @@ -88,8 +93,17 @@ enum is_nitro_capture_command { }; enum is_twl_capture_command { + IS_TWL_CAP_CMD_GET_CAP = 0x00, IS_TWL_CAP_CMD_GET_SERIAL = 0x13, IS_TWL_CAP_CMD_UNLOCK_COMMS = 0x1C, + IS_TWL_CAP_CMD_ENABLE_CAP_PRE_3 = 0x80, + IS_TWL_CAP_CMD_ENABLE_CAP_PRE_1 = 0x81, + IS_TWL_CAP_CMD_ASK_CAPTURE_INFORMATION = 0x83, + IS_TWL_CAP_CMD_ENABLE_CAP = 0x84, + IS_TWL_CAP_CMD_ENABLE_CAP_PRE_2 = 0x85, + IS_TWL_CAP_CMD_STOP_FRAME_READS = 0x88, + IS_TWL_CAP_CMD_SET_LAST_FRAME_INFORMATION = 0x8B, + IS_TWL_CAP_CMD_ENABLE_CAP_PART_2 = 0x8C, }; enum is_nitro_emulator_forward_bits { @@ -126,6 +140,26 @@ struct PACKED is_device_rw_packet_header { uint32_t length_w; uint32_t length_r; }; + +struct PACKED is_twl_ask_capture_information_packet { + uint32_t unk[2]; + uint32_t max_length_video; + uint32_t unk2[2]; + uint32_t max_length_audio; + uint32_t unk3[0x10]; +}; +// These two are probably the same struct, with different fields used for +// different packets... +// I say probably, that's why I keep them separate. +struct PACKED is_twl_capture_information_packet { + uint32_t available_video_length; + uint32_t video_start_address; + uint32_t unk; + uint32_t available_audio_length; + uint32_t audio_start_address; + uint32_t unk2; + uint32_t unk3[0x10]; +}; #pragma pack(pop) static const is_device_usb_device usb_is_nitro_emu_rare_desc = { @@ -137,7 +171,7 @@ static const is_device_usb_device usb_is_nitro_emu_rare_desc = { .write_pipe = "Pipe00", .read_pipe = "Pipe01", .product_id = 2, .manufacturer_id = 1, .device_type = IS_NITRO_EMULATOR_DEVICE, .video_data_type = VIDEO_DATA_BGR, .max_usb_packet_size = IS_NITRO_USB_PACKET_LIMIT, -.do_pipe_clear_reset = true +.do_pipe_clear_reset = true, .audio_enabled = false, .max_audio_samples_size = 0 }; static const is_device_usb_device usb_is_nitro_emu_common_desc = { @@ -149,7 +183,7 @@ static const is_device_usb_device usb_is_nitro_emu_common_desc = { .write_pipe = "Pipe00", .read_pipe = "Pipe01", .product_id = 2, .manufacturer_id = 1, .device_type = IS_NITRO_EMULATOR_DEVICE, .video_data_type = VIDEO_DATA_BGR, .max_usb_packet_size = IS_NITRO_USB_PACKET_LIMIT, -.do_pipe_clear_reset = true +.do_pipe_clear_reset = true, .audio_enabled = false, .max_audio_samples_size = 0 }; static const is_device_usb_device usb_is_nitro_cap_desc = { @@ -161,11 +195,11 @@ static const is_device_usb_device usb_is_nitro_cap_desc = { .write_pipe = "Pipe00", .read_pipe = "Pipe01", .product_id = 2, .manufacturer_id = 1, .device_type = IS_NITRO_CAPTURE_DEVICE, .video_data_type = VIDEO_DATA_BGR, .max_usb_packet_size = IS_NITRO_USB_PACKET_LIMIT, -.do_pipe_clear_reset = true +.do_pipe_clear_reset = true, .audio_enabled = false, .max_audio_samples_size = 0 }; static const is_device_usb_device usb_is_twl_cap_desc = { -.name = "ISTC", .long_name = "IS TWL Capture", +.name = "ISTCD", .long_name = "IS TWL Capture (Dev)", .vid = 0x0f6e, .pid = 0x0501, .default_config = 1, .default_interface = 0, .bulk_timeout = 500, @@ -173,11 +207,11 @@ static const is_device_usb_device usb_is_twl_cap_desc = { .write_pipe = "Pipe01", .read_pipe = "Pipe03", .product_id = 2, .manufacturer_id = 1, .device_type = IS_TWL_CAPTURE_DEVICE, .video_data_type = VIDEO_DATA_RGB, .max_usb_packet_size = IS_TWL_USB_PACKET_LIMIT, -.do_pipe_clear_reset = false +.do_pipe_clear_reset = false, .audio_enabled = true, .max_audio_samples_size = sizeof(ISTWLCaptureAudioReceived) * TWL_CAPTURE_MAX_SAMPLES_CHUNK_NUM }; static const is_device_usb_device usb_is_twl_cap_desc_2 = { -.name = "ISTC", .long_name = "IS TWL Capture", +.name = "ISTCR", .long_name = "IS TWL Capture (Retail)", .vid = 0x0f6e, .pid = 0x0502, .default_config = 1, .default_interface = 0, .bulk_timeout = 500, @@ -185,7 +219,7 @@ static const is_device_usb_device usb_is_twl_cap_desc_2 = { .write_pipe = "Pipe01", .read_pipe = "Pipe03", .product_id = 2, .manufacturer_id = 1, .device_type = IS_TWL_CAPTURE_DEVICE, .video_data_type = VIDEO_DATA_RGB, .max_usb_packet_size = IS_TWL_USB_PACKET_LIMIT, -.do_pipe_clear_reset = false +.do_pipe_clear_reset = false, .audio_enabled = true, .max_audio_samples_size = sizeof(ISTWLCaptureAudioReceived) * TWL_CAPTURE_MAX_SAMPLES_CHUNK_NUM }; static const is_device_usb_device* all_usb_is_device_devices_desc[] = { @@ -510,6 +544,10 @@ int SendWriteCommand(is_device_device_handlers* handlers, uint16_t command, uint return SendWritePacket(handlers, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length, device_desc); } +int SendReadWriteCommand(is_device_device_handlers* handlers, uint16_t command, uint8_t* out_buf, int out_length, uint8_t* in_buf, int in_length, const is_device_usb_device* device_desc) { + return SendReadWritePacket(handlers, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, out_buf, out_length, in_buf, in_length, device_desc); +} + int SendReadCommandU32(is_device_device_handlers* handlers, uint16_t command, uint32_t* out, const is_device_usb_device* device_desc) { uint32_t buffer; int ret = SendReadCommand(handlers, command, (uint8_t*)&buffer, sizeof(uint32_t), device_desc); @@ -617,6 +655,9 @@ int DisableLca2(is_device_device_handlers* handlers, const is_device_usb_device* int StartUsbCaptureDma(is_device_device_handlers* handlers, const is_device_usb_device* device_desc) { int ret = 0; + uint8_t buffer[0x200]; + uint8_t is_twl_handshake_buffer[0x10] = {0x39, 0xC5, 0x5E, 0xC1, 0xA3, 0x02, 0x1D, 0xA1, 0x1E, 0xE9, 0x2F, 0x35, 0x29, 0x98, 0x63, 0x6C}; + uint8_t is_twl_handshake_buffer_pre2[0x10] = {0x0C, 0xF7, 0x48, 0x2B, 0xE9, 0x5A, 0xA2, 0x3C, 0xE5, 0x0E, 0x1F, 0xEB, 0x1C, 0x71, 0xB8, 0xAE}; switch(device_desc->device_type) { case IS_NITRO_EMULATOR_DEVICE: ret = WriteNecMemU16(handlers, REG_USB_DMA_CONTROL_2, 2, device_desc); @@ -625,6 +666,24 @@ int StartUsbCaptureDma(is_device_device_handlers* handlers, const is_device_usb_ return WriteNecMemU16(handlers, REG_USB_BIU_CONTROL_2, 1, device_desc); case IS_NITRO_CAPTURE_DEVICE: return SendReadPacket(handlers, IS_NITRO_CAP_CMD_ENABLE_CAP, IS_NITRO_CAPTURE_PACKET_TYPE_DMA_CONTROL, 0, NULL, 1, device_desc, false); + case IS_TWL_CAPTURE_DEVICE: + ret = SendWriteCommand(handlers, IS_TWL_CAP_CMD_ENABLE_CAP_PART_2, NULL, 0, device_desc); + if(ret < 0) + return ret; + ret = SendReadWriteCommand(handlers, IS_TWL_CAP_CMD_ENABLE_CAP_PRE_1, is_twl_0x81_packet, is_twl_0x81_packet_len, buffer, is_twl_0x81_packet_len, device_desc); + if(ret < 0) + return ret; + ret = SendWriteCommand(handlers, IS_TWL_CAP_CMD_ENABLE_CAP_PRE_2, is_twl_handshake_buffer_pre2, 0x10, device_desc); + if(ret < 0) + return ret; + ret = SendReadCommand(handlers, IS_TWL_CAP_CMD_ENABLE_CAP_PRE_3, buffer, 0x200, device_desc); + if(ret < 0) + return ret; + ret = SendReadCommand(handlers, IS_TWL_CAP_CMD_ENABLE_CAP, buffer, 0x10, device_desc); + if(ret < 0) + return ret; + ret = SendReadWriteCommand(handlers, IS_TWL_CAP_CMD_ENABLE_CAP_PART_2, is_twl_handshake_buffer, sizeof(is_twl_handshake_buffer), buffer, 8, device_desc); + return ret; default: return 0; } @@ -640,6 +699,8 @@ int StopUsbCaptureDma(is_device_device_handlers* handlers, const is_device_usb_d return WriteNecMemU16(handlers, REG_USB_BIU_CONTROL_2, 0, device_desc); case IS_NITRO_CAPTURE_DEVICE: return SendReadPacket(handlers, IS_NITRO_CAP_CMD_ENABLE_CAP, IS_NITRO_CAPTURE_PACKET_TYPE_DMA_CONTROL, 0, NULL, 0, device_desc); + case IS_TWL_CAPTURE_DEVICE: + return SendWriteCommand(handlers, IS_TWL_CAP_CMD_STOP_FRAME_READS, NULL, 0, device_desc); default: return 0; } @@ -798,6 +859,46 @@ int ResetFullHardware(is_device_device_handlers* handlers, const is_device_usb_d } } +int AskFrameLengthPos(is_device_device_handlers* handlers, uint32_t* video_address, uint32_t* video_length, bool video_enabled, uint32_t* audio_address, uint32_t* audio_length, bool audio_enabled, const is_device_usb_device* device_desc) { + if(device_desc->device_type != IS_TWL_CAPTURE_DEVICE) + return 0; + is_twl_ask_capture_information_packet out_packet; + is_twl_capture_information_packet in_packet; + memset(&out_packet, 0, sizeof(out_packet)); + if(video_enabled) + out_packet.max_length_video = to_le((uint32_t)DEFAULT_MAX_LENGTH_IS_TWL_VIDEO); + if(audio_enabled) + out_packet.max_length_audio = to_le((uint32_t)DEFAULT_MAX_LENGTH_IS_TWL_AUDIO); + int ret = SendReadWriteCommand(handlers, IS_TWL_CAP_CMD_ASK_CAPTURE_INFORMATION, (uint8_t*)&out_packet, sizeof(out_packet), (uint8_t*)&in_packet, sizeof(in_packet), device_desc); + if(ret < 0) + return ret; + *video_length = from_le(in_packet.available_video_length); + *video_address = from_le(in_packet.video_start_address); + *audio_length = from_le(in_packet.available_audio_length); + *audio_address = from_le(in_packet.audio_start_address); + return ret; +} + +int SetLastFrameInfo(is_device_device_handlers* handlers, uint32_t video_address, uint32_t video_length, uint32_t audio_address, uint32_t audio_length, const is_device_usb_device* device_desc) { + if(device_desc->device_type != IS_TWL_CAPTURE_DEVICE) + return 0; + is_twl_capture_information_packet out_packet; + memset(&out_packet, 0, sizeof(out_packet)); + out_packet.available_video_length = to_le(video_length); + out_packet.video_start_address = to_le(video_address); + out_packet.available_audio_length = to_le(audio_length); + out_packet.audio_start_address = to_le(audio_address); + return SendWriteCommand(handlers, IS_TWL_CAP_CMD_SET_LAST_FRAME_INFORMATION, (uint8_t*)&out_packet, sizeof(out_packet), device_desc); +} + +int ReadFrame(is_device_device_handlers* handlers, uint8_t* buf, uint32_t address, uint32_t length, const is_device_usb_device* device_desc) { + if(device_desc->device_type != IS_TWL_CAPTURE_DEVICE) + return 0; + if(length == 0) + return 0; + return SendReadPacket(handlers, IS_TWL_CAP_CMD_GET_CAP, IS_TWL_PACKET_TYPE_FRAME, address, buf, length, device_desc); +} + int ReadFrame(is_device_device_handlers* handlers, uint8_t* buf, int length, const is_device_usb_device* device_desc) { // Maybe making this async would be better for lower end hardware... int num_bytes = 0; diff --git a/source/CaptureDeviceSpecific/ISDevices/usb_is_twl_acquisition_capture.cpp b/source/CaptureDeviceSpecific/ISDevices/usb_is_twl_acquisition_capture.cpp new file mode 100644 index 0000000..3441b6d --- /dev/null +++ b/source/CaptureDeviceSpecific/ISDevices/usb_is_twl_acquisition_capture.cpp @@ -0,0 +1,139 @@ +#include "devicecapture.hpp" +#include "usb_is_device_communications.hpp" +#include "usb_is_device_acquisition_general.hpp" +#include "usb_is_twl_acquisition_capture.hpp" + +// Code created by analyzing the USB packets sent and received by the IS TWL Capture device. + +#define RESET_TIMEOUT 4.0 +#define SLEEP_RESET_TIME_MS 2000 +#define DEFAULT_FRAME_TIME_MS 16.7 +#define SLEEP_FRAME_DIVIDER 4 + +static int process_frame_and_read(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, CaptureSpeedsType curr_capture_speed, std::chrono::time_point* clock_start, uint32_t &last_read_frame_index, uint32_t video_address, uint32_t video_length, uint32_t audio_address, uint32_t audio_length, bool &processed, float &last_frame_length) { + processed = false; + int multiplier = 1; + switch(curr_capture_speed) { + case CAPTURE_SPEEDS_HALF: + multiplier = 2; + break; + case CAPTURE_SPEEDS_THIRD: + multiplier = 3; + break; + case CAPTURE_SPEEDS_QUARTER: + multiplier = 4; + break; + default: + break; + } + int num_available_frames = multiplier; + if(num_available_frames < multiplier) + return 0; + processed = true; + int frame_next = num_available_frames - 1; + last_read_frame_index += num_available_frames; + video_address = video_address + (frame_next * sizeof(ISTWLCaptureVideoReceived)); + is_device_device_handlers* handlers = (is_device_device_handlers*)capture_data->handle; + const is_device_usb_device* usb_device_desc = (const is_device_usb_device*)capture_data->status.device.descriptor; + size_t video_processed_size = usb_is_device_get_video_in_size(curr_capture_type, IS_TWL_CAPTURE_DEVICE); + int ret = ReadFrame(handlers, (uint8_t*)&capture_buf->is_twl_capture_received.video_capture_in, 0, video_processed_size, usb_device_desc); + if(ret < 0) + return ret; + int audio_diff_from_max = usb_device_desc->max_audio_samples_size - audio_length; + if(audio_diff_from_max >= 0) + audio_diff_from_max = 0; + else { + audio_address -= audio_diff_from_max; + audio_length = usb_device_desc->max_audio_samples_size; + } + ret = ReadFrame(handlers, (uint8_t*)&capture_buf->is_twl_capture_received.audio_capture_in, audio_address, audio_length, 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); + last_frame_length = diff.count(); + output_to_thread(capture_data, capture_buf, curr_capture_type, clock_start, video_processed_size + (audio_length / sizeof(ISTWLCaptureAudioReceived)) * (sizeof(ISTWLCaptureAudioReceived) - sizeof(uint32_t))); + return 0; +} + +int initial_cleanup_twl_capture(const is_device_usb_device* usb_device_desc, is_device_device_handlers* handlers) { + //EndAcquisition(handlers, usb_device_desc, false, 0, CAPTURE_SCREENS_BOTH); + return LIBUSB_SUCCESS; +} + +int EndAcquisitionTWLCapture(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data) { + wait_all_is_device_transfers_done(capture_data, is_device_capture_recv_data); + return EndAcquisitionTWLCapture((const is_device_usb_device*)capture_data->status.device.descriptor, (is_device_device_handlers*)capture_data->handle); +} + +int EndAcquisitionTWLCapture(const is_device_usb_device* usb_device_desc, is_device_device_handlers* handlers) { + return StopUsbCaptureDma(handlers, usb_device_desc); +} + +void is_twl_acquisition_capture_main_loop(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data) { + is_device_device_handlers* handlers = (is_device_device_handlers*)capture_data->handle; + const is_device_usb_device* usb_device_desc = (const is_device_usb_device*)capture_data->status.device.descriptor; + bool is_acquisition_off = true; + uint32_t last_read_frame_index = 0; + CaptureScreensType curr_capture_type = capture_data->status.capture_type; + CaptureSpeedsType curr_capture_speed = capture_data->status.capture_speed; + bool audio_enabled = true; + std::chrono::time_point clock_last_frame = std::chrono::high_resolution_clock::now(); + float last_frame_length = 0.0; + int ret = 0; + uint32_t video_address = 0; + uint32_t video_length = 0; + uint32_t audio_address = 0; + uint32_t audio_length = 0; + ret = StartUsbCaptureDma(handlers, usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Capture Start: Failed"); + return; + } + /* + ret = AskFrameLengthPos(handlers, &video_address, &video_length, true, &audio_address, &audio_length, audio_enabled, usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Initial Frame Info Read: Failed"); + return; + } + ret = SetLastFrameInfo(handlers, video_address, video_length, audio_address, audio_length, usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Initial Frame Info Set: Failed"); + return; + } + */ + default_sleep(DEFAULT_FRAME_TIME_MS / SLEEP_FRAME_DIVIDER); + + while(capture_data->status.connected && capture_data->status.running) { + bool processed = false; + /* + ret = AskFrameLengthPos(handlers, &video_address, &video_length, true, &audio_address, &audio_length, audio_enabled, usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Frame Info Read: Failed"); + return; + } + if(video_length > 0) { + */ + ret = process_frame_and_read(capture_data, &is_device_capture_recv_data[0].buffer, curr_capture_type, curr_capture_speed, &clock_last_frame, last_read_frame_index, video_address, video_length, audio_address, audio_length, processed, last_frame_length); + if(ret < 0) { + capture_error_print(true, capture_data, "Frame Read: Error " + std::to_string(ret)); + return; + } + /* + if(processed) { + ret = SetLastFrameInfo(handlers, video_address, video_length, audio_address, audio_length, usb_device_desc); + if(ret < 0) { + capture_error_print(true, capture_data, "Frame Info Set: Failed"); + return; + } + } + } + */ + if(last_frame_length > 0) + default_sleep((last_frame_length * 1000) / SLEEP_FRAME_DIVIDER); + else + default_sleep(DEFAULT_FRAME_TIME_MS / SLEEP_FRAME_DIVIDER); + } + if(!is_acquisition_off) + EndAcquisition(capture_data, is_device_capture_recv_data, true, 0, curr_capture_type); +} diff --git a/source/conversions.cpp b/source/conversions.cpp index cd2588e..ad23d41 100755 --- a/source/conversions.cpp +++ b/source/conversions.cpp @@ -25,6 +25,32 @@ struct deinterleaved_ds_pixels_le { uint64_t fourth : 16; }; +struct twl_16bit_pixels { + uint16_t first_r : 5; + uint16_t first_g : 6; + uint16_t first_b : 5; + uint16_t second_r : 5; + uint16_t second_g : 6; + uint16_t second_b : 5; + uint16_t third_r : 5; + uint16_t third_g : 6; + uint16_t third_b : 5; + uint16_t fourth_r : 5; + uint16_t fourth_g : 6; + uint16_t fourth_b : 5; +}; + +struct twl_2bit_pixels { + uint8_t first_r : 1; + uint8_t first_b : 1; + uint8_t second_r : 1; + uint8_t second_b : 1; + uint8_t third_r : 1; + uint8_t third_b : 1; + uint8_t fourth_r : 1; + uint8_t fourth_b : 1; +}; + static inline void convertVideoToOutputChunk(RGB83DSVideoInputData *p_in, VideoOutputData *p_out, int iters, int start_in, int start_out) { memcpy(&p_out->screen_data[start_out], &p_in->screen_data[start_in], iters * 3); } @@ -171,17 +197,63 @@ static void usb_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_o usb_oldDSconvertVideoToOutput(&p_in->usb_received_old_ds, p_out, is_big_endian); } +inline static uint8_t to_8_bit_6(uint8_t data) { + return (data << 2) | (data >> 4); +} + +inline static void to_8_bit_6(uint8_t* out, uint8_t* in) { + out[0] = to_8_bit_6(in[0]); + out[1] = to_8_bit_6(in[1]); + out[2] = to_8_bit_6(in[2]); +} + static void usb_is_device_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureStatus* status, CaptureScreensType capture_type) { - int num_pixels = usb_is_device_get_video_in_size(status) / 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; + bool is_nitro = true; + #ifdef USE_IS_DEVICES_USB + is_nitro = is_device_is_nitro(&status->device); + #endif + if(is_nitro) { + int num_pixels = usb_is_device_get_video_in_size(status) / 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); + memcpy(p_out->screen_data[out_start_pos], p_in->is_nitro_capture_received.video_in.screen_data, num_pixels * 3); + return; + } + ISTWLCaptureVideoInputData* data = &p_in->is_twl_capture_received.video_capture_in.video_in; + twl_16bit_pixels* data_16_bit = (twl_16bit_pixels*)data->screen_data; + twl_2bit_pixels* data_2_bit = (twl_2bit_pixels*)data->bit_6_rb_screen_data; + int num_pixels = IN_VIDEO_SIZE_DS; + const int num_pixels_struct = sizeof(twl_16bit_pixels) / sizeof(uint16_t); + const int num_screens = 2; + for(int i = 0; i < HEIGHT_DS; i++) { + for(int j = 0; j < num_screens; j++) { + size_t out_pos = ((num_screens - 1 - j) * (WIDTH_DS * HEIGHT_DS)) + (i * WIDTH_DS); + for(int u = 0; u < (WIDTH_DS / num_pixels_struct); u++) { + uint8_t pixels[num_pixels_struct][3]; + size_t index = (((i * num_screens * WIDTH_DS) + (j * WIDTH_DS)) / num_pixels_struct) + u; + pixels[0][0] = (data_16_bit[index].first_r << 1) | (data_2_bit[index].first_r); + pixels[0][1] = data_16_bit[index].first_g; + pixels[0][2] = (data_16_bit[index].first_b << 1) | (data_2_bit[index].first_b); + pixels[1][0] = (data_16_bit[index].second_r << 1) | (data_2_bit[index].second_r); + pixels[1][1] = data_16_bit[index].second_g; + pixels[1][2] = (data_16_bit[index].second_b << 1) | (data_2_bit[index].second_b); + pixels[2][0] = (data_16_bit[index].third_r << 1) | (data_2_bit[index].third_r); + pixels[2][1] = data_16_bit[index].third_g; + pixels[2][2] = (data_16_bit[index].third_b << 1) | (data_2_bit[index].third_b); + pixels[3][0] = (data_16_bit[index].fourth_r << 1) | (data_2_bit[index].fourth_r); + pixels[3][1] = data_16_bit[index].fourth_g; + pixels[3][2] = (data_16_bit[index].fourth_b << 1) | (data_2_bit[index].fourth_b); + for(int k = 0; k < num_pixels_struct; k++) + to_8_bit_6(p_out->screen_data[out_pos + (u * num_pixels_struct) + k], pixels[k]); + } + } } - if((capture_type == CAPTURE_SCREENS_BOTTOM) || (capture_type == CAPTURE_SCREENS_TOP)) - memset(p_out->screen_data[out_clear_pos], 0, num_pixels * 3); - memcpy(p_out->screen_data[out_start_pos], p_in->is_nitro_capture_received.video_in.screen_data, num_pixels * 3); } bool convertVideoToOutput(VideoOutputData *p_out, const bool is_big_endian, CaptureDataSingleBuffer* data_buffer, CaptureStatus* status) { @@ -240,6 +312,16 @@ bool convertAudioToOutput(std::int16_t *p_out, uint64_t &n_samples, const bool i base_ptr = (uint8_t*)p_in->usb_received_3ds_3d.audio_data; } #endif + #ifdef USE_IS_DEVICES_USB + if(status->device.cc_type == CAPTURE_CONN_IS_NITRO) { + base_ptr = (uint8_t*)p_in->is_twl_capture_received.audio_capture_in; + size_t size_real = n_samples * 2; + size_t size_packet_converted = sizeof(ISTWLCaptureAudioReceived) - sizeof(uint32_t); + size_t num_packets = size_real / size_packet_converted; + for(int i = 0; i < num_packets; i++) + memcpy(base_ptr + (i * size_packet_converted), ((uint8_t*)&p_in->is_twl_capture_received.audio_capture_in[i]) + sizeof(uint32_t), size_packet_converted); + } + #endif if(base_ptr == NULL) return false; if(!is_big_endian)