Really basic frame read implementation for IS TWL Capture
Some checks are pending
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:linux32 flags:32 name:Linux GCC 32 os:ubuntu-latest]) (push) Waiting to run
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:linux64 flags:64 name:Linux GCC x64 os:ubuntu-latest]) (push) Waiting to run
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:linuxarm32 flags:arm32 name:Linux GCC ARM 32 os:ubuntu-latest]) (push) Waiting to run
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:linuxarm64 flags:arm64 name:Linux GCC ARM 64 os:ubuntu-latest]) (push) Waiting to run
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:macos name:macOS Apple Silicon os:macos-14]) (push) Waiting to run
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:win32 flags:-A Win32 name:Windows VS2022 Win32 os:windows-2022]) (push) Waiting to run
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:win64 flags:-A x64 name:Windows VS2022 x64 os:windows-2022]) (push) Waiting to run
CD / ${{ matrix.platform.name }} ${{ matrix.config.name }} (map[flags:-DBUILD_SHARED_LIBS=FALSE name:Static], map[artifact_name:winarm64 flags:-A ARM64 name:Windows VS2022 ARM os:windows-2022]) (push) Waiting to run
CD / Create Pi Mono Setup (push) Blocked by required conditions
CD / Publishing (push) Blocked by required conditions

This commit is contained in:
Lorenzooone 2024-12-14 23:38:05 +01:00
parent 5f152f4913
commit 4bd8a189fb
10 changed files with 388 additions and 22 deletions

View File

@ -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"

View File

@ -0,0 +1 @@
"R¤ŠÅJV1åiTÁE•ªè<C2AA>$°bÄÌÍÔ.§÷î‡L7mçªf€Ö«rVÄöå&65™'Eö´¥eYºôð@»´BØ<dý<64>>»x?óþ£¼;}Ûº‚àÒ»:sš$ò1¹x<C2B9>Ga÷Ý´¹…]©ðð\˸c @ œ!?H_Oÿï_Œ?mÞ©~À+>žþåѽHѧ}—ÙÂAeYòÓH<O_N ø÷‚

View File

@ -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<std::chrono::high_resolution_clock> &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<std::chrono::high_resolution_clock>* 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);

View File

@ -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);

View File

@ -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

View File

@ -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 {

View File

@ -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 <libusb.h>
@ -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<CaptureDevice>& 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<CaptureDevice>& 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<std::chrono::high_resolution_clock>* clock_start) {
void output_to_thread(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point<std::chrono::high_resolution_clock>* 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<double> 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<std::chrono::high_resolution_clock>* 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<std::chrono::high_resolution_clock> &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;
}

View File

@ -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 <libusb.h>
#include <cstring>
@ -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;

View File

@ -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<std::chrono::high_resolution_clock>* 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<double> 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<std::chrono::high_resolution_clock> 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);
}

View File

@ -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)