diff --git a/95-usb3dscapture.rules b/95-usb3dscapture.rules
new file mode 100644
index 0000000..3b32b6a
--- /dev/null
+++ b/95-usb3dscapture.rules
@@ -0,0 +1 @@
+SUBSYSTEM=="usb", ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="06a3", MODE="0666"
diff --git a/95-usbdscapture.rules b/95-usbdscapture.rules
new file mode 100644
index 0000000..b19535b
--- /dev/null
+++ b/95-usbdscapture.rules
@@ -0,0 +1 @@
+SUBSYSTEM=="usb", ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0647", MODE="0666"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 59a7f98..2ca9817 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.16)
project(cc3dsfs VERSION 1.0.0 LANGUAGES CXX)
include(ExternalProject)
+set(FETCHCONTENT_UPDATES_DISCONNECTED ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
set(CMAKE_HOST_CXX_COMPILER ${CMAKE_CXX_COMPILER} CACHE STRING "Host Compiler")
@@ -101,6 +102,10 @@ if(IS_DIRECTORY "${sfml_SOURCE_DIR}")
set_property(DIRECTORY ${sfml_SOURCE_DIR} PROPERTY EXCLUDE_FROM_ALL YES)
endif()
+if(IS_DIRECTORY "${libusb1_SOURCE_DIR}")
+ set_property(DIRECTORY ${libusb1_SOURCE_DIR} PROPERTY EXCLUDE_FROM_ALL YES)
+endif()
+
file(MAKE_DIRECTORY ${ftd3xx_BINARY_DIR}/win)
file(MAKE_DIRECTORY ${ftd3xx_BINARY_DIR}/macos)
file(MAKE_DIRECTORY ${ftd3xx_BINARY_DIR}/linux)
@@ -145,7 +150,7 @@ file(MAKE_DIRECTORY ${TOOLS_DATA_DIR})
set(OUTPUT_NAME cc3dsfs)
add_executable(CMakeBin2C tools/bin2c.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.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 ${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_ftdi.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 ${TOOLS_DATA_DIR}/font_ttf.cpp)
add_dependencies(${OUTPUT_NAME} FTD3XX_BUILD_PROJECT CMakeBin2C)
target_link_libraries(${OUTPUT_NAME} PRIVATE sfml-graphics sfml-audio sfml-window sfml-system usb-1.0 ${ftd3xx_BINARY_DIR}/${FTD3XX_SUBFOLDER}/${FTD3XX_LIB} ${EXTRA_LIBRARIES})
target_link_directories(${OUTPUT_NAME} PRIVATE ${ftd3xx_BINARY_DIR}/${FTD3XX_SUBFOLDER})
@@ -188,7 +193,10 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
set(FW_APP ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${OUTPUT_NAME}.app)
set(FW_DIRS ${sfml_SOURCE_DIR}/extlibs/libs-osx/Frameworks)
set_target_properties(${OUTPUT_NAME} PROPERTIES
- MACOSX_BUNDLE TRUE)
+ MACOSX_BUNDLE TRUE
+ MACOSX_BUNDLE_BUNDLE_NAME ${OUTPUT_NAME}
+ MACOSX_BUNDLE_GUI_IDENTIFIER ${OUTPUT_NAME}
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/data/MacOSXBundleInfo.plist.in)
set(CMAKE_MACOSX_RPATH TRUE)
add_custom_command(
@@ -208,6 +216,8 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
else()
install(FILES 51-ftd3xx.rules DESTINATION .)
+ install(FILES 95-usb3dscapture.rules DESTINATION .)
+ install(FILES 95-usbdscapture.rules DESTINATION .)
endif()
diff --git a/README.md b/README.md
index 7bf0d29..4182b4d 100755
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# cc3dsfs
-cc3dsfs is a multi-platform capture and display program for [3dscapture's](https://3dscapture.com/) N3DSXL capture board written in C++.
+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.
## Features
@@ -21,8 +21,8 @@ cc3dsfs has three build dependencies: CMake, g++ and git.
Make sure all are installed.
On MacOS, [Homebrew](https://brew.sh/) can be used to install both CMake and git. An automatic popup should appear to install g++ at Compile time.
-cc3dsfs has two library dependencies: [FTDI's D3XX driver](https://ftdichip.com/drivers/d3xx-drivers/) and [SFML](https://www.sfml-dev.org/).
-Both should get downloaded automatically via CMake during the building process.
+cc3dsfs has three library dependencies: [FTDI's D3XX driver](https://ftdichip.com/drivers/d3xx-drivers/), [libusb](https://libusb.info/) and [SFML](https://www.sfml-dev.org/).
+All of them should get downloaded automatically via CMake during the building process.
Linux users will also need to install the SFML dependencies. Different distributions will require slightly different processes.
Below, the command for Debian-based distributions, which also lists the required libraries.
@@ -56,7 +56,7 @@ To compile the program, assuming CMake, git and g++ are installed on the system,
cmake -B build ; cmake --build build --config Release
```
-This will download both FTD3XX and SFML, which may take a while during the first execution of the command. Later runs should be much faster.
+This will download FTD3XX, libusb and SFML, which may take a while during the first execution of the command. Later runs should be much faster.
On MacOS, you may also be prompted to install the Apple Command Line Developer Tools first.
## Controls
@@ -80,10 +80,10 @@ Most of the settings are explained in [Keyboard shortcuts](#Keyboard-shortcuts).
_Note: Currently only tested using a PS5 controller._
### Keyboard shortcuts
-- __O key__: Open/Close connection to the 3DS.
+- __O key__: Open/Close connection to the 3DS/DS.
- __F key__: Toggles Fullscreen mode on/off. Only guaranteed to work on the primary monitor. When using certain setups, it also works for multiple monitors.
- __S key__: Swaps between Split mode and Joint mode. In Split mode, each screen has its own window. In Joint mode, they are combined into a single one.
-- __C key__: Cycles to the next Crop mode for the focused window. The currently supported cropping modes are: 3DS, 16:10 DS, scaled DS, native DS, scaled GBA, native GBA, scaled GB, native GB, scaled SNES, native SNES and native NES.
+- __C key__: Cycles to the next Crop mode for the focused window. For 3DS, the currently supported cropping modes are: 3DS, 16:10 DS, scaled DS, native DS, scaled GBA, native GBA, scaled GB, native GB, scaled SNES, native SNES and native NES. For DS, the currently supported cropping modes are: DS, Top GBA and Bottom GBA.
- __-/0 key__: Decrements/Increments the scaling by 0.5x for the non-Fullscreen focused window. 1.0x is the minimum. 45.0x is the maximum.
- __Y/U key__: In Joint Fullscreen mode, increases the size of the top/bottom screen. Corresponds to the _Screens ratio_ Video Setting.
- __Z/X key__: Decreases/Increases the Menu Scaling multiplier by 0.1. 0.3 is the minimum. 10.0 is the maximum.
@@ -118,15 +118,19 @@ The current configuration can be saved to various extra profiles, creating the g
The name of profiles can be changed by altering the __name__ field in its file.
## Notes
-
+- On Linux, you may need to include the udev USB access rules. You can use the .rules files available in the repository, or define your own.
- At startup, the audio may be unstable. It should fix itself, if you give it enough time.
-- If, at first, the connection to the 3DS fails, reconnect the 3DS and then try again. If that also doesn't work, try restarting the program. If that also doesn't work, try restarting the computer.
-- USB Hubs can be the cause of connection issues. If you're having problems, try checking whether the 3DS connects fine or not without any other devices connected.
+- If, at first, the connection to the 3DS/DS fails, reconnect the 3DS/DS and then try again. If that also doesn't work, try restarting the program. If that also doesn't work, try restarting the computer.
+- USB Hubs can be the cause of connection issues. If you're having problems, try checking whether the 3DS/DS connects fine or not without any other devices connected.
- Fullscreen mode on MacOS may mistake the screen for being bigger than what it really is. Changing the resolution to the proper one of the screen in the _Resolution Settings_ under Video Settings will fix the issue.
- Current font in use: OFL Sorts Mill Goudy TT
- Enabling Slow Poll may slightly boost the FPS of the software, at the cost of an extremely slight decrease in frame latency, and slower reaction times of the software to key presses. Disabled by default (as when the FPS are greater than the CC's, it's not reccomended).
-- When compiling on a Raspberry Pi, to enable usage of GPIO, use:
+- When compiling on a Raspberry Pi, to enable usage of GPIO, use:
```
cmake -B build -DRASPBERRY_PI_COMPILATION=TRUE ; cmake --build build --config Release
```
+- On MacOS, you may get a notice about the app being damaged. It's Apple quaranteening the app. To make it work, open a terminal and run the following:
+```
+xattr -c ./cc3dsfs.app
+```
diff --git a/data/MacOSXBundleInfo.plist.in b/data/MacOSXBundleInfo.plist.in
new file mode 100644
index 0000000..724223f
--- /dev/null
+++ b/data/MacOSXBundleInfo.plist.in
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${MACOSX_BUNDLE_EXECUTABLE_NAME}
+ CFBundleGetInfoString
+ ${MACOSX_BUNDLE_INFO_STRING}
+ CFBundleIdentifier
+ ${MACOSX_BUNDLE_GUI_IDENTIFIER}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleLongVersionString
+ ${MACOSX_BUNDLE_LONG_VERSION_STRING}
+ CFBundleName
+ ${MACOSX_BUNDLE_BUNDLE_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ ${MACOSX_BUNDLE_SHORT_VERSION_STRING}
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ ${MACOSX_BUNDLE_BUNDLE_VERSION}
+ NSHighResolutionCapable
+
+ NSHumanReadableCopyright
+ ${MACOSX_BUNDLE_COPYRIGHT}
+
+
diff --git a/include/3dscapture.hpp b/include/3dscapture.hpp
deleted file mode 100755
index 7d6437e..0000000
--- a/include/3dscapture.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef __3DSCAPTURE_HPP
-#define __3DSCAPTURE_HPP
-
-#include "utils.hpp"
-#include "hw_defs.hpp"
-#include "capture_structs.hpp"
-#include "frontend.hpp"
-
-#if defined(_WIN32) || defined(_WIN64)
-#define FTD3XX_STATIC
-#include
-#define FT_ASYNC_CALL FT_ReadPipeEx
-#else
-#include
-#define FT_ASYNC_CALL FT_ReadPipeAsync
-#endif
-
-// Max value (Due to support of old FTD3XX versions...)
-#define NUM_CONCURRENT_DATA_BUFFERS 8
-
-// When is_bad_ftd3xx is true, it may happen that a frame is lost.
-// This value prevents showing a black frame for that.
-#define MAX_ALLOWED_BLANKS 1
-
-struct CaptureData {
- FT_HANDLE handle;
- ULONG read[NUM_CONCURRENT_DATA_BUFFERS];
- CaptureReceived capture_buf[NUM_CONCURRENT_DATA_BUFFERS];
- double time_in_buf[NUM_CONCURRENT_DATA_BUFFERS];
- CaptureStatus status;
-};
-
-bool connect(bool print_failed, CaptureData* capture_data, FrontendData* frontend_data);
-void captureCall(CaptureData* capture_data);
-#endif
diff --git a/include/3dscapture_ftdi.hpp b/include/3dscapture_ftdi.hpp
new file mode 100755
index 0000000..1a127b9
--- /dev/null
+++ b/include/3dscapture_ftdi.hpp
@@ -0,0 +1,19 @@
+#ifndef __3DSCAPTURE_FTDI_HPP
+#define __3DSCAPTURE_FTDI_HPP
+
+#include
+#include "utils.hpp"
+#include "hw_defs.hpp"
+#include "capture_structs.hpp"
+#include "display_structs.hpp"
+
+#define USE_FTDI
+
+void list_devices_ftdi(std::vector &devices_list);
+bool connect_ftdi(bool print_failed, CaptureData* capture_data, CaptureDevice* device);
+void ftdi_capture_main_loop(CaptureData* capture_data);
+void ftdi_capture_cleanup(CaptureData* capture_data);
+void ftdi_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, bool enabled_3d);
+uint64_t ftdi_get_video_in_size(CaptureData* capture_data);
+
+#endif
diff --git a/include/Menus/ConnectionMenu.hpp b/include/Menus/ConnectionMenu.hpp
index 923225e..988fe04 100755
--- a/include/Menus/ConnectionMenu.hpp
+++ b/include/Menus/ConnectionMenu.hpp
@@ -12,7 +12,7 @@ public:
ConnectionMenu(bool font_load_success, sf::Font &text_font);
~ConnectionMenu();
void prepare(float scaling_factor, int view_size_x, int view_size_y);
- void insert_data(DevicesList *devices_list);
+ void insert_data(std::vector *devices_list);
int selected_index = CONNECTION_MENU_NO_ACTION;
void reset_output_option();
protected:
@@ -21,6 +21,6 @@ protected:
std::string get_string_option(int index, int action);
void class_setup();
private:
- DevicesList *devices_list;
+ std::vector *devices_list;
};
#endif
diff --git a/include/capture_structs.hpp b/include/capture_structs.hpp
index 4551ed8..e8f3696 100755
--- a/include/capture_structs.hpp
+++ b/include/capture_structs.hpp
@@ -5,30 +5,102 @@
#include "hw_defs.hpp"
#include
-#define REAL_SERIAL_NUMBER_SIZE 16
-#define SERIAL_NUMBER_SIZE (REAL_SERIAL_NUMBER_SIZE+1)
+// Max value (Due to support of old FTD3XX versions...)
+#define NUM_CONCURRENT_DATA_BUFFERS 8
-#define MAX_SAMPLES_IN 1096
+// It may happen that a frame is lost.
+// This value prevents showing a black frame for that.
+// Shouldn't happen with recent updates, though...
+#define MAX_ALLOWED_BLANKS 1
#define FIX_PARTIAL_FIRST_FRAME_NUM 3
#pragma pack(push, 1)
-struct PACKED VideoInputData {
- uint8_t screen_data[IN_VIDEO_SIZE][3];
+struct PACKED FTDI3DSVideoInputData {
+ uint8_t screen_data[IN_VIDEO_SIZE_3DS][3];
};
-struct PACKED CaptureReceived {
- VideoInputData video_data;
- uint16_t audio_data[MAX_SAMPLES_IN];
+struct PACKED FTDI3DSVideoInputData_3D {
+ uint8_t screen_data[IN_VIDEO_SIZE_3DS_3D][3];
+};
+
+struct PACKED USB3DSVideoInputData {
+ uint8_t screen_data[IN_VIDEO_SIZE_3DS][3];
+};
+
+struct PACKED USBOldDSPixelData {
+ uint16_t b : 5;
+ uint16_t special : 1;
+ uint16_t g : 5;
+ uint16_t r : 5;
+};
+
+struct PACKED USBOldDSVideoInputData {
+ USBOldDSPixelData screen_data[IN_VIDEO_SIZE_DS];
+};
+
+struct PACKED FTDI3DSCaptureReceived {
+ FTDI3DSVideoInputData video_in;
+ uint16_t audio_data[N3DSXL_SAMPLES_IN];
+};
+
+struct PACKED FTDI3DSCaptureReceived_3D {
+ FTDI3DSVideoInputData_3D video_in;
+ uint16_t audio_data[N3DSXL_SAMPLES_IN];
+};
+
+struct PACKED USB3DSCaptureReceived {
+ USB3DSVideoInputData video_in;
+};
+
+struct PACKED USBOldDSFrameInfo {
+ uint8_t half_line_flags[(HEIGHT_DS >> 3) << 1];
+ uint32_t frame;
+ uint8_t valid;
+ uint8_t unused[11];
+};
+
+struct PACKED USBOldDSCaptureReceived {
+ USBOldDSVideoInputData video_in;
+ USBOldDSFrameInfo frameinfo;
};
#pragma pack(pop)
-struct CaptureStatus {
+union CaptureReceived {
+ FTDI3DSCaptureReceived ftdi_received;
+ FTDI3DSCaptureReceived_3D ftdi_received_3d;
+ USB3DSCaptureReceived usb_received_3ds;
+ USBOldDSCaptureReceived usb_received_old_ds;
+};
+
+struct CaptureDevice {
+ CaptureDevice(std::string serial_number, std::string name, bool is_ftdi, bool is_3ds, bool has_3d, bool has_audio, int width, int height, int max_samples_in, int rgb_bits_size, int base_rotation, int top_screen_x, int top_screen_y, int bot_screen_x, int bot_screen_y): serial_number(serial_number), name(name), is_ftdi(is_ftdi), is_3ds(is_3ds), has_3d(has_3d), has_audio(has_audio), width(width), height(height), max_samples_in(max_samples_in), rgb_bits_size(rgb_bits_size), base_rotation(base_rotation), top_screen_x(top_screen_x), top_screen_y(top_screen_y), bot_screen_x(bot_screen_x), bot_screen_y(bot_screen_y) {}
+ CaptureDevice(): serial_number(""), name(""), is_ftdi(false), is_3ds(false), has_3d(false), has_audio(false), width(0), height(0), max_samples_in(0), rgb_bits_size(0), base_rotation(0), top_screen_x(0), top_screen_y(0), bot_screen_x(0), bot_screen_y(0) {}
+
std::string serial_number;
+ std::string name;
+ bool is_ftdi;
+ bool is_3ds;
+ bool has_3d;
+ bool has_audio;
+ int width;
+ int height;
+ int max_samples_in;
+ int rgb_bits_size;
+ int base_rotation;
+ int top_screen_x;
+ int top_screen_y;
+ int bot_screen_x;
+ int bot_screen_y;
+};
+
+struct CaptureStatus {
+ CaptureDevice device;
std::string error_text;
bool new_error_text;
+ bool enabled_3d = false;
volatile int curr_in = 0;
volatile int cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM;
volatile bool connected = false;
@@ -38,9 +110,11 @@ struct CaptureStatus {
ConsumerMutex audio_wait;
};
-struct DevicesList {
- int numAllocedDevices;
- int numValidDevices;
- char *serialNumbers;
+struct CaptureData {
+ void* handle;
+ uint64_t read[NUM_CONCURRENT_DATA_BUFFERS];
+ CaptureReceived capture_buf[NUM_CONCURRENT_DATA_BUFFERS];
+ double time_in_buf[NUM_CONCURRENT_DATA_BUFFERS];
+ CaptureStatus status;
};
#endif
diff --git a/include/conversions.hpp b/include/conversions.hpp
index a3110a6..0e3b98b 100755
--- a/include/conversions.hpp
+++ b/include/conversions.hpp
@@ -3,8 +3,8 @@
#include
#include "capture_structs.hpp"
-#include "frontend.hpp"
+#include "display_structs.hpp"
-void convertVideoToOutput(VideoInputData *p_in, VideoOutputData *p_out);
-void convertAudioToOutput(CaptureReceived *p_in, sf::Int16 *p_out, const int n_samples, const bool is_big_endian);
+void convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureData* capture_data);
+void convertAudioToOutput(CaptureReceived *p_in, sf::Int16 *p_out, uint64_t n_samples, const bool is_big_endian, CaptureData* capture_data);
#endif
diff --git a/include/devicecapture.hpp b/include/devicecapture.hpp
new file mode 100755
index 0000000..af6345b
--- /dev/null
+++ b/include/devicecapture.hpp
@@ -0,0 +1,17 @@
+#ifndef __DEVICECAPTURE_HPP
+#define __DEVICECAPTURE_HPP
+
+#include "utils.hpp"
+#include "hw_defs.hpp"
+#include "capture_structs.hpp"
+#include "frontend.hpp"
+
+void capture_init();
+void capture_close();
+
+bool connect(bool print_failed, CaptureData* capture_data, FrontendData* frontend_data);
+void captureCall(CaptureData* capture_data);
+void capture_error_print(bool print_failed, CaptureData* capture_data, std::string error_string);
+uint64_t get_audio_n_samples(CaptureData* capture_data, uint64_t read);
+uint64_t get_video_in_size(CaptureData* capture_data);
+#endif
diff --git a/include/display_structs.hpp b/include/display_structs.hpp
index 73177f6..8aaef55 100755
--- a/include/display_structs.hpp
+++ b/include/display_structs.hpp
@@ -11,6 +11,7 @@ enum CurrMenuType { DEFAULT_MENU_TYPE, CONNECT_MENU_TYPE, MAIN_MENU_TYPE, VIDEO_
struct ScreenInfo {
bool is_blurred;
int crop_kind;
+ int crop_kind_ds;
double scaling;
bool is_fullscreen;
BottomRelativePosition bottom_pos;
@@ -36,12 +37,13 @@ struct DisplayData {
bool split;
bool mono_app_mode;
bool fast_poll;
+ bool last_connected_ds;
};
#pragma pack(push, 1)
struct PACKED VideoOutputData {
- uint8_t screen_data[IN_VIDEO_SIZE][4];
+ uint8_t screen_data[MAX_IN_VIDEO_SIZE][4];
};
#pragma pack(pop)
@@ -58,6 +60,8 @@ struct CropData {
bool allowed_joint;
bool allowed_top;
bool allowed_bottom;
+ bool allowed_3ds;
+ bool allowed_ds;
std::string name;
};
diff --git a/include/frontend.hpp b/include/frontend.hpp
index 9869fc3..d1f7981 100755
--- a/include/frontend.hpp
+++ b/include/frontend.hpp
@@ -63,9 +63,10 @@ public:
void end();
void after_thread_join();
void draw(double frame_time, VideoOutputData* out_buf);
- void setup_connection_menu(DevicesList *devices_list, bool reset_data = true);
+ void setup_connection_menu(std::vector *devices_list, bool reset_data = true);
int check_connection_menu_result();
void end_connection_menu();
+ void update_ds_3ds_connection(bool changed_type);
void update_save_menu();
void print_notification(std::string text, TextKind kind = TEXT_KIND_NORMAL);
@@ -142,6 +143,7 @@ private:
ShortcutMenu *shortcut_menu;
ActionSelectionMenu *action_selection_menu;
std::vector possible_crops;
+ std::vector possible_crops_ds;
std::vector possible_pars;
std::vector possible_resolutions;
std::vector possible_files;
@@ -278,8 +280,7 @@ struct FrontendData {
void FPSArrayInit(FPSArray *array);
void FPSArrayDestroy(FPSArray *array);
void FPSArrayInsertElement(FPSArray *array, double frame_time);
-bool is_allowed_crop(const CropData* crop_data, ScreenType s_type);
-void insert_basic_crops(std::vector &crop_vector, ScreenType s_type);
+void insert_basic_crops(std::vector &crop_vector, ScreenType s_type, bool is_ds);
void insert_basic_pars(std::vector &par_vector);
void reset_display_data(DisplayData *display_data);
void reset_fullscreen_info(ScreenInfo &info);
@@ -288,7 +289,8 @@ 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(float &width, float &height, float multiplier_factor, const PARData *correction_factor);
void get_par_size(int &width, int &height, float multiplier_factor, const PARData *correction_factor);
-void default_sleep();
+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);
#endif
diff --git a/include/hw_defs.hpp b/include/hw_defs.hpp
index cd71210..7775d76 100755
--- a/include/hw_defs.hpp
+++ b/include/hw_defs.hpp
@@ -39,11 +39,33 @@
#define WIDTH_NES 256
#define HEIGHT_NES 240
-#define IN_VIDEO_WIDTH HEIGHT_3DS
-#define IN_VIDEO_HEIGHT (TOP_WIDTH_3DS + BOT_WIDTH_3DS)
-#define IN_VIDEO_SIZE (IN_VIDEO_WIDTH * IN_VIDEO_HEIGHT)
-
#define TOP_SIZE_3DS (TOP_WIDTH_3DS * HEIGHT_3DS)
-#define IN_VIDEO_NO_BOTTOM_SIZE ((TOP_WIDTH_3DS - BOT_WIDTH_3DS) * HEIGHT_3DS)
+#define TOP_SIZE_DS (WIDTH_DS * HEIGHT_DS)
+
+#define IN_VIDEO_WIDTH_3DS HEIGHT_3DS
+#define IN_VIDEO_HEIGHT_3DS (TOP_WIDTH_3DS + BOT_WIDTH_3DS)
+#define IN_VIDEO_NO_BOTTOM_SIZE_3DS ((TOP_WIDTH_3DS - BOT_WIDTH_3DS) * HEIGHT_3DS)
+#define IN_VIDEO_SIZE_3DS (IN_VIDEO_WIDTH_3DS * IN_VIDEO_HEIGHT_3DS)
+#define IN_VIDEO_BPP_SIZE_3DS 24
+
+#define IN_VIDEO_WIDTH_3DS_3D HEIGHT_3DS
+#define IN_VIDEO_HEIGHT_3DS_3D (TOP_WIDTH_3DS + TOP_WIDTH_3DS + BOT_WIDTH_3DS)
+#define IN_VIDEO_SIZE_3DS_3D (IN_VIDEO_WIDTH_3DS_3D * IN_VIDEO_HEIGHT_3DS_3D)
+#define IN_VIDEO_BPP_SIZE_3DS_3D 24
+
+#define IN_VIDEO_WIDTH_DS WIDTH_DS
+#define IN_VIDEO_HEIGHT_DS (HEIGHT_DS + HEIGHT_DS)
+#define IN_VIDEO_SIZE_DS (IN_VIDEO_WIDTH_DS * IN_VIDEO_HEIGHT_DS)
+#define IN_VIDEO_BPP_SIZE_DS_BASIC 15
+#define IN_VIDEO_BPP_SIZE_DS_BLENDING 18
+
+#define MAX_IN_VIDEO_WIDTH IN_VIDEO_WIDTH_DS
+#define MAX_IN_VIDEO_HEIGHT IN_VIDEO_HEIGHT_3DS_3D
+#define MAX_IN_VIDEO_SIZE (MAX_IN_VIDEO_WIDTH * MAX_IN_VIDEO_HEIGHT)
+#define MAX_IN_VIDEO_BPP_SIZE IN_VIDEO_BPP_SIZE_3DS
+
+#define N3DSXL_SAMPLES_IN 1096
+#define DS_SAMPLES_IN 1096
+#define MAX_SAMPLES_IN N3DSXL_SAMPLES_IN
#endif
diff --git a/include/usb_ds_3ds_capture.hpp b/include/usb_ds_3ds_capture.hpp
new file mode 100755
index 0000000..7f84d19
--- /dev/null
+++ b/include/usb_ds_3ds_capture.hpp
@@ -0,0 +1,21 @@
+#ifndef __USB_DS_3DS_CAPTURE_HPP
+#define __USB_DS_3DS_CAPTURE_HPP
+
+#include
+#include "utils.hpp"
+#include "hw_defs.hpp"
+#include "capture_structs.hpp"
+#include "display_structs.hpp"
+
+#define USE_USB
+
+void list_devices_usb_ds_3ds(std::vector &devices_list);
+bool connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device);
+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();
+
+#endif
diff --git a/include/utils.hpp b/include/utils.hpp
index 6bcb406..271657f 100755
--- a/include/utils.hpp
+++ b/include/utils.hpp
@@ -42,7 +42,7 @@ public:
private:
std::mutex access_mutex;
std::condition_variable_any condition;
- const std::chrono::durationmax_timed_wait = std::chrono::duration(1.0 / 45);
+ const std::chrono::durationmax_timed_wait = std::chrono::duration(1.0 / 30);
int count = 0;
};
diff --git a/source/3dscapture.cpp b/source/3dscapture.cpp
deleted file mode 100755
index 1e4aa60..0000000
--- a/source/3dscapture.cpp
+++ /dev/null
@@ -1,348 +0,0 @@
-#include "3dscapture.hpp"
-
-#include
-#include
-#include
-#include
-
-#define BULK_OUT 0x02
-#define BULK_IN 0x82
-
-#if defined(_WIN32) || defined(_WIN64)
-#define FIFO_CHANNEL 0x82
-#else
-#define FIFO_CHANNEL 0
-#endif
-
-#define CONNECTION_NO_DEVICE_SELECTED (-1)
-
-static bool get_is_bad_ftd3xx() {
- #if (defined(_WIN32) || defined(_WIN64))
- return false;
- #endif
-
- bool is_bad_ftd3xx = false;
- DWORD ftd3xx_lib_version;
-
- if(FT_FAILED(FT_GetLibraryVersion(&ftd3xx_lib_version))) {
- ftd3xx_lib_version = 0;
- }
- if(ftd3xx_lib_version == 0x0100001A) {
- is_bad_ftd3xx = true;
- }
- return is_bad_ftd3xx;
-}
-
-static void list_devices(DevicesList &devices_list) {
- FT_STATUS ftStatus;
- DWORD numDevs = 0;
- std::string valid_descriptions[] = {"N3DSXL", "N3DSXL.2"};
- ftStatus = FT_CreateDeviceInfoList(&numDevs);
- devices_list.numAllocedDevices = 0;
- devices_list.numValidDevices = 0;
- if (!FT_FAILED(ftStatus) && numDevs > 0)
- {
- const int debug_multiplier = 1;
- devices_list.numAllocedDevices = numDevs * debug_multiplier;
- devices_list.serialNumbers = new char[devices_list.numAllocedDevices * SERIAL_NUMBER_SIZE];
- FT_HANDLE ftHandle = NULL;
- DWORD Flags = 0;
- DWORD Type = 0;
- DWORD ID = 0;
- char SerialNumber[REAL_SERIAL_NUMBER_SIZE] = { 0 };
- char Description[32] = { 0 };
- for (DWORD i = 0; i < numDevs; i++)
- {
- ftStatus = FT_GetDeviceInfoDetail(i, &Flags, &Type, &ID, NULL,
- SerialNumber, Description, &ftHandle);
- if (!FT_FAILED(ftStatus))
- {
- for(int j = 0; j < sizeof(valid_descriptions) / sizeof(*valid_descriptions); j++) {
- if(Description == valid_descriptions[j]) {
- for(int u = 0; u < debug_multiplier; u++) {
- for(int k = 0; k < REAL_SERIAL_NUMBER_SIZE; k++)
- devices_list.serialNumbers[(SERIAL_NUMBER_SIZE * devices_list.numValidDevices) + k] = SerialNumber[k];
- devices_list.serialNumbers[(SERIAL_NUMBER_SIZE * devices_list.numValidDevices) + REAL_SERIAL_NUMBER_SIZE] = 0;
- ++devices_list.numValidDevices;
- }
- break;
- }
- }
- }
- }
- }
-}
-
-static bool poll_connection_window_screen(WindowScreen *screen, int &chosen_index) {
- screen->poll();
- if(screen->check_connection_menu_result() != CONNECTION_MENU_NO_ACTION) {
- chosen_index = screen->check_connection_menu_result();
- return true;
- }
- if(screen->close_capture()) {
- return true;
- }
- return false;
-}
-
-static int choose_device(DevicesList *devices_list, FrontendData* frontend_data) {
- if(devices_list->numValidDevices == 1)
- return 0;
- int chosen_index = CONNECTION_NO_DEVICE_SELECTED;
- frontend_data->top_screen->setup_connection_menu(devices_list);
- frontend_data->bot_screen->setup_connection_menu(devices_list);
- frontend_data->joint_screen->setup_connection_menu(devices_list);
- bool done = false;
- while(!done) {
- update_output(frontend_data);
- done = poll_connection_window_screen(frontend_data->top_screen, chosen_index);
- if(done)
- break;
- done = poll_connection_window_screen(frontend_data->bot_screen, chosen_index);
- if(done)
- break;
- done = poll_connection_window_screen(frontend_data->joint_screen, chosen_index);
- if(done)
- break;
- default_sleep();
- }
- frontend_data->top_screen->end_connection_menu();
- frontend_data->bot_screen->end_connection_menu();
- frontend_data->joint_screen->end_connection_menu();
- return chosen_index;
-}
-
-static void capture_error_print(bool print_failed, CaptureData* capture_data, std::string error_string) {
- if(print_failed) {
- capture_data->status.error_text = error_string;
- capture_data->status.new_error_text = true;
- }
-}
-
-static void preemptive_close_connection(CaptureData* capture_data) {
- FT_AbortPipe(capture_data->handle, BULK_IN);
- FT_Close(capture_data->handle);
-}
-
-bool connect(bool print_failed, CaptureData* capture_data, FrontendData* frontend_data) {
- capture_data->status.new_error_text = false;
- if (capture_data->status.connected) {
- capture_data->status.close_success = false;
- return false;
- }
-
- if(!capture_data->status.close_success) {
- capture_error_print(print_failed, capture_data, "Previous device still closing...");
- return false;
- }
-
- DevicesList devices_list;
- list_devices(devices_list);
-
- if(devices_list.numValidDevices <= 0) {
- capture_error_print(print_failed, capture_data, "No device was found");
- if(devices_list.numAllocedDevices > 0)
- delete []devices_list.serialNumbers;
- return false;
- }
-
- int chosen_device = choose_device(&devices_list, frontend_data);
- if(chosen_device == CONNECTION_NO_DEVICE_SELECTED) {
- capture_error_print(print_failed, capture_data, "No device was selected");
- delete []devices_list.serialNumbers;
- return false;
- }
-
- char SerialNumber[SERIAL_NUMBER_SIZE] = { 0 };
- for(int i = 0; i < REAL_SERIAL_NUMBER_SIZE; i++)
- SerialNumber[i] = devices_list.serialNumbers[(SERIAL_NUMBER_SIZE * chosen_device) + i];
- SerialNumber[REAL_SERIAL_NUMBER_SIZE] = 0;
- delete []devices_list.serialNumbers;
- capture_data->status.serial_number = "N3DSXL - " + std::string(SerialNumber);
-
- if (FT_Create(SerialNumber, FT_OPEN_BY_SERIAL_NUMBER, &capture_data->handle)) {
- capture_error_print(print_failed, capture_data, "Create failed");
- return false;
- }
-
- UCHAR buf[4] = {0x40, 0x80, 0x00, 0x00};
- ULONG written = 0;
-
- if (FT_WritePipe(capture_data->handle, BULK_OUT, buf, 4, &written, 0)) {
- capture_error_print(print_failed, capture_data, "Write failed");
- preemptive_close_connection(capture_data);
- return false;
- }
-
- buf[1] = 0x00;
-
- if (FT_WritePipe(capture_data->handle, BULK_OUT, buf, 4, &written, 0)) {
- capture_error_print(print_failed, capture_data, "Write failed");
- preemptive_close_connection(capture_data);
- return false;
- }
-
- if (FT_SetStreamPipe(capture_data->handle, false, false, BULK_IN, sizeof(CaptureReceived))) {
- capture_error_print(print_failed, capture_data, "Stream failed");
- preemptive_close_connection(capture_data);
- return false;
- }
-
- if(!get_is_bad_ftd3xx()) {
- if(FT_AbortPipe(capture_data->handle, BULK_IN)) {
- capture_error_print(print_failed, capture_data, "Abort failed");
- preemptive_close_connection(capture_data);
- return false;
- }
-
- if (FT_SetStreamPipe(capture_data->handle, false, false, BULK_IN, sizeof(CaptureReceived))) {
- capture_error_print(print_failed, capture_data, "Stream failed");
- preemptive_close_connection(capture_data);
- return false;
- }
- }
-
- // Avoid having old open locks
- capture_data->status.video_wait.try_lock();
- capture_data->status.audio_wait.try_lock();
-
- return true;
-}
-
-static void fast_capture_call(CaptureData* capture_data, OVERLAPPED overlap[NUM_CONCURRENT_DATA_BUFFERS]) {
- int inner_curr_in = 0;
- FT_STATUS ftStatus;
- for (inner_curr_in = 0; inner_curr_in < NUM_CONCURRENT_DATA_BUFFERS; ++inner_curr_in) {
- ftStatus = FT_InitializeOverlapped(capture_data->handle, &overlap[inner_curr_in]);
- if (ftStatus) {
- capture_error_print(true, capture_data, "Disconnected: Initialize failed");
- return;
- }
- }
-
- for (inner_curr_in = 0; inner_curr_in < NUM_CONCURRENT_DATA_BUFFERS - 1; ++inner_curr_in) {
- ftStatus = FT_ASYNC_CALL(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&capture_data->capture_buf[inner_curr_in], sizeof(CaptureReceived), &capture_data->read[inner_curr_in], &overlap[inner_curr_in]);
- if (ftStatus != FT_IO_PENDING) {
- capture_error_print(true, capture_data, "Disconnected: Read failed");
- return;
- }
- }
-
- inner_curr_in = NUM_CONCURRENT_DATA_BUFFERS - 1;
-
- auto clock_start = std::chrono::high_resolution_clock::now();
-
- while (capture_data->status.connected && capture_data->status.running) {
-
- ftStatus = FT_ASYNC_CALL(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&capture_data->capture_buf[inner_curr_in], sizeof(CaptureReceived), &capture_data->read[inner_curr_in], &overlap[inner_curr_in]);
- if (ftStatus != FT_IO_PENDING) {
- capture_error_print(true, capture_data, "Disconnected: Read failed");
- return;
- }
-
- inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS;
-
- ftStatus = FT_GetOverlappedResult(capture_data->handle, &overlap[inner_curr_in], &capture_data->read[inner_curr_in], true);
- if(FT_FAILED(ftStatus)) {
- capture_error_print(true, capture_data, "Disconnected: USB error");
- return;
- }
- const auto curr_time = std::chrono::high_resolution_clock::now();
- const std::chrono::duration diff = curr_time - clock_start;
- capture_data->time_in_buf[inner_curr_in] = diff.count();
- clock_start = curr_time;
-
- capture_data->status.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;
- // Signal that there is data available
- capture_data->status.video_wait.unlock();
- capture_data->status.audio_wait.unlock();
- }
-}
-
-#if !(defined(_WIN32) || defined(_WIN64))
-static bool safe_capture_call(CaptureData* capture_data) {
- int inner_curr_in = 0;
-
- while (capture_data->status.connected && capture_data->status.running) {
-
- auto clock_start = std::chrono::high_resolution_clock::now();
-
- FT_STATUS ftStatus = FT_ReadPipeEx(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&capture_data->capture_buf[inner_curr_in], sizeof(CaptureReceived), &capture_data->read[inner_curr_in], 1000);
- if(FT_FAILED(ftStatus)) {
- capture_error_print(true, capture_data, "Disconnected: Read failed");
- return true;
- }
-
- const auto curr_time = std::chrono::high_resolution_clock::now();
- const std::chrono::duration diff = curr_time - clock_start;
- capture_data->time_in_buf[inner_curr_in] = diff.count();
-
- inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS;
- capture_data->status.curr_in = inner_curr_in;
- if(capture_data->status.cooldown_curr_in)
- capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1;
- capture_data->status.video_wait.unlock();
- capture_data->status.audio_wait.unlock();
- }
-
- return false;
-}
-#endif
-
-void captureCall(CaptureData* capture_data) {
- OVERLAPPED overlap[NUM_CONCURRENT_DATA_BUFFERS];
- FT_STATUS ftStatus;
- int inner_curr_in = 0;
- capture_data->status.curr_in = inner_curr_in;
- capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM;
- bool is_bad_ftd3xx = get_is_bad_ftd3xx();
-
- while(capture_data->status.running) {
- if (!capture_data->status.connected) {
- default_sleep();
- continue;
- }
-
- bool bad_close = false;
- #if !(defined(_WIN32) || defined(_WIN64))
- if(!is_bad_ftd3xx)
- fast_capture_call(capture_data, overlap);
- else
- bad_close = safe_capture_call(capture_data);
- #else
- fast_capture_call(capture_data, overlap);
- #endif
-
- capture_data->status.close_success = false;
- capture_data->status.connected = false;
- capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM;
- // Needed in case the threads went in the connected loop
- // before it is set right above, and they are waiting on the locks!
- capture_data->status.video_wait.unlock();
- capture_data->status.audio_wait.unlock();
-
- if(!is_bad_ftd3xx) {
- for (inner_curr_in = 0; inner_curr_in < NUM_CONCURRENT_DATA_BUFFERS; ++inner_curr_in) {
- ftStatus = FT_GetOverlappedResult(capture_data->handle, &overlap[inner_curr_in], &capture_data->read[inner_curr_in], true);
- if (FT_ReleaseOverlapped(capture_data->handle, &overlap[inner_curr_in])) {
- capture_error_print(true, capture_data, "Disconnected: Release failed");
- }
- }
- }
-
- if(FT_AbortPipe(capture_data->handle, BULK_IN)) {
- capture_error_print(true, capture_data, "Disconnected: Abort failed");
- }
-
- if (FT_Close(capture_data->handle)) {
- capture_error_print(true, capture_data, "Disconnected: Close failed");
- }
-
- capture_data->status.close_success = false;
- capture_data->status.connected = false;
-
- capture_data->status.close_success = true;
- }
-}
diff --git a/source/3dscapture_ftdi.cpp b/source/3dscapture_ftdi.cpp
new file mode 100755
index 0000000..b0b7786
--- /dev/null
+++ b/source/3dscapture_ftdi.cpp
@@ -0,0 +1,293 @@
+#include "3dscapture_ftdi.hpp"
+#include "devicecapture.hpp"
+
+#if defined(_WIN32) || defined(_WIN64)
+#define FTD3XX_STATIC
+#include
+#define FT_ASYNC_CALL FT_ReadPipeEx
+#else
+#include
+#define FT_ASYNC_CALL FT_ReadPipeAsync
+#endif
+
+#include
+#include
+#include
+#include
+
+#define BULK_OUT 0x02
+#define BULK_IN 0x82
+
+#if defined(_WIN32) || defined(_WIN64)
+#define FIFO_CHANNEL 0x82
+#else
+#define FIFO_CHANNEL 0
+#endif
+
+#define REAL_SERIAL_NUMBER_SIZE 16
+#define SERIAL_NUMBER_SIZE (REAL_SERIAL_NUMBER_SIZE+1)
+
+static bool get_is_bad_ftd3xx();
+
+static OVERLAPPED overlap[NUM_CONCURRENT_DATA_BUFFERS];
+static bool is_bad_ftd3xx = get_is_bad_ftd3xx();
+static ULONG read_buffers[NUM_CONCURRENT_DATA_BUFFERS];
+
+static bool get_is_bad_ftd3xx() {
+ #if (defined(_WIN32) || defined(_WIN64))
+ return false;
+ #endif
+
+ bool is_bad_ftd3xx = false;
+ DWORD ftd3xx_lib_version;
+
+ if(FT_FAILED(FT_GetLibraryVersion(&ftd3xx_lib_version))) {
+ ftd3xx_lib_version = 0;
+ }
+ if(ftd3xx_lib_version == 0x0100001A) {
+ is_bad_ftd3xx = true;
+ }
+ return is_bad_ftd3xx;
+}
+
+void list_devices_ftdi(std::vector &devices_list) {
+ FT_STATUS ftStatus;
+ DWORD numDevs = 0;
+ std::string valid_descriptions[] = {"N3DSXL", "N3DSXL.2"};
+ ftStatus = FT_CreateDeviceInfoList(&numDevs);
+ if (!FT_FAILED(ftStatus) && numDevs > 0)
+ {
+ const int debug_multiplier = 1;
+ FT_HANDLE ftHandle = NULL;
+ DWORD Flags = 0;
+ DWORD Type = 0;
+ DWORD ID = 0;
+ char SerialNumber[SERIAL_NUMBER_SIZE] = { 0 };
+ char Description[33] = { 0 };
+ for (DWORD i = 0; i < numDevs; i++)
+ {
+ ftStatus = FT_GetDeviceInfoDetail(i, &Flags, &Type, &ID, NULL,
+ SerialNumber, Description, &ftHandle);
+ if (!FT_FAILED(ftStatus))
+ {
+ for(int j = 0; j < sizeof(valid_descriptions) / sizeof(*valid_descriptions); j++) {
+ if(Description == valid_descriptions[j]) {
+ for(int u = 0; u < debug_multiplier; u++)
+ devices_list.emplace_back(std::string(SerialNumber), "N3DSXL", true, true, true, true, HEIGHT_3DS, TOP_WIDTH_3DS + BOT_WIDTH_3DS, N3DSXL_SAMPLES_IN, IN_VIDEO_BPP_SIZE_3DS, 90, 0, 0, TOP_WIDTH_3DS, 0);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+uint64_t ftdi_get_video_in_size(CaptureData* capture_data) {
+ if(!capture_data->status.enabled_3d)
+ return sizeof(FTDI3DSVideoInputData);
+ return sizeof(FTDI3DSVideoInputData_3D);
+}
+
+static uint64_t get_capture_size(CaptureData* capture_data) {
+ if(!capture_data->status.enabled_3d)
+ return sizeof(FTDI3DSCaptureReceived);
+ return sizeof(FTDI3DSCaptureReceived_3D);
+}
+
+static void preemptive_close_connection(CaptureData* capture_data) {
+ FT_AbortPipe(capture_data->handle, BULK_IN);
+ FT_Close(capture_data->handle);
+}
+
+bool connect_ftdi(bool print_failed, CaptureData* capture_data, CaptureDevice* device) {
+
+ char SerialNumber[SERIAL_NUMBER_SIZE] = { 0 };
+ strncpy(SerialNumber, device->serial_number.c_str(), REAL_SERIAL_NUMBER_SIZE);
+ SerialNumber[REAL_SERIAL_NUMBER_SIZE] = 0;
+ if (FT_Create(SerialNumber, FT_OPEN_BY_SERIAL_NUMBER, &capture_data->handle)) {
+ capture_error_print(print_failed, capture_data, "Create failed");
+ return false;
+ }
+
+ UCHAR buf[4] = {0x40, 0x80, 0x00, 0x00};
+ ULONG written = 0;
+
+ if (FT_WritePipe(capture_data->handle, BULK_OUT, buf, 4, &written, 0)) {
+ capture_error_print(print_failed, capture_data, "Write failed");
+ preemptive_close_connection(capture_data);
+ return false;
+ }
+
+ buf[1] = 0x00;
+
+ if (FT_WritePipe(capture_data->handle, BULK_OUT, buf, 4, &written, 0)) {
+ capture_error_print(print_failed, capture_data, "Write failed");
+ preemptive_close_connection(capture_data);
+ return false;
+ }
+
+ if (FT_SetStreamPipe(capture_data->handle, false, false, BULK_IN, get_capture_size(capture_data))) {
+ capture_error_print(print_failed, capture_data, "Stream failed");
+ preemptive_close_connection(capture_data);
+ return false;
+ }
+
+ if(!get_is_bad_ftd3xx()) {
+ if(FT_AbortPipe(capture_data->handle, BULK_IN)) {
+ capture_error_print(print_failed, capture_data, "Abort failed");
+ preemptive_close_connection(capture_data);
+ return false;
+ }
+
+ if (FT_SetStreamPipe(capture_data->handle, false, false, BULK_IN, get_capture_size(capture_data))) {
+ capture_error_print(print_failed, capture_data, "Stream failed");
+ preemptive_close_connection(capture_data);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void fast_capture_call(CaptureData* capture_data, OVERLAPPED overlap[NUM_CONCURRENT_DATA_BUFFERS]) {
+ int inner_curr_in = 0;
+ FT_STATUS ftStatus;
+ for (inner_curr_in = 0; inner_curr_in < NUM_CONCURRENT_DATA_BUFFERS; ++inner_curr_in) {
+ ftStatus = FT_InitializeOverlapped(capture_data->handle, &overlap[inner_curr_in]);
+ if (ftStatus) {
+ capture_error_print(true, capture_data, "Disconnected: Initialize failed");
+ return;
+ }
+ }
+
+ for (inner_curr_in = 0; inner_curr_in < NUM_CONCURRENT_DATA_BUFFERS - 1; ++inner_curr_in) {
+ ftStatus = FT_ASYNC_CALL(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&capture_data->capture_buf[inner_curr_in], get_capture_size(capture_data), &read_buffers[inner_curr_in], &overlap[inner_curr_in]);
+ if (ftStatus != FT_IO_PENDING) {
+ capture_error_print(true, capture_data, "Disconnected: Read failed");
+ return;
+ }
+ }
+
+ inner_curr_in = NUM_CONCURRENT_DATA_BUFFERS - 1;
+
+ auto clock_start = std::chrono::high_resolution_clock::now();
+
+ while (capture_data->status.connected && capture_data->status.running) {
+
+ ftStatus = FT_ASYNC_CALL(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&capture_data->capture_buf[inner_curr_in], get_capture_size(capture_data), &read_buffers[inner_curr_in], &overlap[inner_curr_in]);
+ if (ftStatus != FT_IO_PENDING) {
+ capture_error_print(true, capture_data, "Disconnected: Read failed");
+ return;
+ }
+
+ inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS;
+
+ ftStatus = FT_GetOverlappedResult(capture_data->handle, &overlap[inner_curr_in], &read_buffers[inner_curr_in], true);
+ if(FT_FAILED(ftStatus)) {
+ capture_error_print(true, capture_data, "Disconnected: USB error");
+ return;
+ }
+ const auto curr_time = std::chrono::high_resolution_clock::now();
+ const std::chrono::duration diff = curr_time - clock_start;
+ capture_data->time_in_buf[inner_curr_in] = diff.count();
+ capture_data->read[inner_curr_in] = read_buffers[inner_curr_in];
+ clock_start = curr_time;
+
+ 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 + 1) % NUM_CONCURRENT_DATA_BUFFERS;
+ // Signal that there is data available
+ capture_data->status.video_wait.unlock();
+ capture_data->status.audio_wait.unlock();
+ }
+}
+
+#if !(defined(_WIN32) || defined(_WIN64))
+static bool safe_capture_call(CaptureData* capture_data) {
+ int inner_curr_in = 0;
+
+ while(capture_data->status.connected && capture_data->status.running) {
+
+ auto clock_start = std::chrono::high_resolution_clock::now();
+
+ FT_STATUS ftStatus = FT_ReadPipeEx(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&capture_data->capture_buf[inner_curr_in], get_capture_size(capture_data), &read_buffers[inner_curr_in], 1000);
+ if(FT_FAILED(ftStatus)) {
+ capture_error_print(true, capture_data, "Disconnected: Read failed");
+ return true;
+ }
+
+ const auto curr_time = std::chrono::high_resolution_clock::now();
+ const std::chrono::duration diff = curr_time - clock_start;
+ capture_data->time_in_buf[inner_curr_in] = diff.count();
+ capture_data->read[inner_curr_in] = read_buffers[inner_curr_in];
+
+ inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS;
+ if(capture_data->status.cooldown_curr_in)
+ capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1;
+ capture_data->status.curr_in = inner_curr_in;
+ capture_data->status.video_wait.unlock();
+ capture_data->status.audio_wait.unlock();
+ }
+
+ return false;
+}
+#endif
+
+void ftdi_capture_main_loop(CaptureData* capture_data) {
+ #if !(defined(_WIN32) || defined(_WIN64))
+ if(!is_bad_ftd3xx)
+ fast_capture_call(capture_data, overlap);
+ else
+ safe_capture_call(capture_data);
+ #else
+ fast_capture_call(capture_data, overlap);
+ #endif
+}
+
+void ftdi_capture_cleanup(CaptureData* capture_data) {
+ FT_STATUS ftStatus;
+ if(!is_bad_ftd3xx) {
+ for(int inner_curr_in = 0; inner_curr_in < NUM_CONCURRENT_DATA_BUFFERS; ++inner_curr_in) {
+ ftStatus = FT_GetOverlappedResult(capture_data->handle, &overlap[inner_curr_in], &read_buffers[inner_curr_in], true);
+ if(FT_ReleaseOverlapped(capture_data->handle, &overlap[inner_curr_in])) {
+ capture_error_print(true, capture_data, "Disconnected: Release failed");
+ }
+ }
+ }
+
+ if(FT_AbortPipe(capture_data->handle, BULK_IN)) {
+ capture_error_print(true, capture_data, "Disconnected: Abort failed");
+ }
+
+ if (FT_Close(capture_data->handle)) {
+ capture_error_print(true, capture_data, "Disconnected: Close failed");
+ }
+}
+
+static inline void convertVideoToOutputChunk(FTDI3DSVideoInputData *p_in, VideoOutputData *p_out, int iters, int start_in, int start_out) {
+ for(int i = 0; i < iters; i++) {
+ for(int u = 0; u < 3; u++)
+ p_out->screen_data[start_out + i][u] = p_in->screen_data[start_in + i][u];
+ p_out->screen_data[start_out + i][3] = 0xff;
+ }
+}
+
+static inline void convertVideoToOutputChunk_3D(FTDI3DSVideoInputData_3D *p_in, VideoOutputData *p_out, int iters, int start_in, int start_out) {
+ for(int i = 0; i < iters; i++) {
+ for(int u = 0; u < 3; u++)
+ p_out->screen_data[start_out + i][u] = p_in->screen_data[start_in + i][u];
+ p_out->screen_data[start_out + i][3] = 0xff;
+ }
+}
+
+void ftdi_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, bool enabled_3d) {
+ if(!enabled_3d) {
+ convertVideoToOutputChunk(&p_in->ftdi_received.video_in, p_out, IN_VIDEO_NO_BOTTOM_SIZE_3DS, 0, 0);
+
+ for(int i = 0; i < ((IN_VIDEO_SIZE_3DS - IN_VIDEO_NO_BOTTOM_SIZE_3DS) / (IN_VIDEO_WIDTH_3DS * 2)); i++) {
+ convertVideoToOutputChunk(&p_in->ftdi_received.video_in, p_out, IN_VIDEO_WIDTH_3DS, ((i * 2) * IN_VIDEO_WIDTH_3DS) + IN_VIDEO_NO_BOTTOM_SIZE_3DS, TOP_SIZE_3DS + (i * IN_VIDEO_WIDTH_3DS));
+ convertVideoToOutputChunk(&p_in->ftdi_received.video_in, p_out, IN_VIDEO_WIDTH_3DS, (((i * 2) + 1) * IN_VIDEO_WIDTH_3DS) + IN_VIDEO_NO_BOTTOM_SIZE_3DS, IN_VIDEO_NO_BOTTOM_SIZE_3DS + (i * IN_VIDEO_WIDTH_3DS));
+ }
+ }
+}
+
diff --git a/source/Menus/ConnectionMenu.cpp b/source/Menus/ConnectionMenu.cpp
index 269c3e7..837354f 100755
--- a/source/Menus/ConnectionMenu.cpp
+++ b/source/Menus/ConnectionMenu.cpp
@@ -23,7 +23,7 @@ void ConnectionMenu::class_setup() {
this->show_title = true;
}
-void ConnectionMenu::insert_data(DevicesList *devices_list) {
+void ConnectionMenu::insert_data(std::vector *devices_list) {
this->devices_list = devices_list;
this->prepare_options();
}
@@ -37,11 +37,11 @@ void ConnectionMenu::set_output_option(int index, int action) {
}
int ConnectionMenu::get_num_options() {
- return this->devices_list->numValidDevices;
+ return this->devices_list->size();
}
std::string ConnectionMenu::get_string_option(int index, int action) {
- return std::string(&this->devices_list->serialNumbers[SERIAL_NUMBER_SIZE * index]);
+ return (*this->devices_list)[index].name + " - " + (*this->devices_list)[index].serial_number;
}
void ConnectionMenu::prepare(float menu_scaling_factor, int view_size_x, int view_size_y) {
diff --git a/source/WindowScreen.cpp b/source/WindowScreen.cpp
index b6582f5..fc7bfad 100755
--- a/source/WindowScreen.cpp
+++ b/source/WindowScreen.cpp
@@ -13,7 +13,8 @@
WindowScreen::WindowScreen(ScreenType stype, CaptureStatus* capture_status, DisplayData* display_data, AudioData* audio_data, ExtraButtonShortcuts* extra_button_shortcuts) {
this->m_stype = stype;
- insert_basic_crops(this->possible_crops, this->m_stype);
+ insert_basic_crops(this->possible_crops, this->m_stype, false);
+ insert_basic_crops(this->possible_crops_ds, this->m_stype, true);
insert_basic_pars(this->possible_pars);
this->m_prepare_save = 0;
this->m_prepare_load = 0;
@@ -27,7 +28,7 @@ WindowScreen::WindowScreen(ScreenType stype, CaptureStatus* capture_status, Disp
FPSArrayInit(&this->in_fps);
FPSArrayInit(&this->draw_fps);
FPSArrayInit(&this->poll_fps);
- this->in_tex.create(IN_VIDEO_WIDTH, IN_VIDEO_HEIGHT);
+ this->in_tex.create(MAX_IN_VIDEO_WIDTH, MAX_IN_VIDEO_HEIGHT);
this->m_in_rect_top.setTexture(&this->in_tex);
this->m_in_rect_bot.setTexture(&this->in_tex);
this->display_data = display_data;
@@ -72,8 +73,6 @@ void WindowScreen::build() {
if(this->m_stype == ScreenType::BOTTOM)
width = BOT_WIDTH_3DS;
- this->reload();
-
this->m_out_rect_top.out_tex.create(top_screen_size.x, top_screen_size.y);
this->m_out_rect_top.out_rect.setSize(top_screen_size);
this->m_out_rect_top.out_rect.setTexture(&this->m_out_rect_top.out_tex.getTexture());
@@ -83,6 +82,8 @@ void WindowScreen::build() {
this->m_out_rect_bot.out_rect.setTexture(&this->m_out_rect_bot.out_tex.getTexture());
this->m_view.setRotation(0);
+
+ this->reload();
}
void WindowScreen::reload() {
@@ -236,12 +237,23 @@ void WindowScreen::free_ownership_of_window(bool is_main_thread) {
}
void WindowScreen::resize_in_rect(sf::RectangleShape &in_rect, int start_x, int start_y, int width, int height) {
- in_rect.setTextureRect(sf::IntRect(start_y, start_x, height, width));
+ int target_x = start_x;
+ int target_y = start_y;
+ int target_width = width;
+ int target_height = height;
+ int target_rotation = -1 * this->capture_status->device.base_rotation;
- in_rect.setSize(sf::Vector2f(height, width));
- in_rect.setOrigin(height / 2, width / 2);
+ if((this->capture_status->device.base_rotation / 10) % 2) {
+ std::swap(target_x, target_y);
+ std::swap(target_width, target_height);
+ }
- in_rect.setRotation(-90);
+ in_rect.setTextureRect(sf::IntRect(target_x, target_y, target_width, target_height));
+
+ in_rect.setSize(sf::Vector2f(target_width, target_height));
+ in_rect.setOrigin(target_width / 2, target_height / 2);
+
+ in_rect.setRotation(target_rotation);
in_rect.setPosition(width / 2, height / 2);
}
@@ -351,7 +363,7 @@ void WindowScreen::window_factory(bool is_main_thread) {
std::string WindowScreen::title_factory() {
std::string title = this->win_title;
if(this->capture_status->connected)
- title += " - " + this->capture_status->serial_number;
+ title += " - " + this->capture_status->device.name + " - " + this->capture_status->device.serial_number;
return title;
}
@@ -359,7 +371,9 @@ void WindowScreen::pre_texture_conversion_processing() {
if(this->loaded_menu == CONNECT_MENU_TYPE)
return;
//Place preprocessing window-specific effects here
- this->in_tex.update((uint8_t*)this->saved_buf, IN_VIDEO_WIDTH, IN_VIDEO_HEIGHT, 0, 0);
+ if(!this->capture_status->connected)
+ return;
+ this->in_tex.update((uint8_t*)this->saved_buf, this->capture_status->device.width, this->capture_status->device.height, 0, 0);
}
void WindowScreen::post_texture_conversion_processing(out_rect_data &rect_data, const sf::RectangleShape &in_rect, bool actually_draw, bool is_top, bool is_debug) {
@@ -376,7 +390,7 @@ void WindowScreen::post_texture_conversion_processing(out_rect_data &rect_data,
}
else {
rect_data.out_tex.clear();
- if(actually_draw) {
+ if(this->capture_status->connected && actually_draw) {
rect_data.out_tex.draw(in_rect);
//Place postprocessing effects here
}
@@ -613,6 +627,10 @@ void WindowScreen::prepare_size_ratios(bool top_increase, bool bot_increase, boo
sf::Vector2f top_screen_size = getShownScreenSize(true, this->m_info.crop_kind);
sf::Vector2f bot_screen_size = getShownScreenSize(false, this->m_info.crop_kind);
+ if(this->display_data->last_connected_ds) {
+ top_screen_size = getShownScreenSize(true, this->m_info.crop_kind_ds);
+ bot_screen_size = getShownScreenSize(false, this->m_info.crop_kind_ds);
+ }
if(this->m_stype == ScreenType::TOP) {
bot_increase = true;
@@ -687,6 +705,10 @@ int WindowScreen::get_fullscreen_offset_y(int top_width, int top_height, int bot
void WindowScreen::resize_window_and_out_rects(bool do_work) {
sf::Vector2f top_screen_size = getShownScreenSize(true, this->loaded_info.crop_kind);
sf::Vector2f bot_screen_size = getShownScreenSize(false, this->loaded_info.crop_kind);
+ if(this->display_data->last_connected_ds) {
+ top_screen_size = getShownScreenSize(true, this->loaded_info.crop_kind_ds);
+ bot_screen_size = getShownScreenSize(false, this->loaded_info.crop_kind_ds);
+ }
int top_width = top_screen_size.x;
int top_height = top_screen_size.y;
float top_scaling = this->loaded_info.scaling;
@@ -796,26 +818,36 @@ void WindowScreen::rotate() {
}
sf::Vector2f WindowScreen::getShownScreenSize(bool is_top, int &crop_kind) {
- if(crop_kind >= this->possible_crops.size())
+ std::vector *crops = &this->possible_crops;
+ if(this->display_data->last_connected_ds)
+ crops = &this->possible_crops_ds;
+ if(crop_kind >= crops->size())
crop_kind = 0;
- int width = this->possible_crops[crop_kind]->top_width;
- int height = this->possible_crops[crop_kind]->top_height;
+ int width = (*crops)[crop_kind]->top_width;
+ int height = (*crops)[crop_kind]->top_height;
if(!is_top) {
- width = this->possible_crops[crop_kind]->bot_width;
- height = this->possible_crops[crop_kind]->bot_height;
+ width = (*crops)[crop_kind]->bot_width;
+ height = (*crops)[crop_kind]->bot_height;
}
return sf::Vector2f(width, height);
}
void WindowScreen::crop() {
- if(this->loaded_info.crop_kind >= this->possible_crops.size())
- this->loaded_info.crop_kind = 0;
+ std::vector *crops = &this->possible_crops;
+ int *crop_value = &this->loaded_info.crop_kind;
+ if(this->display_data->last_connected_ds) {
+ crops = &this->possible_crops_ds;
+ crop_value = &this->loaded_info.crop_kind_ds;
+ }
- sf::Vector2f top_screen_size = getShownScreenSize(true, this->loaded_info.crop_kind);
- sf::Vector2f bot_screen_size = getShownScreenSize(false, this->loaded_info.crop_kind);
+ if((*crop_value) >= crops->size())
+ *crop_value = 0;
- this->resize_in_rect(this->m_in_rect_top, this->possible_crops[this->loaded_info.crop_kind]->top_x, this->possible_crops[this->loaded_info.crop_kind]->top_y, top_screen_size.x, top_screen_size.y);
- this->resize_in_rect(this->m_in_rect_bot, TOP_WIDTH_3DS + this->possible_crops[this->loaded_info.crop_kind]->bot_x, this->possible_crops[this->loaded_info.crop_kind]->bot_y, bot_screen_size.x, bot_screen_size.y);
+ sf::Vector2f top_screen_size = getShownScreenSize(true, *crop_value);
+ sf::Vector2f bot_screen_size = getShownScreenSize(false, *crop_value);
+
+ this->resize_in_rect(this->m_in_rect_top, this->capture_status->device.top_screen_x + (*crops)[*crop_value]->top_x, this->capture_status->device.top_screen_y + (*crops)[*crop_value]->top_y, top_screen_size.x, top_screen_size.y);
+ this->resize_in_rect(this->m_in_rect_bot, this->capture_status->device.bot_screen_x + (*crops)[*crop_value]->bot_x, this->capture_status->device.bot_screen_y + (*crops)[*crop_value]->bot_y, bot_screen_size.x, bot_screen_size.y);
this->loaded_operations.call_screen_settings_update = true;
}
diff --git a/source/WindowScreen_Menu.cpp b/source/WindowScreen_Menu.cpp
index 9219c42..9361177 100755
--- a/source/WindowScreen_Menu.cpp
+++ b/source/WindowScreen_Menu.cpp
@@ -147,10 +147,16 @@ void WindowScreen::padding_change() {
}
void WindowScreen::crop_value_change(int new_crop_value) {
- int new_value = new_crop_value % this->possible_crops.size();
- if(this->m_info.crop_kind != new_value) {
- this->m_info.crop_kind = new_value;
- this->print_notification("Crop: " + this->possible_crops[this->m_info.crop_kind]->name);
+ std::vector *crops = &this->possible_crops;
+ int *crop_value = &this->m_info.crop_kind;
+ if(this->display_data->last_connected_ds) {
+ crops = &this->possible_crops_ds;
+ crop_value = &this->m_info.crop_kind_ds;
+ }
+ int new_value = new_crop_value % crops->size();
+ if((*crop_value) != new_value) {
+ *crop_value = new_value;
+ this->print_notification("Crop: " + (*crops)[*crop_value]->name);
this->prepare_size_ratios(false, false);
this->future_operations.call_crop = true;
}
@@ -289,7 +295,10 @@ bool WindowScreen::execute_cmd(PossibleWindowCommands id) {
this->fullscreen_change();
break;
case WINDOW_COMMAND_CROP:
- this->crop_value_change(this->m_info.crop_kind + 1);
+ if(this->display_data->last_connected_ds)
+ this->crop_value_change(this->m_info.crop_kind_ds + 1);
+ else
+ this->crop_value_change(this->m_info.crop_kind + 1);
break;
case WINDOW_COMMAND_ASYNC:
this->async_change();
@@ -567,7 +576,10 @@ void WindowScreen::setup_crop_menu(bool reset_data) {
this->curr_menu = CROP_MENU_TYPE;
if(reset_data)
this->crop_menu->reset_data();
- this->crop_menu->insert_data(&this->possible_crops);
+ if(this->display_data->last_connected_ds)
+ this->crop_menu->insert_data(&this->possible_crops_ds);
+ else
+ this->crop_menu->insert_data(&this->possible_crops);
this->last_menu_change_time = std::chrono::high_resolution_clock::now();
}
}
@@ -1628,7 +1640,7 @@ void WindowScreen::poll(bool do_everything) {
}
}
-void WindowScreen::setup_connection_menu(DevicesList *devices_list, bool reset_data) {
+void WindowScreen::setup_connection_menu(std::vector *devices_list, bool reset_data) {
// Skip the check here. It's special.
this->curr_menu = CONNECT_MENU_TYPE;
if(reset_data)
@@ -1644,6 +1656,13 @@ void WindowScreen::end_connection_menu() {
this->setup_no_menu();
}
+void WindowScreen::update_ds_3ds_connection(bool changed_type) {
+ if(changed_type && (this->curr_menu == CROP_MENU_TYPE))
+ this->setup_no_menu();
+ this->prepare_size_ratios(false, false);
+ this->future_operations.call_crop = true;
+}
+
int WindowScreen::load_data() {
int ret_val = this->m_prepare_load;
this->m_prepare_load = 0;
diff --git a/source/cc3dsfs.cpp b/source/cc3dsfs.cpp
index 894a6a9..e00dbe3 100755
--- a/source/cc3dsfs.cpp
+++ b/source/cc3dsfs.cpp
@@ -19,7 +19,7 @@
#include
#include "utils.hpp"
-#include "3dscapture.hpp"
+#include "devicecapture.hpp"
#include "hw_defs.hpp"
#include "frontend.hpp"
#include "audio.hpp"
@@ -52,7 +52,7 @@ static void UpdateOutText(OutTextData &out_text_data, std::string full_text, std
static void SuccessConnectionOutTextGenerator(OutTextData &out_text_data, CaptureData* capture_data) {
if(capture_data->status.connected)
- UpdateOutText(out_text_data, "Connected to " + capture_data->status.serial_number, "Connected", TEXT_KIND_SUCCESS);
+ UpdateOutText(out_text_data, "Connected to " + capture_data->status.device.name + " - " + capture_data->status.device.serial_number, "Connected", TEXT_KIND_SUCCESS);
else if(!capture_data->status.new_error_text) {
UpdateOutText(out_text_data, "Disconnected", "Disconnected", TEXT_KIND_WARNING);
}
@@ -96,6 +96,11 @@ static bool load(const std::string path, const std::string name, ScreenInfo &top
continue;
}
+ if(key == "last_connected_ds") {
+ display_data.last_connected_ds = std::stoi(value);
+ continue;
+ }
+
if(key == "extra_button_enter_short") {
extra_button_shortcuts->enter_shortcut = get_window_command(static_cast(std::stoi(value)));
continue;
@@ -175,6 +180,7 @@ static bool save(const std::string path, const std::string name, const std::stri
file << save_screen_info("top_", top_info);
file << "split=" << display_data.split << std::endl;
file << "fast_poll=" << display_data.fast_poll << std::endl;
+ file << "last_connected_ds=" << display_data.last_connected_ds << std::endl;
file << "extra_button_enter_short=" << extra_button_shortcuts->enter_shortcut->cmd << std::endl;
file << "extra_button_page_up_short=" << extra_button_shortcuts->page_up_shortcut->cmd << std::endl;
file << audio_data->save_audio_data();
@@ -208,19 +214,16 @@ static void soundCall(AudioData *audio_data, CaptureData* capture_data) {
volatile int loaded_samples;
while(capture_data->status.running) {
- if(capture_data->status.connected) {
+ if(capture_data->status.connected && capture_data->status.device.has_audio) {
bool timed_out = !capture_data->status.audio_wait.timed_lock();
curr_out = (capture_data->status.curr_in - 1 + NUM_CONCURRENT_DATA_BUFFERS) % NUM_CONCURRENT_DATA_BUFFERS;
if((!capture_data->status.cooldown_curr_in) && (curr_out != prev_out)) {
loaded_samples = audio.samples.size();
- if((capture_data->read[curr_out] > sizeof(VideoInputData)) && (loaded_samples < AUDIO_LATENCY_LIMIT)) {
- int n_samples = (capture_data->read[curr_out] - sizeof(VideoInputData)) / 2;
- if(n_samples > MAX_SAMPLES_IN) {
- n_samples = MAX_SAMPLES_IN;
- }
+ if((capture_data->read[curr_out] > get_video_in_size(capture_data)) && (loaded_samples < AUDIO_LATENCY_LIMIT)) {
+ int n_samples = get_audio_n_samples(capture_data, capture_data->read[curr_out]);
double out_time = capture_data->time_in_buf[curr_out];
- convertAudioToOutput(&capture_data->capture_buf[curr_out], out_buf[audio_buf_counter], n_samples, endianness);
+ convertAudioToOutput(&capture_data->capture_buf[curr_out], out_buf[audio_buf_counter], n_samples, endianness, capture_data);
audio.samples.emplace(out_buf[audio_buf_counter], n_samples, out_time);
if(++audio_buf_counter == NUM_CONCURRENT_AUDIO_BUFFERS) {
audio_buf_counter = 0;
@@ -336,8 +339,8 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
if((!capture_data->status.cooldown_curr_in) && (curr_out != prev_out)) {
last_frame_time = capture_data->time_in_buf[curr_out];
- if(capture_data->read[curr_out] >= sizeof(VideoInputData)) {
- convertVideoToOutput(&capture_data->capture_buf[curr_out].video_data, out_buf);
+ if(capture_data->read[curr_out] >= get_video_in_size(capture_data)) {
+ convertVideoToOutput(&capture_data->capture_buf[curr_out], out_buf, capture_data);
num_allowed_blanks = MAX_ALLOWED_BLANKS;
}
else {
@@ -383,8 +386,12 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
check_close_application(bot_screen, capture_data, ret_val);
check_close_application(joint_screen, capture_data, ret_val);
- if((load_index = top_screen.load_data()) || (load_index = bot_screen.load_data()) || (load_index = joint_screen.load_data()))
+ if((load_index = top_screen.load_data()) || (load_index = bot_screen.load_data()) || (load_index = joint_screen.load_data())) {
+ // This value should only be loaded when starting the program...
+ bool previous_last_connected_ds = frontend_data.display_data.last_connected_ds;
load_layout_file(load_index, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, skip_io, true);
+ frontend_data.display_data.last_connected_ds = previous_last_connected_ds;
+ }
if((save_index = top_screen.save_data()) || (save_index = bot_screen.save_data()) || (save_index = joint_screen.save_data())) {
save_layout_file(save_index, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, skip_io, true);
@@ -496,6 +503,7 @@ int main(int argc, char **argv) {
AudioData audio_data;
audio_data.reset();
CaptureData* capture_data = new CaptureData;
+ capture_init();
std::thread capture_thread(captureCall, capture_data);
std::thread audio_thread(soundCall, &audio_data, capture_data);
@@ -505,6 +513,7 @@ int main(int argc, char **argv) {
capture_thread.join();
delete capture_data;
end_extra_buttons_poll();
+ capture_close();
return ret_val;
}
diff --git a/source/conversions.cpp b/source/conversions.cpp
index 59a5dc3..88cc430 100755
--- a/source/conversions.cpp
+++ b/source/conversions.cpp
@@ -1,30 +1,38 @@
-#include
#include "conversions.hpp"
+#include "devicecapture.hpp"
+#include "3dscapture_ftdi.hpp"
+#include "usb_ds_3ds_capture.hpp"
#include
-static inline void convertVideoToOutputChunk(VideoInputData *p_in, VideoOutputData *p_out, int iters, int start_in, int start_out) {
- for(int i = 0; i < iters; i++) {
- for(int u = 0; u < 3; u++)
- p_out->screen_data[start_out + i][u] = p_in->screen_data[start_in + i][u];
- p_out->screen_data[start_out + i][3] = 0xff;
- }
+void convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureData* capture_data) {
+ #ifdef USE_FTDI
+ if(capture_data->status.device.is_ftdi)
+ ftdi_convertVideoToOutput(p_in, p_out, capture_data->status.enabled_3d);
+ #endif
+ #ifdef USE_USB
+ if(!capture_data->status.device.is_ftdi)
+ usb_convertVideoToOutput(p_in, p_out, &capture_data->status.device, capture_data->status.enabled_3d);
+ #endif
}
-void convertVideoToOutput(VideoInputData *p_in, VideoOutputData *p_out) {
- convertVideoToOutputChunk(p_in, p_out, IN_VIDEO_NO_BOTTOM_SIZE, 0, 0);
-
- for(int i = 0; i < ((IN_VIDEO_SIZE - IN_VIDEO_NO_BOTTOM_SIZE) / (IN_VIDEO_WIDTH * 2)); i++) {
- convertVideoToOutputChunk(p_in, p_out, IN_VIDEO_WIDTH, ((i * 2) * IN_VIDEO_WIDTH) + IN_VIDEO_NO_BOTTOM_SIZE, TOP_SIZE_3DS + (i * IN_VIDEO_WIDTH));
- convertVideoToOutputChunk(p_in, p_out, IN_VIDEO_WIDTH, (((i * 2) + 1) * IN_VIDEO_WIDTH) + IN_VIDEO_NO_BOTTOM_SIZE, IN_VIDEO_NO_BOTTOM_SIZE + (i * IN_VIDEO_WIDTH));
+void convertAudioToOutput(CaptureReceived *p_in, sf::Int16 *p_out, uint64_t n_samples, const bool is_big_endian, CaptureData* capture_data) {
+ if(!capture_data->status.device.has_audio)
+ return;
+ uint8_t* base_ptr = NULL;
+ #ifdef USE_FTDI
+ if(capture_data->status.device.is_ftdi) {
+ if(!capture_data->status.enabled_3d)
+ base_ptr = (uint8_t*)p_in->ftdi_received.audio_data;
+ else
+ base_ptr = (uint8_t*)p_in->ftdi_received_3d.audio_data;
}
-}
-
-void convertAudioToOutput(CaptureReceived *p_in, sf::Int16 *p_out, const int n_samples, const bool is_big_endian) {
+ #endif
+ if(base_ptr == NULL)
+ return;
if(!is_big_endian)
- memcpy(p_out, p_in->audio_data, n_samples * 2);
+ memcpy(p_out, base_ptr, n_samples * 2);
else
- for(int i = 0; i < n_samples; i++)
- p_out[i] = ((p_in->audio_data[i] & 0xFF) << 8) | (p_in->audio_data[i] >> 8);
+ for(int i = 0; i < n_samples; i += 2)
+ p_out[i] = (base_ptr[i + 1] << 8) | base_ptr[i];
}
-
diff --git a/source/devicecapture.cpp b/source/devicecapture.cpp
new file mode 100755
index 0000000..5d35771
--- /dev/null
+++ b/source/devicecapture.cpp
@@ -0,0 +1,186 @@
+#include "devicecapture.hpp"
+#include "3dscapture_ftdi.hpp"
+#include "usb_ds_3ds_capture.hpp"
+
+#include
+#include
+#include
+#include
+
+#define CONNECTION_NO_DEVICE_SELECTED (-1)
+
+static bool poll_connection_window_screen(WindowScreen *screen, int &chosen_index) {
+ screen->poll();
+ if(screen->check_connection_menu_result() != CONNECTION_MENU_NO_ACTION) {
+ chosen_index = screen->check_connection_menu_result();
+ return true;
+ }
+ if(screen->close_capture()) {
+ return true;
+ }
+ return false;
+}
+
+static int choose_device(std::vector *devices_list, FrontendData* frontend_data) {
+ if(devices_list->size() == 1)
+ return 0;
+ int chosen_index = CONNECTION_NO_DEVICE_SELECTED;
+ frontend_data->top_screen->setup_connection_menu(devices_list);
+ frontend_data->bot_screen->setup_connection_menu(devices_list);
+ frontend_data->joint_screen->setup_connection_menu(devices_list);
+ bool done = false;
+ while(!done) {
+ update_output(frontend_data);
+ done = poll_connection_window_screen(frontend_data->top_screen, chosen_index);
+ if(done)
+ break;
+ done = poll_connection_window_screen(frontend_data->bot_screen, chosen_index);
+ if(done)
+ break;
+ done = poll_connection_window_screen(frontend_data->joint_screen, chosen_index);
+ if(done)
+ break;
+ default_sleep();
+ }
+ frontend_data->top_screen->end_connection_menu();
+ frontend_data->bot_screen->end_connection_menu();
+ frontend_data->joint_screen->end_connection_menu();
+ return chosen_index;
+}
+
+void capture_error_print(bool print_failed, CaptureData* capture_data, std::string error_string) {
+ if(print_failed) {
+ capture_data->status.error_text = error_string;
+ capture_data->status.new_error_text = true;
+ }
+}
+
+bool connect(bool print_failed, CaptureData* capture_data, FrontendData* frontend_data) {
+ capture_data->status.new_error_text = false;
+ if (capture_data->status.connected) {
+ capture_data->status.close_success = false;
+ return false;
+ }
+
+ if(!capture_data->status.close_success) {
+ capture_error_print(print_failed, capture_data, "Previous device still closing...");
+ return false;
+ }
+
+ // Device Listing
+ std::vector devices_list;
+ #ifdef USE_FTDI
+ list_devices_ftdi(devices_list);
+ #endif
+ #ifdef USE_USB
+ list_devices_usb_ds_3ds(devices_list);
+ #endif
+
+ if(devices_list.size() <= 0) {
+ capture_error_print(print_failed, capture_data, "No device was found");
+ return false;
+ }
+
+ int chosen_device = choose_device(&devices_list, frontend_data);
+ if(chosen_device == CONNECTION_NO_DEVICE_SELECTED) {
+ capture_error_print(print_failed, capture_data, "No device was selected");
+ return false;
+ }
+
+ // Actual connection
+ #ifdef USE_FTDI
+ if(devices_list[chosen_device].is_ftdi && (!connect_ftdi(print_failed, capture_data, &devices_list[chosen_device])))
+ return false;
+ #endif
+ #ifdef USE_USB
+ if((!devices_list[chosen_device].is_ftdi) && (!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];
+
+ // Avoid having old open locks
+ capture_data->status.video_wait.try_lock();
+ capture_data->status.audio_wait.try_lock();
+
+ return true;
+}
+
+void captureCall(CaptureData* capture_data) {
+ int inner_curr_in = 0;
+ capture_data->status.curr_in = inner_curr_in;
+ capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM;
+
+ while(capture_data->status.running) {
+ if (!capture_data->status.connected) {
+ default_sleep();
+ continue;
+ }
+
+ // Main capture loop
+ #ifdef USE_FTDI
+ if(capture_data->status.device.is_ftdi)
+ ftdi_capture_main_loop(capture_data);
+ #endif
+ #ifdef USE_USB
+ if(!capture_data->status.device.is_ftdi)
+ usb_capture_main_loop(capture_data);
+ #endif
+
+ capture_data->status.close_success = false;
+ capture_data->status.connected = false;
+ capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM;
+ // Needed in case the threads went in the connected loop
+ // before it is set right above, and they are waiting on the locks!
+ capture_data->status.video_wait.unlock();
+ capture_data->status.audio_wait.unlock();
+
+ // Capture cleanup
+ #ifdef USE_FTDI
+ if(capture_data->status.device.is_ftdi)
+ ftdi_capture_cleanup(capture_data);
+ #endif
+ #ifdef USE_USB
+ if(!capture_data->status.device.is_ftdi)
+ usb_capture_cleanup(capture_data);
+ #endif
+
+ capture_data->status.close_success = false;
+ capture_data->status.connected = false;
+
+ capture_data->status.close_success = true;
+ }
+}
+
+uint64_t get_audio_n_samples(CaptureData* capture_data, uint64_t read) {
+ if(!capture_data->status.device.has_audio)
+ return 0;
+ uint64_t n_samples = (read - get_video_in_size(capture_data)) / 2;
+ if(n_samples > capture_data->status.device.max_samples_in)
+ n_samples = capture_data->status.device.max_samples_in;
+ return n_samples;
+}
+
+uint64_t get_video_in_size(CaptureData* capture_data) {
+ #ifdef USE_FTDI
+ if(capture_data->status.device.is_ftdi)
+ return ftdi_get_video_in_size(capture_data);
+ #endif
+ #ifdef USE_USB
+ if(!capture_data->status.device.is_ftdi)
+ return usb_get_video_in_size(capture_data);
+ #endif
+ return 0;
+}
+
+void capture_init() {
+ #ifdef USE_USB
+ usb_init();
+ #endif
+}
+
+void capture_close() {
+ #ifdef USE_USB
+ usb_close();
+ #endif
+}
diff --git a/source/frontend.cpp b/source/frontend.cpp
index f948831..2622b6d 100755
--- a/source/frontend.cpp
+++ b/source/frontend.cpp
@@ -7,6 +7,7 @@ const CropData default_3ds_crop = {
.bot_width = BOT_WIDTH_3DS, .bot_height = HEIGHT_3DS,
.bot_x = 0, .bot_y = 0,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = true,
+.allowed_3ds = true, .allowed_ds = false,
.name = "3DS"};
const CropData special_ds_crop = {
@@ -15,6 +16,7 @@ const CropData special_ds_crop = {
.bot_width = BOT_WIDTH_3DS, .bot_height = HEIGHT_3DS,
.bot_x = 0, .bot_y = 0,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = false,
+.allowed_3ds = true, .allowed_ds = false,
.name = "16:10"};
const CropData scaled_ds_crop = {
@@ -23,6 +25,7 @@ const CropData scaled_ds_crop = {
.bot_width = BOT_WIDTH_3DS, .bot_height = HEIGHT_3DS,
.bot_x = 0, .bot_y = 0,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = false,
+.allowed_3ds = true, .allowed_ds = false,
.name = "Scaled DS"};
const CropData native_ds_crop = {
@@ -31,6 +34,7 @@ const CropData native_ds_crop = {
.bot_width = WIDTH_DS, .bot_height = HEIGHT_DS,
.bot_x = (BOT_WIDTH_3DS - WIDTH_DS) / 2, .bot_y = HEIGHT_3DS - HEIGHT_DS,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = true,
+.allowed_3ds = true, .allowed_ds = false,
.name = "Native DS"};
const CropData scaled_gba_crop = {
@@ -39,6 +43,7 @@ const CropData scaled_gba_crop = {
.bot_width = 0, .bot_height = 0,
.bot_x = 0, .bot_y = 0,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = false,
+.allowed_3ds = true, .allowed_ds = false,
.name = "Scaled GBA"};
const CropData native_gba_crop = {
@@ -47,6 +52,7 @@ const CropData native_gba_crop = {
.bot_width = 0, .bot_height = 0,
.bot_x = 0, .bot_y = 0,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = false,
+.allowed_3ds = true, .allowed_ds = false,
.name = "Native GBA"};
const CropData scaled_vc_gb_crop = {
@@ -55,6 +61,7 @@ const CropData scaled_vc_gb_crop = {
.bot_width = 0, .bot_height = 0,
.bot_x = 0, .bot_y = 0,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = false,
+.allowed_3ds = true, .allowed_ds = false,
.name = "Scaled VC GB"};
const CropData vc_gb_crop = {
@@ -63,6 +70,7 @@ const CropData vc_gb_crop = {
.bot_width = 0, .bot_height = 0,
.bot_x = 0, .bot_y = 0,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = false,
+.allowed_3ds = true, .allowed_ds = false,
.name = "VC GB"};
const CropData scaled_snes_crop = {
@@ -71,6 +79,7 @@ const CropData scaled_snes_crop = {
.bot_width = 0, .bot_height = 0,
.bot_x = 0, .bot_y = 0,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = false,
+.allowed_3ds = true, .allowed_ds = false,
.name = "Scaled SNES"};
const CropData vc_snes_crop = {
@@ -79,6 +88,7 @@ const CropData vc_snes_crop = {
.bot_width = 0, .bot_height = 0,
.bot_x = 0, .bot_y = 0,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = false,
+.allowed_3ds = true, .allowed_ds = false,
.name = "VC SNES"};
const CropData vc_nes_crop = {
@@ -87,8 +97,36 @@ const CropData vc_nes_crop = {
.bot_width = 0, .bot_height = 0,
.bot_x = 0, .bot_y = 0,
.allowed_joint = true, .allowed_top = true, .allowed_bottom = false,
+.allowed_3ds = true, .allowed_ds = false,
.name = "VC NES"};
+const CropData default_ds_crop = {
+.top_width = WIDTH_DS, .top_height = HEIGHT_DS,
+.top_x = 0, .top_y = 0,
+.bot_width = WIDTH_DS, .bot_height = HEIGHT_DS,
+.bot_x = 0, .bot_y = 0,
+.allowed_joint = true, .allowed_top = true, .allowed_bottom = true,
+.allowed_3ds = false, .allowed_ds = true,
+.name = "DS"};
+
+const CropData top_gba_ds_crop = {
+.top_width = WIDTH_GBA, .top_height = HEIGHT_GBA,
+.top_x = (WIDTH_DS - WIDTH_GBA) / 2, .top_y = (HEIGHT_DS - HEIGHT_GBA) / 2,
+.bot_width = 0, .bot_height = 0,
+.bot_x = 0, .bot_y = 0,
+.allowed_joint = true, .allowed_top = true, .allowed_bottom = false,
+.allowed_3ds = false, .allowed_ds = true,
+.name = "Top GBA"};
+
+const CropData bottom_gba_ds_crop = {
+.top_width = 0, .top_height = 0,
+.top_x = 0, .top_y = 0,
+.bot_width = WIDTH_GBA, .bot_height = HEIGHT_GBA,
+.bot_x = (WIDTH_DS - WIDTH_GBA) / 2, .bot_y = (HEIGHT_DS - HEIGHT_GBA) / 2,
+.allowed_joint = true, .allowed_top = false, .allowed_bottom = true,
+.allowed_3ds = false, .allowed_ds = true,
+.name = "Bottom GBA"};
+
static const CropData* basic_possible_crops[] = {
&default_3ds_crop,
&special_ds_crop,
@@ -101,6 +139,9 @@ static const CropData* basic_possible_crops[] = {
&scaled_snes_crop,
&vc_snes_crop,
&vc_nes_crop,
+&default_ds_crop,
+&top_gba_ds_crop,
+&bottom_gba_ds_crop,
};
const PARData base_par = {
@@ -136,19 +177,23 @@ static const PARData* basic_possible_pars[] = {
//&special_3ds_vertical_par,
};
-bool is_allowed_crop(const CropData* crop_data, ScreenType s_type) {
- if(s_type == ScreenType::JOINT && crop_data->allowed_joint)
- return true;
- if(s_type == ScreenType::TOP && crop_data->allowed_top)
- return true;
- if(s_type == ScreenType::BOTTOM && crop_data->allowed_bottom)
- return true;
- return false;
+static bool is_allowed_crop(const CropData* crop_data, ScreenType s_type, bool is_ds) {
+ if(is_ds && (!crop_data->allowed_ds))
+ return false;
+ if((!is_ds) && (!crop_data->allowed_3ds))
+ return false;
+ if((s_type == ScreenType::JOINT) && (!crop_data->allowed_joint))
+ return false;
+ if((s_type == ScreenType::TOP) && (!crop_data->allowed_top))
+ return false;
+ if((s_type == ScreenType::BOTTOM) && (!crop_data->allowed_bottom))
+ return false;
+ return true;
}
-void insert_basic_crops(std::vector &crop_vector, ScreenType s_type) {
+void insert_basic_crops(std::vector &crop_vector, ScreenType s_type, bool is_ds) {
for(int i = 0; i < (sizeof(basic_possible_crops) / sizeof(basic_possible_crops[0])); i++) {
- if(is_allowed_crop(basic_possible_crops[i], s_type))
+ if(is_allowed_crop(basic_possible_crops[i], s_type, is_ds))
crop_vector.push_back(basic_possible_crops[i]);
}
}
@@ -162,6 +207,7 @@ void insert_basic_pars(std::vector &par_vector) {
void reset_display_data(DisplayData *display_data) {
display_data->split = false;
display_data->fast_poll = false;
+ display_data->last_connected_ds = false;
}
void reset_fullscreen_info(ScreenInfo &info) {
@@ -173,6 +219,7 @@ void reset_fullscreen_info(ScreenInfo &info) {
void reset_screen_info(ScreenInfo &info) {
info.is_blurred = false;
info.crop_kind = 0;
+ info.crop_kind_ds = 0;
info.scaling = 1.0;
info.is_fullscreen = false;
info.bottom_pos = UNDER_TOP;
@@ -214,6 +261,10 @@ bool load_screen_info(std::string key, std::string value, std::string base, Scre
info.crop_kind = std::stoi(value);
return true;
}
+ if(key == (base + "crop_ds")) {
+ info.crop_kind_ds = std::stoi(value);
+ return true;
+ }
if(key == (base + "scale")) {
info.scaling = std::stod(value);
if(info.scaling < 1.25)
@@ -335,6 +386,7 @@ std::string save_screen_info(std::string base, const ScreenInfo &info) {
std::string out = "";
out += base + "blur=" + std::to_string(info.is_blurred) + "\n";
out += base + "crop=" + std::to_string(info.crop_kind) + "\n";
+ out += base + "crop_ds=" + std::to_string(info.crop_kind_ds) + "\n";
out += base + "scale=" + std::to_string(info.scaling) + "\n";
out += base + "fullscreen=" + std::to_string(info.is_fullscreen) + "\n";
out += base + "bot_pos=" + std::to_string(info.bottom_pos) + "\n";
@@ -428,8 +480,11 @@ JoystickAction get_joystick_action(uint32_t joystickId, uint32_t joy_button) {
return JOY_ACTION_NONE;
}
-void default_sleep() {
- sf::sleep(sf::milliseconds(1000/USB_CHECKS_PER_SECOND));
+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) {
@@ -448,6 +503,37 @@ void update_output(FrontendData* frontend_data, double frame_time, VideoOutputDa
frontend_data->joint_screen->draw(frame_time, out_buf);
}
+static bool are_cc_device_screens_same(const CaptureDevice &old_cc_device, const CaptureDevice &new_cc_device) {
+ if(old_cc_device.width != new_cc_device.width)
+ return false;
+ if(old_cc_device.height != new_cc_device.height)
+ return false;
+ if(old_cc_device.base_rotation != new_cc_device.base_rotation)
+ return false;
+ if(old_cc_device.top_screen_x != new_cc_device.top_screen_x)
+ return false;
+ if(old_cc_device.top_screen_y != new_cc_device.top_screen_y)
+ return false;
+ if(old_cc_device.bot_screen_x != new_cc_device.bot_screen_x)
+ return false;
+ if(old_cc_device.bot_screen_y != new_cc_device.bot_screen_y)
+ return false;
+ return true;
+}
+
+void update_connected_3ds_ds(FrontendData* frontend_data, const CaptureDevice &old_cc_device, const CaptureDevice &new_cc_device) {
+ bool old_connected = frontend_data->display_data.last_connected_ds;
+ frontend_data->display_data.last_connected_ds = !new_cc_device.is_3ds;
+ bool changed_type = old_connected != frontend_data->display_data.last_connected_ds;
+ bool display_data_updated = !are_cc_device_screens_same(old_cc_device, new_cc_device);
+ if(display_data_updated || changed_type) {
+ frontend_data->top_screen->update_ds_3ds_connection(changed_type);
+ frontend_data->bot_screen->update_ds_3ds_connection(changed_type);
+ frontend_data->joint_screen->update_ds_3ds_connection(changed_type);
+ }
+
+}
+
void screen_display_thread(WindowScreen *screen) {
screen->display_thread();
}
diff --git a/source/usb_ds_3ds_capture.cpp b/source/usb_ds_3ds_capture.cpp
new file mode 100755
index 0000000..11e31f8
--- /dev/null
+++ b/source/usb_ds_3ds_capture.cpp
@@ -0,0 +1,462 @@
+#include "usb_ds_3ds_capture.hpp"
+#include "devicecapture.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+// Adapted from sample code provided by Loopy
+
+#define REAL_SERIAL_NUMBER_SIZE 32
+#define SERIAL_NUMBER_SIZE (REAL_SERIAL_NUMBER_SIZE+1)
+
+#define FIRST_3D_VERSION 6
+#define CAPTURE_SKIP_TIMEOUT_SECONDS 1.0
+#define USE_SHARED_SPECIAL_BIT_OLDDS
+
+enum usb_capture_status {
+ USB_CAPTURE_SUCCESS = 0,
+ USB_CAPTURE_SKIP,
+ USB_CAPTURE_PIPE_ERROR,
+ USB_CAPTURE_FRAMEINFO_ERROR,
+ USB_CAPTURE_ERROR
+};
+
+struct usb_device {
+ bool is_3ds;
+ int bpp;
+ int vid;
+ int pid;
+ int default_config;
+ int capture_interface;
+ int control_timeout;
+ int bulk_timeout;
+ int ep2_in;
+ int cmdin_status;
+ int cmdin_frameinfo;
+ int cmdout_capture_start;
+ int cmdout_capture_stop;
+ int cmdin_i2c_read;
+ int cmdout_i2c_write;
+ int i2caddr_3dsconfig;
+ int bitstream_3dscfg_ver;
+};
+
+struct usb_ds_status {
+ uint32_t framecount;
+ uint8_t lcd_on;
+ uint8_t capture_in_progress;
+};
+
+static const usb_device usb_3ds_desc = {
+.is_3ds = true, .bpp = IN_VIDEO_BPP_SIZE_3DS,
+.vid = 0x16D0, .pid = 0x06A3,
+.default_config = 1, .capture_interface = 0,
+.control_timeout = 30, .bulk_timeout = 50,
+.ep2_in = 2 | LIBUSB_ENDPOINT_IN,
+.cmdin_status = 0, .cmdin_frameinfo = 0,
+.cmdout_capture_start = 0x40, .cmdout_capture_stop = 0,
+.cmdin_i2c_read = 0x21, .cmdout_i2c_write = 0x21,
+.i2caddr_3dsconfig = 0x14, .bitstream_3dscfg_ver = 1
+};
+
+static const usb_device usb_old_ds_desc = {
+.is_3ds = false, .bpp = IN_VIDEO_BPP_SIZE_DS_BASIC,
+.vid = 0x16D0, .pid = 0x0647,
+.default_config = 1, .capture_interface = 0,
+.control_timeout = 500, .bulk_timeout = 500,
+.ep2_in = 2 | LIBUSB_ENDPOINT_IN,
+.cmdin_status = 0x31, .cmdin_frameinfo = 0x30,
+.cmdout_capture_start = 0x30, .cmdout_capture_stop = 0x31,
+.cmdin_i2c_read = 0, .cmdout_i2c_write = 0,
+.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);
+}
+
+// Write vendor request to control endpoint. Returns bytes transferred (<0 = libusb error)
+static int vend_out(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_OUT), bRequest, wValue, wIndex, buf, wLength, usb_device_desc->control_timeout);
+}
+
+// Read from bulk endpoint. Returns libusb error code
+static int bulk_in(libusb_device_handle *handle, const 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);
+}
+
+// Read FPGA configuration regs
+static bool read_config(libusb_device_handle *handle, const usb_device* usb_device_desc, uint8_t cfgAddr, uint8_t *buf, int count) {
+ if(!usb_device_desc->is_3ds)
+ return false;
+ if((count <= 0) || (count >= 256))
+ return false;
+ if(vend_out(handle, usb_device_desc, usb_device_desc->cmdout_i2c_write, usb_device_desc->i2caddr_3dsconfig, 0, 1, &cfgAddr) <= 0)
+ return false;
+ return vend_in(handle, usb_device_desc, usb_device_desc->cmdin_i2c_read, usb_device_desc->i2caddr_3dsconfig, 0, count, buf) == count;
+}
+
+static uint8_t capture_get_version(libusb_device_handle *handle, const usb_device* usb_device_desc) {
+ if(!usb_device_desc->is_3ds)
+ return 0;
+ uint8_t version=0;
+ read_config(handle, usb_device_desc, usb_device_desc->bitstream_3dscfg_ver, &version, 1);
+ return version;
+}
+
+static bool capture_get_has_3d(libusb_device_handle *handle, const usb_device* usb_device_desc) {
+ if(!usb_device_desc->is_3ds)
+ return false;
+ return capture_get_version(handle, usb_device_desc) >= FIRST_3D_VERSION;
+}
+
+static const usb_device* _get_usb_device_desc(CaptureDevice* device) {
+ const usb_device* usb_device_desc = &usb_3ds_desc;
+ if(!device->is_3ds) {
+ if(device->rgb_bits_size == usb_old_ds_desc.bpp)
+ usb_device_desc = &usb_old_ds_desc;
+ }
+ return usb_device_desc;
+}
+
+static const usb_device* get_usb_device_desc(CaptureData* capture_data) {
+ return _get_usb_device_desc(&capture_data->status.device);
+}
+
+static bool insert_device(std::vector &devices_list, const usb_device* usb_device_desc, libusb_device *usb_device, libusb_device_descriptor *usb_descriptor, int &curr_serial_extra_id) {
+ libusb_device_handle *handle = NULL;
+ uint8_t data[SERIAL_NUMBER_SIZE];
+ if((usb_descriptor->idVendor != usb_device_desc->vid) || (usb_descriptor->idProduct != usb_device_desc->pid))
+ return false;
+ 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;
+ if(usb_device_desc->is_3ds)
+ devices_list.emplace_back(serial_str, "3DS", false, true, capture_get_has_3d(handle, usb_device_desc), false, HEIGHT_3DS, TOP_WIDTH_3DS + BOT_WIDTH_3DS, 0, usb_device_desc->bpp, 90, 0, 0, TOP_WIDTH_3DS, 0);
+ else {
+ bool has_audio = false;
+ int max_samples_in = 0;
+ if(usb_device_desc->bpp > IN_VIDEO_BPP_SIZE_DS_BASIC) {
+ has_audio = true;
+ max_samples_in = DS_SAMPLES_IN;
+ }
+ devices_list.emplace_back(serial_str, "DS", false, false, false, has_audio, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, max_samples_in, usb_device_desc->bpp, 0, 0, 0, 0, HEIGHT_DS);
+ }
+ libusb_close(handle);
+ return true;
+}
+
+static libusb_device_handle* usb_find_by_serial_number(const usb_device* usb_device_desc, std::string &serial_number) {
+ if(!usb_initialized)
+ return NULL;
+ libusb_device **usb_devices;
+ int num_devices = libusb_get_device_list(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;
+ 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;
+ 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;
+}
+
+static void capture_end(libusb_device_handle *dev, const usb_device* usb_device_desc, bool interface_claimed = true) {
+ if(interface_claimed)
+ libusb_release_interface(dev, usb_device_desc->capture_interface);
+ libusb_close(dev);
+}
+
+static uint64_t _usb_get_video_in_size(const usb_device* usb_device_desc) {
+ if(!usb_device_desc->is_3ds)
+ return sizeof(USBOldDSVideoInputData);
+ return sizeof(USB3DSVideoInputData);
+}
+
+static usb_capture_status capture_read_oldds_3ds(libusb_device_handle *handle, const usb_device* usb_device_desc, CaptureReceived* data_buffer) {
+ int transferred = 0;
+ int result = 0;
+ int bytesIn=0;
+ uint64_t video_size = _usb_get_video_in_size(usb_device_desc);
+ uint8_t *video_data_ptr = (uint8_t*)data_buffer->usb_received_3ds.video_in.screen_data;
+ if(!usb_device_desc->is_3ds)
+ video_data_ptr = (uint8_t*)data_buffer->usb_received_old_ds.video_in.screen_data;
+
+ uint8_t dummy;
+ result = vend_out(handle, usb_device_desc, usb_device_desc->cmdout_capture_start, 0, 0, 0, &dummy);
+ if(result < 0)
+ return (result == LIBUSB_ERROR_TIMEOUT) ? USB_CAPTURE_SKIP: USB_CAPTURE_ERROR;
+
+ // Both DS and 3DS old CCs send data until end of frame, followed by 0-length packets
+ do {
+ int transferSize = video_size - bytesIn;
+ if(usb_device_desc->is_3ds)
+ transferSize= (transferSize + 0x1ff) & ~0x1ff; //multiple of maxPacketSize
+ result = bulk_in(handle, usb_device_desc, video_data_ptr + bytesIn, transferSize, &transferred);
+ if(result == LIBUSB_SUCCESS)
+ bytesIn += transferred;
+ } while(bytesIn < video_size && result == LIBUSB_SUCCESS && transferred > 0);
+
+ if(result==LIBUSB_ERROR_PIPE) {
+ libusb_clear_halt(handle, usb_device_desc->ep2_in);
+ return USB_CAPTURE_PIPE_ERROR;
+ } else if(result == LIBUSB_ERROR_TIMEOUT || (usb_device_desc->is_3ds && (bytesIn < video_size))) {
+ return USB_CAPTURE_SKIP;
+ } else if(result != LIBUSB_SUCCESS) {
+ return USB_CAPTURE_ERROR;
+ }
+
+ if((!usb_device_desc->is_3ds) && (vend_in(handle, usb_device_desc, usb_device_desc->cmdin_frameinfo, 0, 0, sizeof(data_buffer->usb_received_old_ds.frameinfo), (uint8_t*)&data_buffer->usb_received_old_ds.frameinfo) < 0))
+ return USB_CAPTURE_FRAMEINFO_ERROR;
+
+ return USB_CAPTURE_SUCCESS;
+}
+
+static inline void usb_oldDSconvertVideoToOutputRGBA(USBOldDSPixelData data, uint8_t* target) {
+ int num_bits = 5;
+ uint8_t b = data.b;
+ uint8_t g = data.g;
+ uint8_t r = data.r;
+ #ifdef USE_SHARED_SPECIAL_BIT_OLDDS
+ // Uses the 5th bit of data as the sixth bit for all of them.
+ // Weird, but... It probably works...? Will check how it looks without it
+ // first...
+ num_bits = 6;
+ b = (b << 1) | data.special;
+ g = (g << 1) | data.special;
+ r = (r << 1) | data.special;
+ #endif
+ // Standard conversion stuff for x bits to 8 bits
+ int left_shift = 8 - num_bits;
+ int right_shift = num_bits - left_shift;
+ target[0] = (r << left_shift) | (r >> right_shift);
+ target[1] = (g << left_shift) | (g >> right_shift);
+ target[2] = (b << left_shift) | (b >> right_shift);
+ target[3] = 255;
+}
+
+static inline void usb_oldDSconvertVideoToOutputHalfLine(USBOldDSCaptureReceived *p_in, VideoOutputData *p_out, int halfline) {
+ //de-interleave pixels
+ for(int i = 0; i < WIDTH_DS / 2 ; i++) {
+ uint32_t halfline_pixel = (halfline * (WIDTH_DS / 2)) + i;
+ usb_oldDSconvertVideoToOutputRGBA(p_in->video_in.screen_data[halfline_pixel * 2], p_out->screen_data[halfline_pixel + (WIDTH_DS * HEIGHT_DS)]);
+ usb_oldDSconvertVideoToOutputRGBA(p_in->video_in.screen_data[(halfline_pixel * 2) + 1], p_out->screen_data[halfline_pixel]);
+ }
+}
+
+static void usb_oldDSconvertVideoToOutput(USBOldDSCaptureReceived *p_in, VideoOutputData *p_out) {
+ if(!p_in->frameinfo.valid) { //LCD was off
+ memset(p_out->screen_data, 0, WIDTH_DS * (2 * HEIGHT_DS) * 4);
+ return;
+ }
+
+ // Handle first line being off, if needed
+ memset(p_out->screen_data, 0, WIDTH_DS * 4);
+
+ for(int i = 0; i < 2; i++) {
+ if(p_in->frameinfo.half_line_flags[(i >> 3)] & (1 << (i & 7)))
+ usb_oldDSconvertVideoToOutputHalfLine(p_in, p_out, i);
+ }
+
+ for(int i = 2; i < HEIGHT_DS * 2; i++) {
+ if(p_in->frameinfo.half_line_flags[(i >> 3)] & (1 << (i & 7)))
+ usb_oldDSconvertVideoToOutputHalfLine(p_in, p_out, i);
+ else { // deal with missing half-line
+ memcpy(p_out->screen_data[i * (WIDTH_DS / 2)], p_out->screen_data[(i - 2) * (WIDTH_DS / 2)], (WIDTH_DS / 2) * 4);
+ memcpy(p_out->screen_data[(i * (WIDTH_DS / 2)) + (WIDTH_DS * HEIGHT_DS)], p_out->screen_data[((i - 2) * (WIDTH_DS / 2)) + (WIDTH_DS * HEIGHT_DS)], (WIDTH_DS / 2) * 4);
+ }
+ }
+}
+
+static void usb_3DSconvertVideoToOutput(USB3DSCaptureReceived *p_in, VideoOutputData *p_out, bool enabled_3d) {
+ for(int i = 0; i < IN_VIDEO_HEIGHT_3DS; i++)
+ for(int j = 0; j < IN_VIDEO_WIDTH_3DS; j++) {
+ int pixel = (i * IN_VIDEO_WIDTH_3DS) + j;
+ for(int u = 0; u < 3; u++)
+ p_out->screen_data[pixel][u] = p_in->video_in.screen_data[pixel][u];
+ p_out->screen_data[pixel][3] = 255;
+ }
+}
+
+void list_devices_usb_ds_3ds(std::vector &devices_list) {
+ if(!usb_initialized)
+ return;
+ libusb_device **usb_devices;
+ int num_devices = libusb_get_device_list(usb_ctx, &usb_devices);
+ libusb_device_descriptor usb_descriptor{};
+
+ int curr_serial_extra_id_3ds = 0;
+ int curr_serial_extra_id_old_ds = 0;
+ for(int i = 0; i < num_devices; i++) {
+ int result = libusb_get_device_descriptor(usb_devices[i], &usb_descriptor);
+ if(result < 0)
+ continue;
+ if(insert_device(devices_list, &usb_3ds_desc, usb_devices[i], &usb_descriptor, curr_serial_extra_id_3ds))
+ continue;
+ if(insert_device(devices_list, &usb_old_ds_desc, usb_devices[i], &usb_descriptor, curr_serial_extra_id_old_ds))
+ continue;
+ }
+
+ if(num_devices >= 0)
+ libusb_free_device_list(usb_devices, 1);
+}
+
+bool connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device) {
+ if(!usb_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);
+ if(!dev) {
+ capture_error_print(true, capture_data, "Device not found");
+ return false;
+ }
+ if(libusb_set_configuration(dev, usb_device_desc->default_config) != LIBUSB_SUCCESS) {
+ capture_error_print(true, capture_data, "Configuration failed");
+ capture_end(dev, usb_device_desc, false);
+ return false;
+ }
+ if(libusb_claim_interface(dev, usb_device_desc->capture_interface) != LIBUSB_SUCCESS) {
+ capture_error_print(true, capture_data, "Interface claim failed");
+ capture_end(dev, usb_device_desc, false);
+ return false;
+ }
+
+ if(usb_device_desc->is_3ds) {
+ //FW bug(?) workaround- first read times out sometimes
+ uint8_t dummy;
+ vend_out(dev, usb_device_desc, usb_device_desc->cmdout_capture_start, 0, 0, 0, &dummy);
+ default_sleep(usb_device_desc->bulk_timeout);
+ }
+
+ capture_data->handle = (void*)dev;
+
+ return true;
+}
+
+uint64_t usb_get_video_in_size(CaptureData* capture_data) {
+ return _usb_get_video_in_size(get_usb_device_desc(capture_data));
+}
+
+void usb_capture_main_loop(CaptureData* capture_data) {
+ if(!usb_initialized)
+ return;
+ const usb_device* usb_device_desc = get_usb_device_desc(capture_data);
+ int inner_curr_in = 0;
+ auto clock_start = std::chrono::high_resolution_clock::now();
+ bool done = false;
+
+ while((!done) && capture_data->status.connected && capture_data->status.running) {
+
+ usb_capture_status result = capture_read_oldds_3ds((libusb_device_handle*)capture_data->handle, usb_device_desc, &capture_data->capture_buf[inner_curr_in]);
+
+ const auto curr_time = std::chrono::high_resolution_clock::now();
+ const std::chrono::duration diff = curr_time - clock_start;
+
+ switch(result) {
+ case USB_CAPTURE_SKIP:
+ if(diff.count() >= CAPTURE_SKIP_TIMEOUT_SECONDS) {
+ capture_error_print(true, capture_data, "Disconnected: Too long since last read");
+ done = true;
+ }
+ break;
+ case USB_CAPTURE_PIPE_ERROR:
+ capture_error_print(true, capture_data, "Disconnected: Pipe error");
+ done = true;
+ break;
+ case USB_CAPTURE_FRAMEINFO_ERROR:
+ capture_error_print(true, capture_data, "Disconnected: Frameinfo error");
+ done = true;
+ break;
+ case USB_CAPTURE_SUCCESS:
+ clock_start = curr_time;
+ capture_data->time_in_buf[inner_curr_in] = diff.count();
+ capture_data->read[inner_curr_in] = _usb_get_video_in_size(usb_device_desc);
+
+ 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();
+ break;
+ default:
+ capture_error_print(true, capture_data, "Disconnected: Error");
+ done = true;
+ break;
+ }
+ }
+}
+
+void usb_capture_cleanup(CaptureData* capture_data) {
+ if(!usb_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)
+ return;
+ if(capture_device->is_3ds)
+ usb_3DSconvertVideoToOutput(&p_in->usb_received_3ds, p_out, enabled_3d);
+ else if(capture_device->rgb_bits_size == IN_VIDEO_BPP_SIZE_DS_BASIC)
+ 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_close() {
+ if(usb_initialized)
+ libusb_exit(usb_ctx);
+ usb_ctx = NULL;
+ usb_initialized = false;
+}
+