mirror of
https://github.com/Lorenzooone/cc3dsfs.git
synced 2026-03-21 17:55:00 -05:00
Add really basic IS Nitro Emulator support
This commit is contained in:
parent
098cdf95fd
commit
718294ba02
|
|
@ -43,7 +43,7 @@ endif()
|
|||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(SFML
|
||||
GIT_REPOSITORY https://github.com/Lorenzooone/SFML.git
|
||||
GIT_REPOSITORY https://github.com/SFML/SFML.git
|
||||
GIT_TAG macos_fullscreen_v2.6)
|
||||
|
||||
FetchContent_Declare(libusb1
|
||||
|
|
@ -301,7 +301,7 @@ execute_process(COMMAND ${CMAKE_COMMAND} --build ${TOOLS_DATA_DIR})
|
|||
|
||||
set(OUTPUT_NAME cc3dsfs)
|
||||
|
||||
add_executable(${OUTPUT_NAME} source/cc3dsfs.cpp source/utils.cpp source/audio_data.cpp source/audio.cpp source/frontend.cpp source/TextRectangle.cpp source/WindowScreen.cpp source/WindowScreen_Menu.cpp source/3dscapture_ftd3.cpp source/dscapture_ftd2.cpp source/usb_ds_3ds_capture.cpp source/devicecapture.cpp source/conversions.cpp source/ExtraButtons.cpp source/Menus/ConnectionMenu.cpp source/Menus/OptionSelectionMenu.cpp source/Menus/MainMenu.cpp source/Menus/VideoMenu.cpp source/Menus/CropMenu.cpp source/Menus/PARMenu.cpp source/Menus/RotationMenu.cpp source/Menus/OffsetMenu.cpp source/Menus/AudioMenu.cpp source/Menus/BFIMenu.cpp source/Menus/RelativePositionMenu.cpp source/Menus/ResolutionMenu.cpp source/Menus/FileConfigMenu.cpp source/Menus/ExtraSettingsMenu.cpp source/Menus/StatusMenu.cpp source/Menus/LicenseMenu.cpp source/WindowCommands.cpp source/Menus/ShortcutMenu.cpp source/Menus/ActionSelectionMenu.cpp source/Menus/ScalingRatioMenu.cpp ${TOOLS_DATA_DIR}/font_ttf.cpp)
|
||||
add_executable(${OUTPUT_NAME} source/cc3dsfs.cpp source/utils.cpp source/audio_data.cpp source/audio.cpp source/frontend.cpp source/TextRectangle.cpp source/WindowScreen.cpp source/WindowScreen_Menu.cpp source/3dscapture_ftd3.cpp source/dscapture_ftd2.cpp source/usb_ds_3ds_capture.cpp source/devicecapture.cpp source/conversions.cpp source/ExtraButtons.cpp source/Menus/ConnectionMenu.cpp source/Menus/OptionSelectionMenu.cpp source/Menus/MainMenu.cpp source/Menus/VideoMenu.cpp source/Menus/CropMenu.cpp source/Menus/PARMenu.cpp source/Menus/RotationMenu.cpp source/Menus/OffsetMenu.cpp source/Menus/AudioMenu.cpp source/Menus/BFIMenu.cpp source/Menus/RelativePositionMenu.cpp source/Menus/ResolutionMenu.cpp source/Menus/FileConfigMenu.cpp source/Menus/ExtraSettingsMenu.cpp source/Menus/StatusMenu.cpp source/Menus/LicenseMenu.cpp source/WindowCommands.cpp source/Menus/ShortcutMenu.cpp source/Menus/ActionSelectionMenu.cpp source/Menus/ScalingRatioMenu.cpp source/usb_is_nitro.cpp source/usb_is_nitro_capture.cpp source/usb_generic.cpp ${TOOLS_DATA_DIR}/font_ttf.cpp)
|
||||
add_dependencies(${OUTPUT_NAME} FTD3XX_BUILD_PROJECT FTD2XX_BUILD_PROJECT)
|
||||
target_link_libraries(${OUTPUT_NAME} PRIVATE sfml-graphics sfml-audio sfml-window sfml-system usb-1.0 ${ftd3xx_BINARY_DIR}/${FTD3XX_SUBFOLDER}/${FTD3XX_LIB} ${ftd2xx_BINARY_DIR}/${FTD2XX_SUBFOLDER}/${FTD2XX_LIB} ${EXTRA_LIBRARIES})
|
||||
target_link_directories(${OUTPUT_NAME} PRIVATE ${ftd3xx_BINARY_DIR}/${FTD3XX_SUBFOLDER} ${ftd2xx_BINARY_DIR}/${FTD2XX_SUBFOLDER})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
cc3dsfs is a multi-platform capture and display program for [3dscapture's](https://3dscapture.com/) N3DSXL, 3DS and DS (old) capture boards written in C++.
|
||||
The main goal is to offer the ability to use the Capture Card with a TV, via fullscreen mode.
|
||||
|
||||
Tentative IS Nitro Emulator support (for newer revisions) is also present. Though results may vary (and the amount of video delay may be significantly higher).
|
||||
|
||||
## Features
|
||||
|
||||
- Performance-focused design, with low latency for both audio and video (measured to oscillate between 1 and 2 frames on 120hz displays).
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#define EXTRA_DATA_BUFFER_USB_SIZE (1 << 9)
|
||||
#define EXTRA_DATA_BUFFER_FTD3XX_SIZE (1 << 10)
|
||||
|
||||
enum CaptureConnectionType { CAPTURE_CONN_FTD3, CAPTURE_CONN_USB, CAPTURE_CONN_FTD2 };
|
||||
enum CaptureConnectionType { CAPTURE_CONN_FTD3, CAPTURE_CONN_USB, CAPTURE_CONN_FTD2, CAPTURE_CONN_IS_NITRO };
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
|
|
@ -44,6 +44,10 @@ struct PACKED USBOldDSVideoInputData {
|
|||
USBOldDSPixelData screen_data[IN_VIDEO_SIZE_DS];
|
||||
};
|
||||
|
||||
struct PACKED ISNitroEmulatorVideoInputData {
|
||||
uint8_t screen_data[IN_VIDEO_SIZE_DS][3];
|
||||
};
|
||||
|
||||
struct PACKED FTD3_3DSCaptureReceived {
|
||||
RGB83DSVideoInputData video_in;
|
||||
uint16_t audio_data[N3DSXL_SAMPLES_IN];
|
||||
|
|
@ -81,6 +85,10 @@ struct PACKED USBOldDSCaptureReceived {
|
|||
USBOldDSFrameInfo frameinfo;
|
||||
};
|
||||
|
||||
struct PACKED ISNitroCaptureReceived {
|
||||
ISNitroEmulatorVideoInputData video_in;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
union CaptureReceived {
|
||||
|
|
@ -89,6 +97,7 @@ union CaptureReceived {
|
|||
USB3DSCaptureReceived usb_received_3ds;
|
||||
USB3DSCaptureReceived_3D usb_received_3ds_3d;
|
||||
USBOldDSCaptureReceived usb_received_old_ds;
|
||||
ISNitroCaptureReceived is_nitro_capture_received;
|
||||
};
|
||||
|
||||
struct CaptureDevice {
|
||||
|
|
|
|||
|
|
@ -313,9 +313,9 @@ bool load_screen_info(std::string key, std::string value, std::string base, Scre
|
|||
std::string save_screen_info(std::string base, const ScreenInfo &info);
|
||||
void get_par_size(int &width, int &height, float multiplier_factor, const PARData *correction_factor);
|
||||
float get_par_mult_factor(float width, float height, float max_width, float max_height, const PARData *correction_factor, bool is_rotated);
|
||||
void default_sleep(int wanted_ms = -1);
|
||||
void update_output(FrontendData* frontend_data, double frame_time = 0.0, VideoOutputData *out_buf = NULL);
|
||||
void update_connected_3ds_ds(FrontendData* frontend_data, const CaptureDevice &old_cc_device, const CaptureDevice &new_cc_device);
|
||||
void screen_display_thread(WindowScreen *screen);
|
||||
std::string get_name_non_int_mode(NonIntegerScalingModes input);
|
||||
void default_sleep(int wanted_ms = -1);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include "capture_structs.hpp"
|
||||
#include "display_structs.hpp"
|
||||
|
||||
#define USE_USB
|
||||
#define USE_DS_3DS_USB
|
||||
|
||||
void list_devices_usb_ds_3ds(std::vector<CaptureDevice> &devices_list);
|
||||
bool connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device);
|
||||
|
|
@ -15,7 +15,7 @@ void usb_capture_main_loop(CaptureData* capture_data);
|
|||
void usb_capture_cleanup(CaptureData* capture_data);
|
||||
void usb_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureDevice* capture_device, bool enabled_3d);
|
||||
uint64_t usb_get_video_in_size(CaptureData* capture_data);
|
||||
void usb_init();
|
||||
void usb_close();
|
||||
void usb_ds_3ds_init();
|
||||
void usb_ds_3ds_close();
|
||||
|
||||
#endif
|
||||
|
|
|
|||
11
include/usb_generic.hpp
Normal file
11
include/usb_generic.hpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef __USB_GENERIC_HPP
|
||||
#define __USB_GENERIC_HPP
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
void usb_init();
|
||||
void usb_close();
|
||||
bool usb_is_initialized();
|
||||
libusb_context* get_usb_ctx();
|
||||
|
||||
#endif
|
||||
62
include/usb_is_nitro.hpp
Normal file
62
include/usb_is_nitro.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef __USB_IS_NITRO_HPP
|
||||
#define __USB_IS_NITRO_HPP
|
||||
|
||||
#include <libusb.h>
|
||||
#include <vector>
|
||||
#include "utils.hpp"
|
||||
|
||||
#define IS_NITRO_REAL_SERIAL_NUMBER_SIZE 10
|
||||
|
||||
enum is_nitro_forward_enable_values {
|
||||
IS_NITRO_FORWARD_ENABLE_DISABLE = 0,
|
||||
IS_NITRO_FORWARD_ENABLE_ENABLE = 1,
|
||||
IS_NITRO_FORWARD_ENABLE_RESTART = 2,
|
||||
};
|
||||
|
||||
enum is_nitro_forward_config_values {
|
||||
IS_NITRO_FORWARD_CONFIG_COLOR_RGB24 = 0,
|
||||
IS_NITRO_FORWARD_CONFIG_MODE_BOTH = 0,
|
||||
IS_NITRO_FORWARD_CONFIG_MODE_TOP = 4,
|
||||
IS_NITRO_FORWARD_CONFIG_MODE_BOTTOM = 8,
|
||||
IS_NITRO_FORWARD_CONFIG_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 {
|
||||
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;
|
||||
};
|
||||
|
||||
extern const is_nitro_usb_device usb_is_nitro_desc;
|
||||
|
||||
/*
|
||||
int SendReadCommand(libusb_device_handle *handle, uint16_t command, uint8_t* buf, int length);
|
||||
int SendWriteCommand(libusb_device_handle *handle, uint16_t command, uint8_t* buf, int length);
|
||||
int ReadNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_size, uint8_t* buf, int count);
|
||||
int ReadNecMemU16(libusb_device_handle *handle, uint32_t address, uint16_t* out);
|
||||
int ReadNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t* out);
|
||||
int WriteNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_size, uint8_t* buf, int count);
|
||||
int WriteNecMemU16(libusb_device_handle *handle, uint32_t address, uint16_t value);
|
||||
int WriteNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t value);
|
||||
*/
|
||||
|
||||
int DisableLca2(libusb_device_handle *handle);
|
||||
int StartUsbCaptureDma(libusb_device_handle *handle);
|
||||
int StopUsbCaptureDma(libusb_device_handle *handle);
|
||||
int SetForwardFrameCount(libusb_device_handle *handle, uint16_t count);
|
||||
int GetFrameCounter(libusb_device_handle *handle, uint16_t* out);
|
||||
int GetDeviceSerial(libusb_device_handle *handle, uint8_t* buf);
|
||||
int UpdateFrameForwardConfig(libusb_device_handle *handle, uint8_t value);
|
||||
int UpdateFrameForwardEnable(libusb_device_handle *handle, uint8_t value);
|
||||
int ReadFrame(libusb_device_handle *handle, uint8_t* buf, int length);
|
||||
|
||||
#endif
|
||||
21
include/usb_is_nitro_capture.hpp
Normal file
21
include/usb_is_nitro_capture.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef __USB_IS_NITRO_CAPTURE_HPP
|
||||
#define __USB_IS_NITRO_CAPTURE_HPP
|
||||
|
||||
#include <vector>
|
||||
#include "utils.hpp"
|
||||
#include "hw_defs.hpp"
|
||||
#include "capture_structs.hpp"
|
||||
#include "display_structs.hpp"
|
||||
|
||||
#define USE_IS_NITRO_USB
|
||||
|
||||
void list_devices_is_nitro(std::vector<CaptureDevice> &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 usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureDevice* capture_device);
|
||||
uint64_t usb_is_nitro_emulator_get_video_in_size(CaptureData* capture_data);
|
||||
void usb_is_nitro_init();
|
||||
void usb_is_nitro_close();
|
||||
|
||||
#endif
|
||||
|
|
@ -29,6 +29,10 @@ std::string LayoutNameGenerator(int index);
|
|||
std::string LayoutPathGenerator(int index);
|
||||
std::string load_layout_name(int index, bool &success);
|
||||
bool is_big_endian(void);
|
||||
uint32_t to_le(uint32_t value);
|
||||
uint32_t to_be(uint32_t value);
|
||||
uint16_t to_le(uint16_t value);
|
||||
uint16_t to_be(uint16_t value);
|
||||
std::string get_float_str_decimals(float value, int decimals);
|
||||
|
||||
class ConsumerMutex {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "3dscapture_ftd3.hpp"
|
||||
#include "dscapture_ftd2.hpp"
|
||||
#include "usb_ds_3ds_capture.hpp"
|
||||
#include "usb_is_nitro_capture.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
|
@ -15,10 +16,14 @@ void convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, Capture
|
|||
if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD2)
|
||||
ftd2_convertVideoToOutput(p_in, p_out, capture_data->status.enabled_3d);
|
||||
#endif
|
||||
#ifdef USE_USB
|
||||
#ifdef USE_DS_3DS_USB
|
||||
if(capture_data->status.device.cc_type == CAPTURE_CONN_USB)
|
||||
usb_convertVideoToOutput(p_in, p_out, &capture_data->status.device, capture_data->status.enabled_3d);
|
||||
#endif
|
||||
#ifdef USE_IS_NITRO_USB
|
||||
if(capture_data->status.device.cc_type == CAPTURE_CONN_IS_NITRO)
|
||||
usb_is_nitro_convertVideoToOutput(p_in, p_out, &capture_data->status.device);
|
||||
#endif
|
||||
}
|
||||
|
||||
void convertAudioToOutput(CaptureReceived *p_in, sf::Int16 *p_out, uint64_t n_samples, const bool is_big_endian, CaptureData* capture_data) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "3dscapture_ftd3.hpp"
|
||||
#include "dscapture_ftd2.hpp"
|
||||
#include "usb_ds_3ds_capture.hpp"
|
||||
#include "usb_is_nitro_capture.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
|
@ -16,9 +17,8 @@ static bool poll_connection_window_screen(WindowScreen *screen, int &chosen_inde
|
|||
chosen_index = screen->check_connection_menu_result();
|
||||
return true;
|
||||
}
|
||||
if(screen->close_capture()) {
|
||||
if(screen->close_capture())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -76,9 +76,12 @@ bool connect(bool print_failed, CaptureData* capture_data, FrontendData* fronten
|
|||
#ifdef USE_FTD2
|
||||
list_devices_ftd2(devices_list);
|
||||
#endif
|
||||
#ifdef USE_USB
|
||||
#ifdef USE_DS_3DS_USB
|
||||
list_devices_usb_ds_3ds(devices_list);
|
||||
#endif
|
||||
#ifdef USE_IS_NITRO_USB
|
||||
list_devices_is_nitro(devices_list);
|
||||
#endif
|
||||
|
||||
if(devices_list.size() <= 0) {
|
||||
capture_error_print(print_failed, capture_data, "No device was found");
|
||||
|
|
@ -100,10 +103,14 @@ bool connect(bool print_failed, CaptureData* capture_data, FrontendData* fronten
|
|||
if((devices_list[chosen_device].cc_type == CAPTURE_CONN_FTD2) && (!connect_ftd2(print_failed, capture_data, &devices_list[chosen_device])))
|
||||
return false;
|
||||
#endif
|
||||
#ifdef USE_USB
|
||||
#ifdef USE_DS_3DS_USB
|
||||
if((devices_list[chosen_device].cc_type == CAPTURE_CONN_USB) && (!connect_usb(print_failed, capture_data, &devices_list[chosen_device])))
|
||||
return false;
|
||||
#endif
|
||||
#ifdef USE_IS_NITRO_USB
|
||||
if((devices_list[chosen_device].cc_type == CAPTURE_CONN_IS_NITRO) && (!is_nitro_connect_usb(print_failed, capture_data, &devices_list[chosen_device])))
|
||||
return false;
|
||||
#endif
|
||||
update_connected_3ds_ds(frontend_data, capture_data->status.device, devices_list[chosen_device]);
|
||||
capture_data->status.device = devices_list[chosen_device];
|
||||
|
||||
|
|
@ -134,10 +141,14 @@ void captureCall(CaptureData* capture_data) {
|
|||
if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD2)
|
||||
ftd2_capture_main_loop(capture_data);
|
||||
#endif
|
||||
#ifdef USE_USB
|
||||
#ifdef USE_DS_3DS_USB
|
||||
if(capture_data->status.device.cc_type == CAPTURE_CONN_USB)
|
||||
usb_capture_main_loop(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);
|
||||
#endif
|
||||
|
||||
capture_data->status.close_success = false;
|
||||
capture_data->status.connected = false;
|
||||
|
|
@ -156,10 +167,14 @@ void captureCall(CaptureData* capture_data) {
|
|||
if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD2)
|
||||
ftd2_capture_cleanup(capture_data);
|
||||
#endif
|
||||
#ifdef USE_USB
|
||||
#ifdef USE_DS_3DS_USB
|
||||
if(capture_data->status.device.cc_type == CAPTURE_CONN_USB)
|
||||
usb_capture_cleanup(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);
|
||||
#endif
|
||||
|
||||
capture_data->status.close_success = false;
|
||||
capture_data->status.connected = false;
|
||||
|
|
@ -186,21 +201,31 @@ uint64_t get_video_in_size(CaptureData* capture_data) {
|
|||
if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD2)
|
||||
return ftd2_get_video_in_size(capture_data);
|
||||
#endif
|
||||
#ifdef USE_USB
|
||||
#ifdef USE_DS_3DS_USB
|
||||
if(capture_data->status.device.cc_type == CAPTURE_CONN_USB)
|
||||
return usb_get_video_in_size(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);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void capture_init() {
|
||||
#ifdef USE_USB
|
||||
usb_init();
|
||||
#ifdef USE_DS_3DS_USB
|
||||
usb_ds_3ds_init();
|
||||
#endif
|
||||
#ifdef USE_IS_NITRO_USB
|
||||
usb_is_nitro_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void capture_close() {
|
||||
#ifdef USE_USB
|
||||
usb_close();
|
||||
#ifdef USE_DS_3DS_USB
|
||||
usb_ds_3ds_close();
|
||||
#endif
|
||||
#ifdef USE_IS_NITRO_USB
|
||||
usb_is_nitro_close();
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -740,13 +740,6 @@ JoystickAction get_joystick_action(uint32_t joystickId, uint32_t joy_button) {
|
|||
return JOY_ACTION_NONE;
|
||||
}
|
||||
|
||||
void default_sleep(int wanted_ms) {
|
||||
if(wanted_ms < 0)
|
||||
sf::sleep(sf::milliseconds(1000/USB_CHECKS_PER_SECOND));
|
||||
else
|
||||
sf::sleep(sf::milliseconds(wanted_ms));
|
||||
}
|
||||
|
||||
void update_output(FrontendData* frontend_data, double frame_time, VideoOutputData *out_buf) {
|
||||
if(frontend_data->reload) {
|
||||
frontend_data->top_screen->reload();
|
||||
|
|
@ -793,6 +786,13 @@ void update_connected_3ds_ds(FrontendData* frontend_data, const CaptureDevice &o
|
|||
}
|
||||
}
|
||||
|
||||
void default_sleep(int wanted_ms) {
|
||||
if(wanted_ms < 0)
|
||||
sf::sleep(sf::milliseconds(1000/USB_CHECKS_PER_SECOND));
|
||||
else
|
||||
sf::sleep(sf::milliseconds(wanted_ms));
|
||||
}
|
||||
|
||||
void screen_display_thread(WindowScreen *screen) {
|
||||
screen->display_thread();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "usb_ds_3ds_capture.hpp"
|
||||
#include "devicecapture.hpp"
|
||||
#include "usb_generic.hpp"
|
||||
|
||||
#include <libusb.h>
|
||||
#include <cstring>
|
||||
|
|
@ -73,9 +74,6 @@ static const usb_device usb_old_ds_desc = {
|
|||
.i2caddr_3dsconfig = 0, .bitstream_3dscfg_ver = 0
|
||||
};
|
||||
|
||||
static bool usb_initialized = false;
|
||||
static libusb_context *usb_ctx = NULL; // libusb session context
|
||||
|
||||
// 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);
|
||||
|
|
@ -129,6 +127,18 @@ static const usb_device* get_usb_device_desc(CaptureData* capture_data) {
|
|||
return _get_usb_device_desc(&capture_data->status.device);
|
||||
}
|
||||
|
||||
static std::string get_serial(libusb_device_handle *handle, libusb_device_descriptor *usb_descriptor, int &curr_serial_extra_id) {
|
||||
uint8_t data[SERIAL_NUMBER_SIZE];
|
||||
std::string serial_str = std::to_string(curr_serial_extra_id);
|
||||
if(libusb_get_string_descriptor_ascii(handle, usb_descriptor->iSerialNumber, data, REAL_SERIAL_NUMBER_SIZE-1) >= 0) {
|
||||
data[REAL_SERIAL_NUMBER_SIZE] = '\0';
|
||||
serial_str = std::string((const char*)data);
|
||||
}
|
||||
else
|
||||
curr_serial_extra_id += 1;
|
||||
return serial_str;
|
||||
}
|
||||
|
||||
static bool insert_device(std::vector<CaptureDevice> &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];
|
||||
|
|
@ -137,13 +147,7 @@ static bool insert_device(std::vector<CaptureDevice> &devices_list, const usb_de
|
|||
int result = libusb_open(usb_device, &handle);
|
||||
if(result || (handle == NULL))
|
||||
return true;
|
||||
std::string serial_str = std::to_string(curr_serial_extra_id);
|
||||
if(libusb_get_string_descriptor_ascii(handle, usb_descriptor->iSerialNumber, data, REAL_SERIAL_NUMBER_SIZE-1) >= 0) {
|
||||
data[REAL_SERIAL_NUMBER_SIZE] = '\0';
|
||||
serial_str = std::string((const char*)data);
|
||||
}
|
||||
else
|
||||
curr_serial_extra_id += 1;
|
||||
std::string serial_str = get_serial(handle, usb_descriptor, curr_serial_extra_id);
|
||||
if(usb_device_desc->is_3ds)
|
||||
devices_list.emplace_back(serial_str, "3DS", CAPTURE_CONN_USB, true, capture_get_has_3d(handle, usb_device_desc), true, HEIGHT_3DS, TOP_WIDTH_3DS + BOT_WIDTH_3DS, O3DS_SAMPLES_IN, usb_device_desc->bpp, 90, 0, 0, TOP_WIDTH_3DS, 0);
|
||||
else {
|
||||
|
|
@ -160,10 +164,10 @@ static bool insert_device(std::vector<CaptureDevice> &devices_list, const usb_de
|
|||
}
|
||||
|
||||
static libusb_device_handle* usb_find_by_serial_number(const usb_device* usb_device_desc, std::string &serial_number) {
|
||||
if(!usb_initialized)
|
||||
if(!usb_is_initialized())
|
||||
return NULL;
|
||||
libusb_device **usb_devices;
|
||||
int num_devices = libusb_get_device_list(usb_ctx, &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;
|
||||
|
||||
|
|
@ -179,13 +183,7 @@ static libusb_device_handle* usb_find_by_serial_number(const usb_device* usb_dev
|
|||
result = libusb_open(usb_devices[i], &handle);
|
||||
if(result || (handle == NULL))
|
||||
continue;
|
||||
std::string device_serial_number = std::to_string(curr_serial_extra_id);
|
||||
if(libusb_get_string_descriptor_ascii(handle, usb_descriptor.iSerialNumber, data, REAL_SERIAL_NUMBER_SIZE-1) >= 0) {
|
||||
data[REAL_SERIAL_NUMBER_SIZE] = '\0';
|
||||
device_serial_number = std::string((const char*)data);
|
||||
}
|
||||
else
|
||||
curr_serial_extra_id += 1;
|
||||
std::string device_serial_number = get_serial(handle, &usb_descriptor, curr_serial_extra_id);
|
||||
if(serial_number == device_serial_number) {
|
||||
final_handle = handle;
|
||||
break;
|
||||
|
|
@ -319,10 +317,10 @@ static void usb_3DSconvertVideoToOutput(USB3DSCaptureReceived *p_in, VideoOutput
|
|||
}
|
||||
|
||||
void list_devices_usb_ds_3ds(std::vector<CaptureDevice> &devices_list) {
|
||||
if(!usb_initialized)
|
||||
if(!usb_is_initialized())
|
||||
return;
|
||||
libusb_device **usb_devices;
|
||||
int num_devices = libusb_get_device_list(usb_ctx, &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;
|
||||
|
|
@ -342,7 +340,7 @@ void list_devices_usb_ds_3ds(std::vector<CaptureDevice> &devices_list) {
|
|||
}
|
||||
|
||||
bool connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device) {
|
||||
if(!usb_initialized)
|
||||
if(!usb_is_initialized())
|
||||
return false;
|
||||
const usb_device* usb_device_desc = _get_usb_device_desc(device);
|
||||
libusb_device_handle *dev = usb_find_by_serial_number(usb_device_desc, device->serial_number);
|
||||
|
|
@ -378,7 +376,7 @@ uint64_t usb_get_video_in_size(CaptureData* capture_data) {
|
|||
}
|
||||
|
||||
void usb_capture_main_loop(CaptureData* capture_data) {
|
||||
if(!usb_initialized)
|
||||
if(!usb_is_initialized())
|
||||
return;
|
||||
const usb_device* usb_device_desc = get_usb_device_desc(capture_data);
|
||||
int inner_curr_in = 0;
|
||||
|
|
@ -429,14 +427,14 @@ void usb_capture_main_loop(CaptureData* capture_data) {
|
|||
}
|
||||
|
||||
void usb_capture_cleanup(CaptureData* capture_data) {
|
||||
if(!usb_initialized)
|
||||
if(!usb_is_initialized())
|
||||
return;
|
||||
const usb_device* usb_device_desc = get_usb_device_desc(capture_data);
|
||||
capture_end((libusb_device_handle*)capture_data->handle, usb_device_desc);
|
||||
}
|
||||
|
||||
void usb_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureDevice* capture_device, bool enabled_3d) {
|
||||
if(!usb_initialized)
|
||||
if(!usb_is_initialized())
|
||||
return;
|
||||
if(capture_device->is_3ds) {
|
||||
if(!enabled_3d)
|
||||
|
|
@ -446,22 +444,11 @@ void usb_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, Cap
|
|||
usb_oldDSconvertVideoToOutput(&p_in->usb_received_old_ds, p_out);
|
||||
}
|
||||
|
||||
void usb_init() {
|
||||
if(usb_initialized)
|
||||
return;
|
||||
int result = libusb_init(&usb_ctx); // open session
|
||||
if (result < 0) {
|
||||
usb_ctx = NULL;
|
||||
usb_initialized = false;
|
||||
return;
|
||||
}
|
||||
usb_initialized = true;
|
||||
void usb_ds_3ds_init() {
|
||||
return usb_init();
|
||||
}
|
||||
|
||||
void usb_close() {
|
||||
if(usb_initialized)
|
||||
libusb_exit(usb_ctx);
|
||||
usb_ctx = NULL;
|
||||
usb_initialized = false;
|
||||
void usb_ds_3ds_close() {
|
||||
usb_close();
|
||||
}
|
||||
|
||||
|
|
|
|||
33
source/usb_generic.cpp
Normal file
33
source/usb_generic.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#include "usb_generic.hpp"
|
||||
|
||||
static bool usb_initialized = false;
|
||||
static libusb_context* usb_ctx = NULL; // libusb session context
|
||||
|
||||
void usb_init() {
|
||||
if(usb_initialized)
|
||||
return;
|
||||
int result = libusb_init(&usb_ctx); // open session
|
||||
if (result < 0) {
|
||||
usb_ctx = NULL;
|
||||
usb_initialized = false;
|
||||
return;
|
||||
}
|
||||
usb_initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
void usb_close() {
|
||||
if(usb_initialized) {
|
||||
libusb_exit(usb_ctx);
|
||||
}
|
||||
usb_ctx = NULL;
|
||||
usb_initialized = false;
|
||||
}
|
||||
|
||||
bool usb_is_initialized() {
|
||||
return usb_initialized;
|
||||
}
|
||||
|
||||
libusb_context* get_usb_ctx() {
|
||||
return usb_ctx;
|
||||
}
|
||||
299
source/usb_is_nitro.cpp
Normal file
299
source/usb_is_nitro.cpp
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
#include "frontend.hpp"
|
||||
#include "usb_is_nitro.hpp"
|
||||
|
||||
#include <libusb.h>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
// 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 USB_PACKET_LIMIT 0x2000
|
||||
|
||||
#define REG_USB_DMA_CONTROL_2 0x0C000028
|
||||
#define REG_USB_BIU_CONTROL_2 0x0C0000A4
|
||||
|
||||
enum is_nitro_packet_dir {
|
||||
IS_NITRO_PACKET_DIR_WRITE = 0x10,
|
||||
IS_NITRO_PACKET_DIR_READ = 0x11
|
||||
};
|
||||
|
||||
enum is_nitro_packet_type {
|
||||
IS_NITRO_PACKET_TYPE_COMMAND = 0,
|
||||
IS_NITRO_PACKET_TYPE_EMU_MEMORY = 1,
|
||||
IS_NITRO_PACKET_TYPE_AGB_SRAM = 2,
|
||||
IS_NITRO_PACKET_TYPE_CAPTURE = 3,
|
||||
IS_NITRO_PACKET_TYPE_AGB_CART_ROM = 4,
|
||||
IS_NITRO_PACKET_TYPE_AGB_BUS2 = 5
|
||||
};
|
||||
|
||||
enum is_nitro_command {
|
||||
IS_NITRO_CMD_GET_SERIAL = 0x13,
|
||||
IS_NITRO_CMD_READ_NEC_MEM = 0x17,
|
||||
IS_NITRO_CMD_WRITE_NEC_MEM = 0x24,
|
||||
IS_NITRO_CMD_SET_READ_NEC_MEM = 0x25,
|
||||
IS_NITRO_CMD_AD = 0xAD,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PACKED is_nitro_nec_packet_header {
|
||||
uint8_t command;
|
||||
uint8_t unit_size;
|
||||
uint16_t count;
|
||||
uint32_t address;
|
||||
};
|
||||
|
||||
struct PACKED is_nitro_packet_header {
|
||||
uint16_t command;
|
||||
uint8_t direction;
|
||||
uint8_t type;
|
||||
uint32_t address;
|
||||
uint32_t length;
|
||||
uint32_t padding;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
const is_nitro_usb_device usb_is_nitro_desc = {
|
||||
.vid = 0x0f6e, .pid = 0x0404,
|
||||
.default_config = 1, .default_interface = 0,
|
||||
.bulk_timeout = 500,
|
||||
.ep2_in = 2 | LIBUSB_ENDPOINT_IN, .ep1_out = 1 | LIBUSB_ENDPOINT_OUT,
|
||||
.product_id = 2, .manufacturer_id = 1
|
||||
};
|
||||
|
||||
static void fix_endianness_header(is_nitro_packet_header* header) {
|
||||
header->command = to_le(header->command);
|
||||
header->address = to_le(header->address);
|
||||
header->length = to_le(header->length);
|
||||
}
|
||||
|
||||
static void fix_endianness_header(is_nitro_nec_packet_header* header) {
|
||||
header->count = to_le(header->count);
|
||||
header->address = to_le(header->address);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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 SendWritePacket(libusb_device_handle *handle, uint16_t command, is_nitro_packet_type type, uint32_t address, uint8_t* buf, int length) {
|
||||
is_nitro_packet_header header;
|
||||
uint8_t single_usb_packet[USB_PACKET_LIMIT];
|
||||
int single_packet_covered_size = USB_PACKET_LIMIT - sizeof(header);
|
||||
int num_iters = (length + single_packet_covered_size - 1) / single_packet_covered_size;
|
||||
if(!num_iters)
|
||||
num_iters = 1;
|
||||
for(int i = 0; i < num_iters; i++) {
|
||||
int transfer_size = length - (i * single_packet_covered_size);
|
||||
if(transfer_size > single_packet_covered_size)
|
||||
transfer_size = single_packet_covered_size;
|
||||
|
||||
header.command = command;
|
||||
header.direction = IS_NITRO_PACKET_DIR_WRITE;
|
||||
header.type = type;
|
||||
header.address = address;
|
||||
header.length = transfer_size;
|
||||
header.padding = 0;
|
||||
fix_endianness_header(&header);
|
||||
for(int j = 0; j < sizeof(is_nitro_packet_header); j++)
|
||||
single_usb_packet[j] = ((uint8_t*)&header)[j];
|
||||
for(int j = 0; j < transfer_size; j++)
|
||||
single_usb_packet[sizeof(is_nitro_packet_header) + j] = buf[(i * single_packet_covered_size) + j];
|
||||
int num_bytes = 0;
|
||||
int ret = bulk_out(handle, &usb_is_nitro_desc, single_usb_packet, transfer_size + sizeof(is_nitro_packet_header), &num_bytes);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
if(num_bytes != (transfer_size + sizeof(is_nitro_packet_header)))
|
||||
return LIBUSB_ERROR_INTERRUPTED;
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
for(int i = 0; i < num_iters; i++) {
|
||||
int transfer_size = length - (i * single_packet_covered_size);
|
||||
if(transfer_size > single_packet_covered_size)
|
||||
transfer_size = single_packet_covered_size;
|
||||
|
||||
header.command = command;
|
||||
header.direction = IS_NITRO_PACKET_DIR_READ;
|
||||
header.type = type;
|
||||
header.address = address;
|
||||
header.length = transfer_size;
|
||||
header.padding = 0;
|
||||
fix_endianness_header(&header);
|
||||
int num_bytes = 0;
|
||||
int ret = bulk_out(handle, &usb_is_nitro_desc, (uint8_t*)&header, sizeof(is_nitro_packet_header), &num_bytes);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
if(num_bytes != sizeof(is_nitro_packet_header))
|
||||
return LIBUSB_ERROR_INTERRUPTED;
|
||||
ret = bulk_in(handle, &usb_is_nitro_desc, buf + (i * single_packet_covered_size), transfer_size, &num_bytes);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
if(num_bytes != transfer_size)
|
||||
return LIBUSB_ERROR_INTERRUPTED;
|
||||
}
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
int SendReadCommand(libusb_device_handle *handle, uint16_t command, uint8_t* buf, int length) {
|
||||
return SendReadPacket(handle, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length);
|
||||
}
|
||||
|
||||
int SendWriteCommand(libusb_device_handle *handle, uint16_t command, uint8_t* buf, int length) {
|
||||
return SendWritePacket(handle, command, IS_NITRO_PACKET_TYPE_COMMAND, 0, buf, length);
|
||||
}
|
||||
|
||||
int GetDeviceSerial(libusb_device_handle *handle, uint8_t* buf) {
|
||||
return SendReadCommand(handle, IS_NITRO_CMD_GET_SERIAL, buf, IS_NITRO_REAL_SERIAL_NUMBER_SIZE);
|
||||
}
|
||||
|
||||
int ReadNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_size, uint8_t* buf, int count) {
|
||||
is_nitro_nec_packet_header header;
|
||||
header.command = IS_NITRO_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));
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
return SendReadCommand(handle, IS_NITRO_CMD_READ_NEC_MEM, buf, count * unit_size);
|
||||
}
|
||||
|
||||
int ReadNecMemU16(libusb_device_handle *handle, uint32_t address, uint16_t* out) {
|
||||
uint8_t buffer[2];
|
||||
int ret = ReadNecMem(handle, address, 2, buffer, 1);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
*out = buffer[0] | (buffer[1] << 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t* out) {
|
||||
uint8_t buffer[4];
|
||||
int ret = ReadNecMem(handle, address, 2, buffer, 2);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
*out = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WriteNecMem(libusb_device_handle *handle, uint32_t address, uint8_t unit_size, uint8_t* buf, int count) {
|
||||
uint8_t* buffer = new uint8_t[(count * unit_size) + sizeof(is_nitro_nec_packet_header)];
|
||||
is_nitro_nec_packet_header header;
|
||||
header.command = IS_NITRO_CMD_WRITE_NEC_MEM;
|
||||
header.unit_size = unit_size;
|
||||
header.count = count;
|
||||
header.address = address;
|
||||
fix_endianness_header(&header);
|
||||
for(int i = 0; i < sizeof(is_nitro_nec_packet_header); i++)
|
||||
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));
|
||||
delete []buffer;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int WriteNecMemU16(libusb_device_handle *handle, uint32_t address, uint16_t value) {
|
||||
uint8_t buffer[2];
|
||||
buffer[0] = value & 0xFF;
|
||||
buffer[1] = (value >> 8) & 0xFF;
|
||||
return WriteNecMem(handle, address, 2, buffer, 1);
|
||||
}
|
||||
|
||||
int WriteNecMemU32(libusb_device_handle *handle, uint32_t address, uint32_t value) {
|
||||
uint8_t buffer[4];
|
||||
buffer[0] = value & 0xFF;
|
||||
buffer[1] = (value >> 8) & 0xFF;
|
||||
buffer[2] = (value >> 16) & 0xFF;
|
||||
buffer[3] = (value >> 24) & 0xFF;
|
||||
return WriteNecMem(handle, address, 2, buffer, 2);
|
||||
}
|
||||
|
||||
int DisableLca2(libusb_device_handle *handle) {
|
||||
int ret = WriteNecMemU16(handle, 0x00805180, 0);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = WriteNecMemU16(handle, 0xF84000A, 1);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
default_sleep(2000);
|
||||
return WriteNecMemU16(handle, 0xF84000A, 0);
|
||||
}
|
||||
|
||||
int StartUsbCaptureDma(libusb_device_handle *handle) {
|
||||
int ret = WriteNecMemU16(handle, REG_USB_DMA_CONTROL_2, 2);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
return WriteNecMemU16(handle, REG_USB_BIU_CONTROL_2, 1);
|
||||
}
|
||||
|
||||
int StopUsbCaptureDma(libusb_device_handle *handle) {
|
||||
int ret = WriteNecMemU16(handle, REG_USB_DMA_CONTROL_2, 0);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
return WriteNecMemU16(handle, REG_USB_BIU_CONTROL_2, 0);
|
||||
}
|
||||
|
||||
int SetForwardFrameCount(libusb_device_handle *handle, uint16_t count) {
|
||||
if(!count)
|
||||
return LIBUSB_ERROR_INTERRUPTED;
|
||||
count -= 1;
|
||||
return WriteNecMemU32(handle, 0x800000C, (count >> 8) | ((count & 0xFF) << 16));
|
||||
}
|
||||
|
||||
int GetFrameCounter(libusb_device_handle *handle, uint16_t* out) {
|
||||
uint32_t counter = 0;
|
||||
int ret = ReadNecMemU32(handle, 0x08000028, &counter);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
*out = (counter & 0xFF) | ((counter & 0xFF0000) >> 8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int UpdateFrameForwardConfig(libusb_device_handle *handle, uint8_t value) {
|
||||
return WriteNecMemU16(handle, 0x0800000A, value);
|
||||
}
|
||||
|
||||
int UpdateFrameForwardEnable(libusb_device_handle *handle, uint8_t value) {
|
||||
return WriteNecMemU16(handle, 0x08000008, value);
|
||||
}
|
||||
|
||||
int ReadFrame(libusb_device_handle *handle, uint8_t* buf, int length) {
|
||||
int num_bytes = 0;
|
||||
int ret = bulk_in(handle, &usb_is_nitro_desc, buf, length, &num_bytes);
|
||||
if(num_bytes != length)
|
||||
return LIBUSB_ERROR_INTERRUPTED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
344
source/usb_is_nitro_capture.cpp
Normal file
344
source/usb_is_nitro_capture.cpp
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
#include "devicecapture.hpp"
|
||||
#include "usb_is_nitro.hpp"
|
||||
#include "usb_is_nitro_capture.hpp"
|
||||
#include "usb_generic.hpp"
|
||||
|
||||
#include <libusb.h>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
// 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 NUM_CONSECUTIVE_FRAMES 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);
|
||||
static int StartCapture(libusb_device_handle *handle, int *out_frame_count);
|
||||
static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames);
|
||||
|
||||
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(libusb_reset_device(handle) != LIBUSB_SUCCESS)
|
||||
// conn_success = false;
|
||||
if(conn_success && libusb_claim_interface(handle, usb_device_desc->default_interface) != LIBUSB_SUCCESS)
|
||||
conn_success = false;
|
||||
if(conn_success && EndCapture(handle, false, 0) != LIBUSB_SUCCESS)
|
||||
conn_success = false;
|
||||
if(conn_success && (GetDeviceSerial(handle, data) == LIBUSB_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 bool insert_device(std::vector<CaptureDevice> &devices_list, const is_nitro_usb_device* usb_device_desc, libusb_device *usb_device, libusb_device_descriptor *usb_descriptor, int &curr_serial_extra_id) {
|
||||
libusb_device_handle *handle = NULL;
|
||||
if((usb_descriptor->idVendor != usb_device_desc->vid) || (usb_descriptor->idProduct != usb_device_desc->pid))
|
||||
return false;
|
||||
if((usb_descriptor->iManufacturer != usb_device_desc->manufacturer_id) || (usb_descriptor->iProduct != usb_device_desc->product_id))
|
||||
return false;
|
||||
int result = libusb_open(usb_device, &handle);
|
||||
if(result || (handle == NULL))
|
||||
return true;
|
||||
devices_list.emplace_back(get_serial(usb_device_desc, handle, curr_serial_extra_id), "IS Nitro Emulator", CAPTURE_CONN_IS_NITRO, false, false, false, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, 0, 24, 0, 0, 0, 0, HEIGHT_DS);
|
||||
libusb_close(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
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<CaptureDevice> &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;
|
||||
if(insert_device(devices_list, &usb_is_nitro_desc, usb_devices[i], &usb_descriptor, curr_serial_extra_id_is_nitro_emulator))
|
||||
continue;
|
||||
}
|
||||
|
||||
if(num_devices >= 0)
|
||||
libusb_free_device_list(usb_devices, 1);
|
||||
}
|
||||
|
||||
static void is_nitro_connection_end(libusb_device_handle *dev, bool interface_claimed = true) {
|
||||
if(interface_claimed)
|
||||
libusb_release_interface(dev, usb_is_nitro_desc.default_interface);
|
||||
libusb_close(dev);
|
||||
}
|
||||
|
||||
bool is_nitro_connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device) {
|
||||
if(!usb_is_initialized())
|
||||
return false;
|
||||
libusb_device_handle *dev = usb_find_by_serial_number(&usb_is_nitro_desc, device->serial_number);
|
||||
if(!dev) {
|
||||
capture_error_print(true, capture_data, "Device not found");
|
||||
return false;
|
||||
}
|
||||
if(libusb_set_configuration(dev, usb_is_nitro_desc.default_config) != LIBUSB_SUCCESS) {
|
||||
capture_error_print(true, capture_data, "Configuration failed");
|
||||
is_nitro_connection_end(dev, false);
|
||||
return false;
|
||||
}
|
||||
if(libusb_claim_interface(dev, usb_is_nitro_desc.default_interface) != LIBUSB_SUCCESS) {
|
||||
capture_error_print(true, capture_data, "Interface claim failed");
|
||||
is_nitro_connection_end(dev, false);
|
||||
return false;
|
||||
}
|
||||
capture_data->handle = (void*)dev;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t _is_nitro_emulator_get_video_in_size() {
|
||||
return sizeof(ISNitroEmulatorVideoInputData);
|
||||
}
|
||||
|
||||
static uint64_t get_capture_size() {
|
||||
return sizeof(ISNitroCaptureReceived);
|
||||
}
|
||||
|
||||
uint64_t usb_is_nitro_emulator_get_video_in_size(CaptureData* capture_data) {
|
||||
return _is_nitro_emulator_get_video_in_size();
|
||||
}
|
||||
|
||||
static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames) {
|
||||
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());
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
static int StartCapture(libusb_device_handle *handle, int *out_frame_count) {
|
||||
int ret = DisableLca2(handle);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = UpdateFrameForwardConfig(handle, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24 | IS_NITRO_FORWARD_CONFIG_MODE_BOTH | IS_NITRO_FORWARD_CONFIG_RATE_FULL);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = SetForwardFrameCount(handle, NUM_CONSECUTIVE_FRAMES);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_DISABLE);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
uint16_t oldFrameCount;
|
||||
ret = GetFrameCounter(handle, &oldFrameCount);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_ENABLE | IS_NITRO_FORWARD_ENABLE_RESTART);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
uint16_t newFrameCount;
|
||||
while (true) {
|
||||
ret = GetFrameCounter(handle, &newFrameCount);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
if (newFrameCount < 64 && newFrameCount != oldFrameCount && newFrameCount != oldFrameCount + 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = StartUsbCaptureDma(handle);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = drain_frames(handle, newFrameCount, 0);
|
||||
*out_frame_count = newFrameCount;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames) {
|
||||
int ret = 0;
|
||||
if(do_drain_frames)
|
||||
ret = drain_frames(handle, NUM_CONSECUTIVE_FRAMES, start_frames);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = StopUsbCaptureDma(handle);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
return UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_DISABLE);
|
||||
}
|
||||
|
||||
int reset_capture_frames(libusb_device_handle* handle, int &frame_counter, uint16_t &total_frame_counter) {
|
||||
total_frame_counter += 1;
|
||||
frame_counter += 1;
|
||||
|
||||
if(frame_counter == NUM_CONSECUTIVE_FRAMES) {
|
||||
int ret = StopUsbCaptureDma(handle);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
uint16_t internalFrameCount = -1;
|
||||
int diff;
|
||||
do {
|
||||
if (internalFrameCount != -1)
|
||||
default_sleep(8000);
|
||||
ret = GetFrameCounter(handle, &internalFrameCount);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
diff = internalFrameCount - total_frame_counter;
|
||||
if(diff > 32768)
|
||||
diff -= 1 << 16;
|
||||
} while(diff <= 0);
|
||||
*/
|
||||
|
||||
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_ENABLE);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
frame_counter = 0;
|
||||
ret = StartUsbCaptureDma(handle);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
void is_nitro_capture_main_loop(CaptureData* capture_data) {
|
||||
if(!usb_is_initialized())
|
||||
return;
|
||||
libusb_device_handle *handle = (libusb_device_handle*)capture_data->handle;
|
||||
int frame_counter = 0;
|
||||
int ret = StartCapture(handle, &frame_counter);
|
||||
uint16_t total_frame_counter = frame_counter;
|
||||
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();
|
||||
|
||||
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());
|
||||
if(ret < 0) {
|
||||
capture_error_print(true, capture_data, "Disconnected: Read error");
|
||||
break;
|
||||
}
|
||||
|
||||
const auto curr_time = std::chrono::high_resolution_clock::now();
|
||||
const std::chrono::duration<double> diff = curr_time - clock_start;
|
||||
ret = reset_capture_frames(handle, frame_counter, total_frame_counter);
|
||||
if(ret < 0) {
|
||||
capture_error_print(true, capture_data, "Disconnected: Frame counter reset error");
|
||||
break;
|
||||
}
|
||||
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();
|
||||
|
||||
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();
|
||||
}
|
||||
EndCapture(handle, true, frame_counter);
|
||||
}
|
||||
|
||||
void usb_is_nitro_capture_cleanup(CaptureData* capture_data) {
|
||||
if(!usb_is_initialized())
|
||||
return;
|
||||
is_nitro_connection_end((libusb_device_handle*)capture_data->handle);
|
||||
}
|
||||
|
||||
void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureDevice* capture_device) {
|
||||
if(!usb_is_initialized())
|
||||
return;
|
||||
for(int i = 0; i < IN_VIDEO_HEIGHT_DS; i++)
|
||||
for(int j = 0; j < IN_VIDEO_WIDTH_DS; j++) {
|
||||
int pixel = (i * IN_VIDEO_WIDTH_DS) + j;
|
||||
p_out->screen_data[pixel][0] = p_in->is_nitro_capture_received.video_in.screen_data[pixel][2];
|
||||
p_out->screen_data[pixel][1] = p_in->is_nitro_capture_received.video_in.screen_data[pixel][1];
|
||||
p_out->screen_data[pixel][2] = p_in->is_nitro_capture_received.video_in.screen_data[pixel][0];
|
||||
}
|
||||
}
|
||||
|
||||
void usb_is_nitro_init() {
|
||||
return usb_init();
|
||||
}
|
||||
|
||||
void usb_is_nitro_close() {
|
||||
usb_close();
|
||||
}
|
||||
|
||||
|
|
@ -21,13 +21,44 @@
|
|||
#define APP_VERSION_LETTER M
|
||||
#endif
|
||||
|
||||
static bool checked_be_once = false;
|
||||
static bool _is_be = false;
|
||||
|
||||
bool is_big_endian(void) {
|
||||
if(checked_be_once)
|
||||
return _is_be;
|
||||
union {
|
||||
uint32_t i;
|
||||
char c[4];
|
||||
} value = {0x01020304};
|
||||
|
||||
return value.c[0] == 1;
|
||||
checked_be_once = true;
|
||||
_is_be = value.c[0] == 1;
|
||||
return _is_be;
|
||||
}
|
||||
|
||||
uint32_t to_le(uint32_t value) {
|
||||
if(is_big_endian())
|
||||
value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t to_be(uint32_t value) {
|
||||
if(!is_big_endian())
|
||||
value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint16_t to_le(uint16_t value) {
|
||||
if(is_big_endian())
|
||||
value = ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint16_t to_be(uint16_t value) {
|
||||
if(!is_big_endian())
|
||||
value = ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string get_version_string(bool get_letter) {
|
||||
|
|
|
|||
1
usb_rules/51-isnitro.rules
Normal file
1
usb_rules/51-isnitro.rules
Normal file
|
|
@ -0,0 +1 @@
|
|||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0f6e", ATTRS{idProduct}=="0404", MODE="0666"
|
||||
Loading…
Reference in New Issue
Block a user