mirror of
https://github.com/Lorenzooone/cc3dsfs.git
synced 2025-06-18 16:45:39 -04:00
Implement proper frame delay and VRR for IS Nitro Emulator
This commit is contained in:
parent
718294ba02
commit
6f56918f3f
@ -44,7 +44,7 @@ endif()
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(SFML
|
||||
GIT_REPOSITORY https://github.com/SFML/SFML.git
|
||||
GIT_TAG macos_fullscreen_v2.6)
|
||||
GIT_TAG 2.6.x)
|
||||
|
||||
FetchContent_Declare(libusb1
|
||||
GIT_REPOSITORY https://github.com/libusb/libusb-cmake.git
|
||||
@ -301,7 +301,7 @@ execute_process(COMMAND ${CMAKE_COMMAND} --build ${TOOLS_DATA_DIR})
|
||||
|
||||
set(OUTPUT_NAME cc3dsfs)
|
||||
|
||||
add_executable(${OUTPUT_NAME} source/cc3dsfs.cpp source/utils.cpp source/audio_data.cpp source/audio.cpp source/frontend.cpp source/TextRectangle.cpp source/WindowScreen.cpp source/WindowScreen_Menu.cpp source/3dscapture_ftd3.cpp source/dscapture_ftd2.cpp source/usb_ds_3ds_capture.cpp source/devicecapture.cpp source/conversions.cpp source/ExtraButtons.cpp source/Menus/ConnectionMenu.cpp source/Menus/OptionSelectionMenu.cpp source/Menus/MainMenu.cpp source/Menus/VideoMenu.cpp source/Menus/CropMenu.cpp source/Menus/PARMenu.cpp source/Menus/RotationMenu.cpp source/Menus/OffsetMenu.cpp source/Menus/AudioMenu.cpp source/Menus/BFIMenu.cpp source/Menus/RelativePositionMenu.cpp source/Menus/ResolutionMenu.cpp source/Menus/FileConfigMenu.cpp source/Menus/ExtraSettingsMenu.cpp source/Menus/StatusMenu.cpp source/Menus/LicenseMenu.cpp source/WindowCommands.cpp source/Menus/ShortcutMenu.cpp source/Menus/ActionSelectionMenu.cpp source/Menus/ScalingRatioMenu.cpp source/usb_is_nitro.cpp source/usb_is_nitro_capture.cpp source/usb_generic.cpp ${TOOLS_DATA_DIR}/font_ttf.cpp)
|
||||
add_executable(${OUTPUT_NAME} source/cc3dsfs.cpp source/utils.cpp source/audio_data.cpp source/audio.cpp source/frontend.cpp source/TextRectangle.cpp source/WindowScreen.cpp source/WindowScreen_Menu.cpp source/3dscapture_ftd3.cpp source/dscapture_ftd2.cpp source/usb_ds_3ds_capture.cpp source/devicecapture.cpp source/conversions.cpp source/ExtraButtons.cpp source/Menus/ConnectionMenu.cpp source/Menus/OptionSelectionMenu.cpp source/Menus/MainMenu.cpp source/Menus/VideoMenu.cpp source/Menus/CropMenu.cpp source/Menus/PARMenu.cpp source/Menus/RotationMenu.cpp source/Menus/OffsetMenu.cpp source/Menus/AudioMenu.cpp source/Menus/BFIMenu.cpp source/Menus/RelativePositionMenu.cpp source/Menus/ResolutionMenu.cpp source/Menus/FileConfigMenu.cpp source/Menus/ExtraSettingsMenu.cpp source/Menus/StatusMenu.cpp source/Menus/LicenseMenu.cpp source/WindowCommands.cpp source/Menus/ShortcutMenu.cpp source/Menus/ActionSelectionMenu.cpp source/Menus/ScalingRatioMenu.cpp source/usb_is_nitro.cpp source/usb_is_nitro_capture.cpp source/usb_generic.cpp source/Menus/ISNitroMenu.cpp ${TOOLS_DATA_DIR}/font_ttf.cpp)
|
||||
add_dependencies(${OUTPUT_NAME} FTD3XX_BUILD_PROJECT FTD2XX_BUILD_PROJECT)
|
||||
target_link_libraries(${OUTPUT_NAME} PRIVATE sfml-graphics sfml-audio sfml-window sfml-system usb-1.0 ${ftd3xx_BINARY_DIR}/${FTD3XX_SUBFOLDER}/${FTD3XX_LIB} ${ftd2xx_BINARY_DIR}/${FTD2XX_SUBFOLDER}/${FTD2XX_LIB} ${EXTRA_LIBRARIES})
|
||||
target_link_directories(${OUTPUT_NAME} PRIVATE ${ftd3xx_BINARY_DIR}/${FTD3XX_SUBFOLDER} ${ftd2xx_BINARY_DIR}/${FTD2XX_SUBFOLDER})
|
||||
|
@ -3,7 +3,7 @@
|
||||
cc3dsfs is a multi-platform capture and display program for [3dscapture's](https://3dscapture.com/) N3DSXL, 3DS and DS (old) capture boards written in C++.
|
||||
The main goal is to offer the ability to use the Capture Card with a TV, via fullscreen mode.
|
||||
|
||||
Tentative IS Nitro Emulator support (for newer revisions) is also present. Though results may vary (and the amount of video delay may be significantly higher).
|
||||
IS Nitro Emulator support (for newer revisions) is also present. Though results may vary (and the amount of video delay may be significantly higher based on the cable used).
|
||||
|
||||
## Features
|
||||
|
||||
|
39
include/Menus/ISNitroMenu.hpp
Normal file
39
include/Menus/ISNitroMenu.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef __ISNMENU_HPP
|
||||
#define __ISNMENU_HPP
|
||||
|
||||
#include "OptionSelectionMenu.hpp"
|
||||
#include <chrono>
|
||||
|
||||
#include "TextRectangle.hpp"
|
||||
#include "sfml_gfx_structs.hpp"
|
||||
#include "display_structs.hpp"
|
||||
#include "capture_structs.hpp"
|
||||
|
||||
enum ISNitroMenuOutAction{
|
||||
ISN_MENU_NO_ACTION,
|
||||
ISN_MENU_BACK,
|
||||
ISN_MENU_DELAY,
|
||||
ISN_MENU_TYPE_DEC,
|
||||
ISN_MENU_TYPE_INC,
|
||||
};
|
||||
|
||||
class ISNitroMenu : public OptionSelectionMenu {
|
||||
public:
|
||||
ISNitroMenu(bool font_load_success, sf::Font &text_font);
|
||||
~ISNitroMenu();
|
||||
void prepare(float scaling_factor, int view_size_x, int view_size_y, CaptureStatus* capture_status);
|
||||
void insert_data();
|
||||
ISNitroMenuOutAction selected_index = ISNitroMenuOutAction::ISN_MENU_NO_ACTION;
|
||||
void reset_output_option();
|
||||
protected:
|
||||
bool is_option_selectable(int index, int action);
|
||||
bool is_option_inc_dec(int index);
|
||||
void set_output_option(int index, int action);
|
||||
int get_num_options();
|
||||
std::string get_string_option(int index, int action);
|
||||
void class_setup();
|
||||
private:
|
||||
int *options_indexes;
|
||||
int num_enabled_options;
|
||||
};
|
||||
#endif
|
@ -7,6 +7,7 @@
|
||||
#include "TextRectangle.hpp"
|
||||
#include "sfml_gfx_structs.hpp"
|
||||
#include "display_structs.hpp"
|
||||
#include "capture_structs.hpp"
|
||||
|
||||
enum MainMenuOutAction{
|
||||
MAIN_MENU_NO_ACTION,
|
||||
@ -24,6 +25,7 @@ enum MainMenuOutAction{
|
||||
MAIN_MENU_EXTRA_SETTINGS,
|
||||
MAIN_MENU_SHUTDOWN,
|
||||
MAIN_MENU_SHORTCUT_SETTINGS,
|
||||
MAIN_MENU_ISN_SETTINGS,
|
||||
};
|
||||
|
||||
class MainMenu : public OptionSelectionMenu {
|
||||
@ -31,7 +33,7 @@ public:
|
||||
MainMenu(bool font_load_success, sf::Font &text_font);
|
||||
~MainMenu();
|
||||
void prepare(float scaling_factor, int view_size_x, int view_size_y, bool connected);
|
||||
void insert_data(ScreenType s_type, bool is_fullscreen, bool mono_app_mode, bool enable_shortcuts);
|
||||
void insert_data(ScreenType s_type, bool is_fullscreen, bool mono_app_mode, bool enable_shortcuts, CaptureConnectionType cc_type, bool connected);
|
||||
MainMenuOutAction selected_index = MainMenuOutAction::MAIN_MENU_NO_ACTION;
|
||||
void reset_output_option();
|
||||
protected:
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define EXTRA_DATA_BUFFER_FTD3XX_SIZE (1 << 10)
|
||||
|
||||
enum CaptureConnectionType { CAPTURE_CONN_FTD3, CAPTURE_CONN_USB, CAPTURE_CONN_FTD2, CAPTURE_CONN_IS_NITRO };
|
||||
enum CaptureScreensType { CAPTURE_SCREENS_BOTH, CAPTURE_SCREENS_TOP, CAPTURE_SCREENS_BOTTOM, CAPTURE_SCREENS_ENUM_END };
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
@ -125,18 +126,21 @@ 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;
|
||||
volatile bool running = true;
|
||||
volatile bool close_success = true;
|
||||
volatile int curr_delay = 0;
|
||||
bool enabled_3d = false;
|
||||
CaptureScreensType capture_type;
|
||||
ConsumerMutex video_wait;
|
||||
ConsumerMutex audio_wait;
|
||||
};
|
||||
|
||||
struct CaptureData {
|
||||
void* handle;
|
||||
CaptureScreensType capture_type[NUM_CONCURRENT_DATA_BUFFERS];
|
||||
uint64_t read[NUM_CONCURRENT_DATA_BUFFERS];
|
||||
CaptureReceived capture_buf[NUM_CONCURRENT_DATA_BUFFERS];
|
||||
double time_in_buf[NUM_CONCURRENT_DATA_BUFFERS];
|
||||
|
@ -5,6 +5,6 @@
|
||||
#include "capture_structs.hpp"
|
||||
#include "display_structs.hpp"
|
||||
|
||||
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);
|
||||
void convertVideoToOutput(int index, VideoOutputData *p_out, CaptureData* capture_data);
|
||||
void convertAudioToOutput(int index, sf::Int16 *p_out, uint64_t n_samples, const bool is_big_endian, CaptureData* capture_data);
|
||||
#endif
|
||||
|
@ -7,7 +7,7 @@
|
||||
enum ScreenType { TOP, BOTTOM, JOINT };
|
||||
enum BottomRelativePosition { UNDER_TOP, LEFT_TOP, ABOVE_TOP, RIGHT_TOP, BOT_REL_POS_END };
|
||||
enum NonIntegerScalingModes { SMALLER_PRIORITY, INVERSE_PROPORTIONAL_PRIORITY, EQUAL_PRIORITY, PROPORTIONAL_PRIORITY, BIGGER_PRIORITY, END_NONINT_SCALE_MODES };
|
||||
enum CurrMenuType { DEFAULT_MENU_TYPE, CONNECT_MENU_TYPE, MAIN_MENU_TYPE, VIDEO_MENU_TYPE, AUDIO_MENU_TYPE, CROP_MENU_TYPE, TOP_PAR_MENU_TYPE, BOTTOM_PAR_MENU_TYPE, ROTATION_MENU_TYPE, OFFSET_MENU_TYPE, BFI_MENU_TYPE, LOAD_MENU_TYPE, SAVE_MENU_TYPE, RESOLUTION_MENU_TYPE, EXTRA_MENU_TYPE, STATUS_MENU_TYPE, LICENSES_MENU_TYPE, RELATIVE_POS_MENU_TYPE, SHORTCUTS_MENU_TYPE, ACTION_SELECTION_MENU_TYPE, SCALING_RATIO_MENU_TYPE };
|
||||
enum CurrMenuType { DEFAULT_MENU_TYPE, CONNECT_MENU_TYPE, MAIN_MENU_TYPE, VIDEO_MENU_TYPE, AUDIO_MENU_TYPE, CROP_MENU_TYPE, TOP_PAR_MENU_TYPE, BOTTOM_PAR_MENU_TYPE, ROTATION_MENU_TYPE, OFFSET_MENU_TYPE, BFI_MENU_TYPE, LOAD_MENU_TYPE, SAVE_MENU_TYPE, RESOLUTION_MENU_TYPE, EXTRA_MENU_TYPE, STATUS_MENU_TYPE, LICENSES_MENU_TYPE, RELATIVE_POS_MENU_TYPE, SHORTCUTS_MENU_TYPE, ACTION_SELECTION_MENU_TYPE, SCALING_RATIO_MENU_TYPE, ISN_MENU_TYPE };
|
||||
|
||||
struct ScreenInfo {
|
||||
bool is_blurred;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "ShortcutMenu.hpp"
|
||||
#include "ActionSelectionMenu.hpp"
|
||||
#include "ScalingRatioMenu.hpp"
|
||||
#include "ISNitroMenu.hpp"
|
||||
#include "display_structs.hpp"
|
||||
#include "WindowCommands.hpp"
|
||||
|
||||
@ -68,6 +69,7 @@ public:
|
||||
int check_connection_menu_result();
|
||||
void end_connection_menu();
|
||||
void update_ds_3ds_connection(bool changed_type);
|
||||
void update_capture_specific_settings();
|
||||
void update_save_menu();
|
||||
|
||||
void print_notification(std::string text, TextKind kind = TEXT_KIND_NORMAL);
|
||||
@ -146,6 +148,7 @@ private:
|
||||
LicenseMenu *license_menu;
|
||||
ShortcutMenu *shortcut_menu;
|
||||
ActionSelectionMenu *action_selection_menu;
|
||||
ISNitroMenu *is_nitro_menu;
|
||||
ScalingRatioMenu *scaling_ratio_menu;
|
||||
std::vector<const CropData*> possible_crops;
|
||||
std::vector<const CropData*> possible_crops_ds;
|
||||
@ -219,6 +222,7 @@ private:
|
||||
void fast_poll_change();
|
||||
void padding_change();
|
||||
void game_crop_enable_change();
|
||||
void is_nitro_capture_type_change(bool positive);
|
||||
void crop_value_change(int new_crop_value, bool do_print_notification = true, bool do_cycle = true);
|
||||
void par_value_change(int new_par_value, bool is_top);
|
||||
void offset_change(float &value, float change);
|
||||
@ -273,7 +277,7 @@ private:
|
||||
void setWinSize(bool is_main_thread);
|
||||
bool can_setup_menu();
|
||||
void setup_no_menu();
|
||||
void setup_main_menu(bool reset_data = true);
|
||||
void setup_main_menu(bool reset_data = true, bool skip_setup_check = false);
|
||||
void setup_video_menu(bool reset_data = true);
|
||||
void setup_crop_menu(bool reset_data = true);
|
||||
void setup_par_menu(bool is_top, bool reset_data = true);
|
||||
@ -290,6 +294,7 @@ private:
|
||||
void setup_licenses_menu(bool reset_data = true);
|
||||
void setup_relative_pos_menu(bool reset_data = true);
|
||||
void setup_scaling_ratio_menu(bool reset_data = true);
|
||||
void setup_is_nitro_menu(bool reset_data = true);
|
||||
void update_connection();
|
||||
};
|
||||
|
||||
@ -315,7 +320,8 @@ void get_par_size(int &width, int &height, float multiplier_factor, const PARDat
|
||||
float get_par_mult_factor(float width, float height, float max_width, float max_height, const PARData *correction_factor, bool is_rotated);
|
||||
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 update_connected_specific_settings(FrontendData* frontend_data, const CaptureDevice &cc_device);
|
||||
void screen_display_thread(WindowScreen *screen);
|
||||
std::string get_name_non_int_mode(NonIntegerScalingModes input);
|
||||
void default_sleep(int wanted_ms = -1);
|
||||
void default_sleep(float wanted_ms = -1);
|
||||
#endif
|
||||
|
@ -13,7 +13,7 @@ void list_devices_is_nitro(std::vector<CaptureDevice> &devices_list);
|
||||
bool is_nitro_connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device);
|
||||
void is_nitro_capture_main_loop(CaptureData* capture_data);
|
||||
void usb_is_nitro_capture_cleanup(CaptureData* capture_data);
|
||||
void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureDevice* capture_device);
|
||||
void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureScreensType capture_type);
|
||||
uint64_t usb_is_nitro_emulator_get_video_in_size(CaptureData* capture_data);
|
||||
void usb_is_nitro_init();
|
||||
void usb_is_nitro_close();
|
||||
|
136
source/Menus/ISNitroMenu.cpp
Normal file
136
source/Menus/ISNitroMenu.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include "ISNitroMenu.hpp"
|
||||
|
||||
#define NUM_TOTAL_MENU_OPTIONS (sizeof(pollable_options)/sizeof(pollable_options[0]))
|
||||
|
||||
struct ISNitroMenuOptionInfo {
|
||||
const std::string base_name;
|
||||
const std::string false_name;
|
||||
const bool is_selectable;
|
||||
const bool is_inc;
|
||||
const std::string dec_str;
|
||||
const std::string inc_str;
|
||||
const ISNitroMenuOutAction inc_out_action;
|
||||
const ISNitroMenuOutAction out_action;
|
||||
};
|
||||
|
||||
static const ISNitroMenuOptionInfo is_nitro_delay_option = {
|
||||
.base_name = "Delay", .false_name = "", .is_selectable = false,
|
||||
.is_inc = false, .dec_str = "", .inc_str = "", .inc_out_action = ISN_MENU_NO_ACTION,
|
||||
.out_action = ISN_MENU_DELAY};
|
||||
|
||||
static const ISNitroMenuOptionInfo is_nitro_type_option = {
|
||||
.base_name = "Capture", .false_name = "", .is_selectable = true,
|
||||
.is_inc = true, .dec_str = "<", .inc_str = ">", .inc_out_action = ISN_MENU_TYPE_INC,
|
||||
.out_action = ISN_MENU_TYPE_DEC};
|
||||
|
||||
static const ISNitroMenuOptionInfo* pollable_options[] = {
|
||||
&is_nitro_delay_option,
|
||||
&is_nitro_type_option,
|
||||
};
|
||||
|
||||
ISNitroMenu::ISNitroMenu(bool font_load_success, sf::Font &text_font) : OptionSelectionMenu(){
|
||||
this->options_indexes = new int[NUM_TOTAL_MENU_OPTIONS];
|
||||
this->initialize(font_load_success, text_font);
|
||||
this->num_enabled_options = 0;
|
||||
}
|
||||
|
||||
ISNitroMenu::~ISNitroMenu() {
|
||||
delete []this->options_indexes;
|
||||
}
|
||||
|
||||
void ISNitroMenu::class_setup() {
|
||||
this->num_options_per_screen = 5;
|
||||
this->min_elements_text_scaling_factor = num_options_per_screen + 2;
|
||||
this->width_factor_menu = 16;
|
||||
this->width_divisor_menu = 9;
|
||||
this->base_height_factor_menu = 12;
|
||||
this->base_height_divisor_menu = 6;
|
||||
this->min_text_size = 0.3;
|
||||
this->max_width_slack = 1.1;
|
||||
this->menu_color = sf::Color(30, 30, 60, 192);
|
||||
this->title = "IS Nitro Settings";
|
||||
this->show_back_x = true;
|
||||
this->show_x = false;
|
||||
this->show_title = true;
|
||||
}
|
||||
|
||||
void ISNitroMenu::insert_data() {
|
||||
this->num_enabled_options = 0;
|
||||
for(int i = 0; i < NUM_TOTAL_MENU_OPTIONS; i++) {
|
||||
this->options_indexes[this->num_enabled_options] = i;
|
||||
this->num_enabled_options++;
|
||||
}
|
||||
this->prepare_options();
|
||||
}
|
||||
|
||||
void ISNitroMenu::reset_output_option() {
|
||||
this->selected_index = ISNitroMenuOutAction::ISN_MENU_NO_ACTION;
|
||||
}
|
||||
|
||||
void ISNitroMenu::set_output_option(int index, int action) {
|
||||
if(index == BACK_X_OUTPUT_OPTION)
|
||||
this->selected_index = ISN_MENU_BACK;
|
||||
else if((action == INC_ACTION) && this->is_option_inc_dec(index))
|
||||
this->selected_index = pollable_options[this->options_indexes[index]]->inc_out_action;
|
||||
else
|
||||
this->selected_index = pollable_options[this->options_indexes[index]]->out_action;
|
||||
}
|
||||
|
||||
int ISNitroMenu::get_num_options() {
|
||||
return this->num_enabled_options;
|
||||
}
|
||||
|
||||
std::string ISNitroMenu::get_string_option(int index, int action) {
|
||||
if((action == INC_ACTION) && this->is_option_inc_dec(index))
|
||||
return pollable_options[this->options_indexes[index]]->inc_str;
|
||||
if((action == DEC_ACTION) && this->is_option_inc_dec(index))
|
||||
return pollable_options[this->options_indexes[index]]->dec_str;
|
||||
if(action == FALSE_ACTION)
|
||||
return pollable_options[this->options_indexes[index]]->false_name;
|
||||
return pollable_options[this->options_indexes[index]]->base_name;
|
||||
}
|
||||
|
||||
bool ISNitroMenu::is_option_selectable(int index, int action) {
|
||||
return pollable_options[this->options_indexes[index]]->is_selectable;
|
||||
}
|
||||
|
||||
bool ISNitroMenu::is_option_inc_dec(int index) {
|
||||
return pollable_options[this->options_indexes[index]]->is_inc;
|
||||
}
|
||||
|
||||
static std::string get_capture_type_name(CaptureScreensType capture_type) {
|
||||
switch(capture_type) {
|
||||
case CAPTURE_SCREENS_TOP:
|
||||
return "Top Screen";
|
||||
case CAPTURE_SCREENS_BOTTOM:
|
||||
return "Bottom Screen";
|
||||
default:
|
||||
return "Both Screens";
|
||||
}
|
||||
}
|
||||
|
||||
void ISNitroMenu::prepare(float menu_scaling_factor, int view_size_x, int view_size_y, CaptureStatus* capture_status) {
|
||||
int num_pages = this->get_num_pages();
|
||||
if(this->future_data.page >= num_pages)
|
||||
this->future_data.page = num_pages - 1;
|
||||
int start = this->future_data.page * this->num_options_per_screen;
|
||||
for(int i = 0; i < this->num_options_per_screen + 1; i++) {
|
||||
int index = (i * this->single_option_multiplier) + this->elements_start_id;
|
||||
if(!this->future_enabled_labels[index])
|
||||
continue;
|
||||
int real_index = start + i;
|
||||
int option_index = this->options_indexes[real_index];
|
||||
switch(pollable_options[option_index]->out_action) {
|
||||
case ISN_MENU_DELAY:
|
||||
this->labels[index]->setText(this->setTextOptionInt(real_index, capture_status->curr_delay));
|
||||
break;
|
||||
case ISN_MENU_TYPE_DEC:
|
||||
this->labels[index]->setText(this->setTextOptionString(real_index, get_capture_type_name(capture_status->capture_type)));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->base_prepare(menu_scaling_factor, view_size_x, view_size_y);
|
||||
}
|
@ -12,6 +12,7 @@ struct MainMenuOptionInfo {
|
||||
const bool active_bottom_screen;
|
||||
const bool enabled_normal_mode;
|
||||
const bool enabled_mono_mode;
|
||||
const bool is_cc_specific;
|
||||
const MainMenuOutAction out_action;
|
||||
};
|
||||
|
||||
@ -19,107 +20,114 @@ static const MainMenuOptionInfo connect_option = {
|
||||
.base_name = "Disconnect", .false_name = "Connect",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_OPEN};
|
||||
|
||||
static const MainMenuOptionInfo windowed_option = {
|
||||
.base_name = "Windowed Mode", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = false,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = false,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = false, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_FULLSCREEN};
|
||||
|
||||
static const MainMenuOptionInfo fullscreen_option = {
|
||||
.base_name = "Fullscreen Mode", .false_name = "",
|
||||
.active_fullscreen = false, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = false,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = false, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_FULLSCREEN};
|
||||
|
||||
static const MainMenuOptionInfo join_screens_option = {
|
||||
.base_name = "Join Screens", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = false, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = false,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = false, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_SPLIT};
|
||||
|
||||
static const MainMenuOptionInfo split_screens_option = {
|
||||
.base_name = "Split Screens", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = false, .active_bottom_screen = false,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = false,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = false, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_SPLIT};
|
||||
|
||||
static const MainMenuOptionInfo video_settings_option = {
|
||||
.base_name = "Video Settings", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_VIDEO_SETTINGS};
|
||||
|
||||
static const MainMenuOptionInfo quit_option = {
|
||||
.base_name = "Quit Application", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = false,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = false, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_QUIT_APPLICATION};
|
||||
|
||||
static const MainMenuOptionInfo audio_settings_option = {
|
||||
.base_name = "Audio Settings", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_AUDIO_SETTINGS};
|
||||
|
||||
static const MainMenuOptionInfo save_profiles_option = {
|
||||
.base_name = "Save Profile", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_SAVE_PROFILES};
|
||||
|
||||
static const MainMenuOptionInfo load_profiles_option = {
|
||||
.base_name = "Load Profile", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_LOAD_PROFILES};
|
||||
|
||||
static const MainMenuOptionInfo status_option = {
|
||||
.base_name = "Status", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_STATUS};
|
||||
|
||||
static const MainMenuOptionInfo licenses_option = {
|
||||
.base_name = "Licenses", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_LICENSES};
|
||||
|
||||
static const MainMenuOptionInfo extra_settings_option = {
|
||||
.base_name = "Extra Settings", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = false, .enabled_mono_mode = true,
|
||||
.enabled_normal_mode = false, .enabled_mono_mode = true, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_EXTRA_SETTINGS};
|
||||
|
||||
static const MainMenuOptionInfo shortcut_option = {
|
||||
.base_name = "Shortcuts", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_SHORTCUT_SETTINGS};
|
||||
|
||||
static const MainMenuOptionInfo shutdown_option = {
|
||||
.base_name = "Shutdown", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = false, .enabled_mono_mode = true,
|
||||
.enabled_normal_mode = false, .enabled_mono_mode = true, .is_cc_specific = false,
|
||||
.out_action = MAIN_MENU_SHUTDOWN};
|
||||
|
||||
static const MainMenuOptionInfo isn_settings_option = {
|
||||
.base_name = "Is Nitro Settings", .false_name = "",
|
||||
.active_fullscreen = true, .active_windowed_screen = true,
|
||||
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
|
||||
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = true,
|
||||
.out_action = MAIN_MENU_ISN_SETTINGS};
|
||||
|
||||
static const MainMenuOptionInfo* pollable_options[] = {
|
||||
&connect_option,
|
||||
&windowed_option,
|
||||
@ -132,6 +140,7 @@ static const MainMenuOptionInfo* pollable_options[] = {
|
||||
&load_profiles_option,
|
||||
&shortcut_option,
|
||||
&status_option,
|
||||
&isn_settings_option,
|
||||
&licenses_option,
|
||||
&extra_settings_option,
|
||||
&quit_option,
|
||||
@ -164,7 +173,13 @@ void MainMenu::class_setup() {
|
||||
this->show_title = true;
|
||||
}
|
||||
|
||||
void MainMenu::insert_data(ScreenType s_type, bool is_fullscreen, bool mono_app_mode, bool enable_shortcut) {
|
||||
static bool check_cc_specific_option(const MainMenuOptionInfo* option, CaptureConnectionType cc_type) {
|
||||
if((option->out_action == MAIN_MENU_ISN_SETTINGS) && (cc_type == CAPTURE_CONN_IS_NITRO))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainMenu::insert_data(ScreenType s_type, bool is_fullscreen, bool mono_app_mode, bool enable_shortcut, CaptureConnectionType cc_type, bool connected) {
|
||||
this->num_enabled_options = 0;
|
||||
for(int i = 0; i < NUM_TOTAL_MENU_OPTIONS; i++) {
|
||||
bool valid = true;
|
||||
@ -182,6 +197,8 @@ void MainMenu::insert_data(ScreenType s_type, bool is_fullscreen, bool mono_app_
|
||||
valid = valid && pollable_options[i]->enabled_mono_mode;
|
||||
else
|
||||
valid = valid && pollable_options[i]->enabled_normal_mode;
|
||||
if(pollable_options[i]->is_cc_specific)
|
||||
valid = valid && connected && check_cc_specific_option(pollable_options[i], cc_type);
|
||||
if((pollable_options[i]->out_action == MAIN_MENU_SHORTCUT_SETTINGS) && (!enable_shortcut))
|
||||
valid = false;
|
||||
if(valid) {
|
||||
|
@ -109,6 +109,7 @@ void WindowScreen::init_menus() {
|
||||
this->shortcut_menu = new ShortcutMenu(this->font_load_success, this->text_font);
|
||||
this->action_selection_menu = new ActionSelectionMenu(this->font_load_success, this->text_font);
|
||||
this->scaling_ratio_menu = new ScalingRatioMenu(this->font_load_success, this->text_font);
|
||||
this->is_nitro_menu = new ISNitroMenu(this->font_load_success, this->text_font);
|
||||
}
|
||||
|
||||
void WindowScreen::destroy_menus() {
|
||||
@ -130,6 +131,7 @@ void WindowScreen::destroy_menus() {
|
||||
delete this->shortcut_menu;
|
||||
delete this->action_selection_menu;
|
||||
delete this->scaling_ratio_menu;
|
||||
delete this->is_nitro_menu;
|
||||
}
|
||||
|
||||
void WindowScreen::set_close(int ret_val) {
|
||||
@ -174,6 +176,15 @@ void WindowScreen::fast_poll_change() {
|
||||
this->print_notification_on_off("Slow Poll", this->display_data->fast_poll);
|
||||
}
|
||||
|
||||
void WindowScreen::is_nitro_capture_type_change(bool positive) {
|
||||
int new_value = (int)(this->capture_status->capture_type);
|
||||
if(positive)
|
||||
new_value += 1;
|
||||
else
|
||||
new_value += CAPTURE_SCREENS_ENUM_END - 1;
|
||||
this->capture_status->capture_type = static_cast<CaptureScreensType>(new_value % CAPTURE_SCREENS_ENUM_END);
|
||||
}
|
||||
|
||||
void WindowScreen::padding_change() {
|
||||
if(this->m_info.is_fullscreen)
|
||||
return;
|
||||
@ -615,14 +626,14 @@ void WindowScreen::setup_no_menu() {
|
||||
this->last_menu_change_time = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void WindowScreen::setup_main_menu(bool reset_data) {
|
||||
if(!this->can_setup_menu())
|
||||
void WindowScreen::setup_main_menu(bool reset_data, bool skip_setup_check) {
|
||||
if((!skip_setup_check) && (!this->can_setup_menu()))
|
||||
return;
|
||||
if(this->curr_menu != MAIN_MENU_TYPE) {
|
||||
this->curr_menu = MAIN_MENU_TYPE;
|
||||
if(reset_data)
|
||||
this->main_menu->reset_data();
|
||||
this->main_menu->insert_data(this->m_stype, this->m_info.is_fullscreen, this->display_data->mono_app_mode, is_shortcut_valid());
|
||||
this->main_menu->insert_data(this->m_stype, this->m_info.is_fullscreen, this->display_data->mono_app_mode, is_shortcut_valid(), this->capture_status->device.cc_type, this->capture_status->connected);
|
||||
this->last_menu_change_time = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
@ -900,6 +911,18 @@ void WindowScreen::setup_scaling_ratio_menu(bool reset_data) {
|
||||
}
|
||||
}
|
||||
|
||||
void WindowScreen::setup_is_nitro_menu(bool reset_data) {
|
||||
if(!this->can_setup_menu())
|
||||
return;
|
||||
if(this->curr_menu != ISN_MENU_TYPE) {
|
||||
this->curr_menu = ISN_MENU_TYPE;
|
||||
if(reset_data)
|
||||
this->is_nitro_menu->reset_data();
|
||||
this->is_nitro_menu->insert_data();
|
||||
this->last_menu_change_time = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowScreen::update_save_menu() {
|
||||
if(this->curr_menu == SAVE_MENU_TYPE) {
|
||||
this->curr_menu = DEFAULT_MENU_TYPE;
|
||||
@ -1234,6 +1257,10 @@ void WindowScreen::poll(bool do_everything) {
|
||||
this->setup_shortcuts_menu();
|
||||
done = true;
|
||||
break;
|
||||
case MAIN_MENU_ISN_SETTINGS:
|
||||
this->setup_is_nitro_menu();
|
||||
done = true;
|
||||
break;
|
||||
case MAIN_MENU_SHUTDOWN:
|
||||
this->set_close(1);
|
||||
this->setup_no_menu();
|
||||
@ -1771,6 +1798,30 @@ void WindowScreen::poll(bool do_everything) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ISN_MENU_TYPE:
|
||||
if(this->is_nitro_menu->poll(event_data)) {
|
||||
switch(this->is_nitro_menu->selected_index) {
|
||||
case ISN_MENU_BACK:
|
||||
this->setup_main_menu(false);
|
||||
done = true;
|
||||
break;
|
||||
case ISN_MENU_NO_ACTION:
|
||||
break;
|
||||
case ISN_MENU_DELAY:
|
||||
break;
|
||||
case ISN_MENU_TYPE_DEC:
|
||||
this->is_nitro_capture_type_change(false);
|
||||
break;
|
||||
case ISN_MENU_TYPE_INC:
|
||||
this->is_nitro_capture_type_change(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this->is_nitro_menu->reset_output_option();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1800,6 +1851,15 @@ void WindowScreen::update_ds_3ds_connection(bool changed_type) {
|
||||
this->future_operations.call_crop = true;
|
||||
}
|
||||
|
||||
void WindowScreen::update_capture_specific_settings() {
|
||||
if(this->curr_menu == MAIN_MENU_TYPE) {
|
||||
this->setup_no_menu();
|
||||
this->setup_main_menu(true, true);
|
||||
}
|
||||
if(this->curr_menu == ISN_MENU_TYPE)
|
||||
this->setup_no_menu();
|
||||
}
|
||||
|
||||
int WindowScreen::load_data() {
|
||||
int ret_val = this->m_prepare_load;
|
||||
this->m_prepare_load = 0;
|
||||
@ -1985,6 +2045,9 @@ void WindowScreen::prepare_menu_draws(int view_size_x, int view_size_y) {
|
||||
case SCALING_RATIO_MENU_TYPE:
|
||||
this->scaling_ratio_menu->prepare(this->loaded_info.menu_scaling_factor, view_size_x, view_size_y, &this->loaded_info);
|
||||
break;
|
||||
case ISN_MENU_TYPE:
|
||||
this->is_nitro_menu->prepare(this->loaded_info.menu_scaling_factor, view_size_x, view_size_y, this->capture_status);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2052,6 +2115,9 @@ void WindowScreen::execute_menu_draws() {
|
||||
case SCALING_RATIO_MENU_TYPE:
|
||||
this->scaling_ratio_menu->draw(this->loaded_info.menu_scaling_factor, this->m_win);
|
||||
break;
|
||||
case ISN_MENU_TYPE:
|
||||
this->is_nitro_menu->draw(this->loaded_info.menu_scaling_factor, this->m_win);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ static void SuccessConnectionOutTextGenerator(OutTextData &out_text_data, Captur
|
||||
}
|
||||
}
|
||||
|
||||
static bool load(const std::string path, const std::string name, ScreenInfo &top_info, ScreenInfo &bottom_info, ScreenInfo &joint_info, DisplayData &display_data, AudioData *audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts) {
|
||||
static bool load(const std::string path, const std::string name, ScreenInfo &top_info, ScreenInfo &bottom_info, ScreenInfo &joint_info, DisplayData &display_data, AudioData *audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status) {
|
||||
std::ifstream file(path + name);
|
||||
std::string line;
|
||||
|
||||
@ -110,6 +110,11 @@ static bool load(const std::string path, const std::string name, ScreenInfo &top
|
||||
continue;
|
||||
}
|
||||
|
||||
if(key == "is_screen_capture_type") {
|
||||
capture_status->capture_type = static_cast<CaptureScreensType>(std::stoi(value) % CaptureScreensType::CAPTURE_SCREENS_ENUM_END);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(audio_data->load_audio_data(key, value))
|
||||
continue;
|
||||
}
|
||||
@ -125,7 +130,9 @@ static bool load(const std::string path, const std::string name, ScreenInfo &top
|
||||
return result;
|
||||
}
|
||||
|
||||
static void defaults_reload(FrontendData *frontend_data, AudioData* audio_data, ExtraButtonShortcuts* extra_button_shortcuts) {
|
||||
static void defaults_reload(FrontendData *frontend_data, AudioData* audio_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status) {
|
||||
capture_status->enabled_3d = false;
|
||||
capture_status->capture_type = CAPTURE_SCREENS_BOTH;
|
||||
reset_screen_info(frontend_data->top_screen->m_info);
|
||||
reset_screen_info(frontend_data->bot_screen->m_info);
|
||||
reset_screen_info(frontend_data->joint_screen->m_info);
|
||||
@ -138,11 +145,11 @@ static void defaults_reload(FrontendData *frontend_data, AudioData* audio_data,
|
||||
frontend_data->reload = true;
|
||||
}
|
||||
|
||||
static void load_layout_file(int load_index, FrontendData *frontend_data, AudioData* audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, bool skip_io, bool do_print) {
|
||||
static void load_layout_file(int load_index, FrontendData *frontend_data, AudioData* audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status, bool skip_io, bool do_print) {
|
||||
if(skip_io)
|
||||
return;
|
||||
|
||||
defaults_reload(frontend_data, audio_data, extra_button_shortcuts);
|
||||
defaults_reload(frontend_data, audio_data, extra_button_shortcuts, capture_status);
|
||||
|
||||
if(load_index == SIMPLE_RESET_DATA_INDEX) {
|
||||
UpdateOutText(out_text_data, "Reset detected. Defaults re-loaded", "Reset detected\nDefaults re-loaded", TEXT_KIND_WARNING);
|
||||
@ -152,16 +159,16 @@ static void load_layout_file(int load_index, FrontendData *frontend_data, AudioD
|
||||
bool name_load_success = false;
|
||||
std::string layout_name = LayoutNameGenerator(load_index);
|
||||
std::string layout_path = LayoutPathGenerator(load_index);
|
||||
bool op_success = load(layout_path, layout_name, frontend_data->top_screen->m_info, frontend_data->bot_screen->m_info, frontend_data->joint_screen->m_info, frontend_data->display_data, audio_data, out_text_data, extra_button_shortcuts);
|
||||
bool op_success = load(layout_path, layout_name, frontend_data->top_screen->m_info, frontend_data->bot_screen->m_info, frontend_data->joint_screen->m_info, frontend_data->display_data, audio_data, out_text_data, extra_button_shortcuts, capture_status);
|
||||
if(do_print && op_success) {
|
||||
std::string load_name = load_layout_name(load_index, name_load_success);
|
||||
UpdateOutText(out_text_data, "Layout loaded from: " + layout_path + layout_name, "Layout " + load_name + " loaded", TEXT_KIND_SUCCESS);
|
||||
}
|
||||
else if(!op_success)
|
||||
defaults_reload(frontend_data, audio_data, extra_button_shortcuts);
|
||||
defaults_reload(frontend_data, audio_data, extra_button_shortcuts, capture_status);
|
||||
}
|
||||
|
||||
static bool save(const std::string path, const std::string name, const std::string save_name, const ScreenInfo &top_info, const ScreenInfo &bottom_info, const ScreenInfo &joint_info, DisplayData &display_data, AudioData *audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts) {
|
||||
static bool save(const std::string path, const std::string name, const std::string save_name, const ScreenInfo &top_info, const ScreenInfo &bottom_info, const ScreenInfo &joint_info, DisplayData &display_data, AudioData *audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status) {
|
||||
#if (!defined(_MSC_VER)) || (_MSC_VER > 1916)
|
||||
std::filesystem::create_directories(path);
|
||||
#else
|
||||
@ -183,13 +190,14 @@ static bool save(const std::string path, const std::string name, const std::stri
|
||||
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 << "is_screen_capture_type=" << capture_status->capture_type << std::endl;
|
||||
file << audio_data->save_audio_data();
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void save_layout_file(int save_index, FrontendData *frontend_data, AudioData* audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, bool skip_io, bool do_print) {
|
||||
static void save_layout_file(int save_index, FrontendData *frontend_data, AudioData* audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status, bool skip_io, bool do_print) {
|
||||
if(skip_io)
|
||||
return;
|
||||
|
||||
@ -200,7 +208,7 @@ static void save_layout_file(int save_index, FrontendData *frontend_data, AudioD
|
||||
std::string save_name = load_layout_name(save_index, name_load_success);
|
||||
std::string layout_name = LayoutNameGenerator(save_index);
|
||||
std::string layout_path = LayoutPathGenerator(save_index);
|
||||
bool op_success = save(layout_path, layout_name, save_name, frontend_data->top_screen->m_info, frontend_data->bot_screen->m_info, frontend_data->joint_screen->m_info, frontend_data->display_data, audio_data, out_text_data, extra_button_shortcuts);
|
||||
bool op_success = save(layout_path, layout_name, save_name, frontend_data->top_screen->m_info, frontend_data->bot_screen->m_info, frontend_data->joint_screen->m_info, frontend_data->display_data, audio_data, out_text_data, extra_button_shortcuts, capture_status);
|
||||
if(do_print && op_success) {
|
||||
UpdateOutText(out_text_data, "Layout saved to: " + layout_path + layout_name, "Layout " + save_name + " saved", TEXT_KIND_SUCCESS);
|
||||
}
|
||||
@ -223,7 +231,7 @@ static void soundCall(AudioData *audio_data, CaptureData* capture_data) {
|
||||
if((capture_data->read[curr_out] > get_video_in_size(capture_data)) && (loaded_samples < MAX_MAX_AUDIO_LATENCY)) {
|
||||
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, capture_data);
|
||||
convertAudioToOutput(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;
|
||||
@ -307,7 +315,7 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
|
||||
frontend_data.bot_screen = bot_screen;
|
||||
frontend_data.joint_screen = joint_screen;
|
||||
|
||||
load_layout_file(STARTUP_FILE_INDEX, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, skip_io, false);
|
||||
load_layout_file(STARTUP_FILE_INDEX, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, &capture_data->status, skip_io, false);
|
||||
// Due to the risk for seizures, at the start of the program, set BFI to false!
|
||||
top_screen->m_info.bfi = false;
|
||||
bot_screen->m_info.bfi = false;
|
||||
@ -322,6 +330,7 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
|
||||
std::thread joint_thread(screen_display_thread, joint_screen);
|
||||
|
||||
capture_data->status.connected = connect(true, capture_data, &frontend_data);
|
||||
bool last_connected = capture_data->status.connected;
|
||||
SuccessConnectionOutTextGenerator(out_text_data, capture_data);
|
||||
|
||||
while(capture_data->status.running) {
|
||||
@ -337,13 +346,16 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
|
||||
VideoOutputData *chosen_buf = out_buf;
|
||||
bool blank_out = false;
|
||||
if(capture_data->status.connected) {
|
||||
if(!last_connected)
|
||||
update_connected_specific_settings(&frontend_data, capture_data->status.device);
|
||||
last_connected = true;
|
||||
bool timed_out = !capture_data->status.video_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)) {
|
||||
last_frame_time = capture_data->time_in_buf[curr_out];
|
||||
if(capture_data->read[curr_out] >= get_video_in_size(capture_data)) {
|
||||
convertVideoToOutput(&capture_data->capture_buf[curr_out], out_buf, capture_data);
|
||||
convertVideoToOutput(curr_out, out_buf, capture_data);
|
||||
num_allowed_blanks = MAX_ALLOWED_BLANKS;
|
||||
}
|
||||
else {
|
||||
@ -355,11 +367,14 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(capture_data->status.cooldown_curr_in)
|
||||
else if((capture_data->status.cooldown_curr_in) || timed_out)
|
||||
blank_out = true;
|
||||
prev_out = curr_out;
|
||||
}
|
||||
else {
|
||||
if(last_connected)
|
||||
update_connected_specific_settings(&frontend_data, capture_data->status.device);
|
||||
last_connected = false;
|
||||
default_sleep();
|
||||
blank_out = true;
|
||||
}
|
||||
@ -392,12 +407,12 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_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);
|
||||
load_layout_file(load_index, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, &capture_data->status, 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);
|
||||
save_layout_file(save_index, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, &capture_data->status, skip_io, true);
|
||||
top_screen->update_save_menu();
|
||||
bot_screen->update_save_menu();
|
||||
joint_screen->update_save_menu();
|
||||
@ -433,7 +448,7 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
|
||||
bot_screen->after_thread_join();
|
||||
joint_screen->after_thread_join();
|
||||
|
||||
save_layout_file(STARTUP_FILE_INDEX, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, skip_io, false);
|
||||
save_layout_file(STARTUP_FILE_INDEX, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, &capture_data->status, skip_io, false);
|
||||
|
||||
if(!out_text_data.consumed) {
|
||||
ConsoleOutText(out_text_data.full_text);
|
||||
|
@ -7,7 +7,8 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
void convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureData* capture_data) {
|
||||
void convertVideoToOutput(int index, VideoOutputData *p_out, CaptureData* capture_data) {
|
||||
CaptureReceived *p_in = &capture_data->capture_buf[index];
|
||||
#ifdef USE_FTD3
|
||||
if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD3)
|
||||
ftd3_convertVideoToOutput(p_in, p_out, capture_data->status.enabled_3d);
|
||||
@ -22,13 +23,14 @@ void convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, Capture
|
||||
#endif
|
||||
#ifdef USE_IS_NITRO_USB
|
||||
if(capture_data->status.device.cc_type == CAPTURE_CONN_IS_NITRO)
|
||||
usb_is_nitro_convertVideoToOutput(p_in, p_out, &capture_data->status.device);
|
||||
usb_is_nitro_convertVideoToOutput(p_in, p_out, capture_data->capture_type[index]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void convertAudioToOutput(CaptureReceived *p_in, sf::Int16 *p_out, uint64_t n_samples, const bool is_big_endian, CaptureData* capture_data) {
|
||||
void convertAudioToOutput(int index, sf::Int16 *p_out, uint64_t n_samples, const bool is_big_endian, CaptureData* capture_data) {
|
||||
if(!capture_data->status.device.has_audio)
|
||||
return;
|
||||
CaptureReceived *p_in = &capture_data->capture_buf[index];
|
||||
uint8_t* base_ptr = NULL;
|
||||
#ifdef USE_FTD3
|
||||
if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD3) {
|
||||
|
@ -786,9 +786,17 @@ void update_connected_3ds_ds(FrontendData* frontend_data, const CaptureDevice &o
|
||||
}
|
||||
}
|
||||
|
||||
void default_sleep(int wanted_ms) {
|
||||
void update_connected_specific_settings(FrontendData* frontend_data, const CaptureDevice &cc_device) {
|
||||
if(cc_device.cc_type == CAPTURE_CONN_IS_NITRO) {
|
||||
frontend_data->top_screen->update_capture_specific_settings();
|
||||
frontend_data->bot_screen->update_capture_specific_settings();
|
||||
frontend_data->joint_screen->update_capture_specific_settings();
|
||||
}
|
||||
}
|
||||
|
||||
void default_sleep(float wanted_ms) {
|
||||
if(wanted_ms < 0)
|
||||
sf::sleep(sf::milliseconds(1000/USB_CHECKS_PER_SECOND));
|
||||
sf::sleep(sf::milliseconds(1000.0/USB_CHECKS_PER_SECOND));
|
||||
else
|
||||
sf::sleep(sf::milliseconds(wanted_ms));
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ int DisableLca2(libusb_device_handle *handle) {
|
||||
ret = WriteNecMemU16(handle, 0xF84000A, 1);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
default_sleep(2000);
|
||||
//default_sleep(2000);
|
||||
return WriteNecMemU16(handle, 0xF84000A, 0);
|
||||
}
|
||||
|
||||
@ -290,6 +290,7 @@ int UpdateFrameForwardEnable(libusb_device_handle *handle, uint8_t value) {
|
||||
}
|
||||
|
||||
int ReadFrame(libusb_device_handle *handle, uint8_t* buf, int length) {
|
||||
// Maybe making this async would be better for lower end hardware...
|
||||
int num_bytes = 0;
|
||||
int ret = bulk_in(handle, &usb_is_nitro_desc, buf, length, &num_bytes);
|
||||
if(num_bytes != length)
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
#define SERIAL_NUMBER_SIZE (IS_NITRO_REAL_SERIAL_NUMBER_SIZE + 1)
|
||||
|
||||
#define NUM_CONSECUTIVE_FRAMES 32
|
||||
#define FRAME_BUFFER_SIZE 32
|
||||
|
||||
enum usb_capture_status {
|
||||
USB_CAPTURE_SUCCESS = 0,
|
||||
@ -40,9 +40,9 @@ enum usb_capture_status {
|
||||
USB_CAPTURE_ERROR
|
||||
};
|
||||
|
||||
static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames);
|
||||
static int StartCapture(libusb_device_handle *handle, int *out_frame_count);
|
||||
static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames);
|
||||
static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames, CaptureScreensType capture_type);
|
||||
static int StartCapture(libusb_device_handle *handle, uint16_t *out_frame_count, float* single_frame_time, CaptureScreensType capture_type);
|
||||
static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames, CaptureScreensType capture_type);
|
||||
|
||||
static std::string get_serial(const is_nitro_usb_device* usb_device_desc, libusb_device_handle *handle, int &curr_serial_extra_id) {
|
||||
uint8_t data[SERIAL_NUMBER_SIZE];
|
||||
@ -50,13 +50,18 @@ static std::string get_serial(const is_nitro_usb_device* usb_device_desc, libusb
|
||||
bool conn_success = true;
|
||||
if(libusb_set_configuration(handle, usb_device_desc->default_config) != LIBUSB_SUCCESS)
|
||||
conn_success = false;
|
||||
//if(libusb_reset_device(handle) != LIBUSB_SUCCESS)
|
||||
// conn_success = false;
|
||||
if(conn_success && libusb_claim_interface(handle, usb_device_desc->default_interface) != LIBUSB_SUCCESS)
|
||||
conn_success = false;
|
||||
if(conn_success && EndCapture(handle, false, 0) != LIBUSB_SUCCESS)
|
||||
if(conn_success && EndCapture(handle, false, 0, CAPTURE_SCREENS_BOTH) != LIBUSB_SUCCESS)
|
||||
conn_success = false;
|
||||
if(conn_success && (GetDeviceSerial(handle, data) == LIBUSB_SUCCESS)) {
|
||||
if(conn_success && (GetDeviceSerial(handle, data) != LIBUSB_SUCCESS)) {
|
||||
int ret = 0;
|
||||
while(ret >= 0)
|
||||
ret = drain_frames(handle, FRAME_BUFFER_SIZE * 2, 0, CAPTURE_SCREENS_TOP);
|
||||
if((GetDeviceSerial(handle, data) != LIBUSB_SUCCESS))
|
||||
conn_success = false;
|
||||
}
|
||||
if(conn_success) {
|
||||
data[IS_NITRO_REAL_SERIAL_NUMBER_SIZE] = '\0';
|
||||
serial_str = std::string((const char*)data);
|
||||
}
|
||||
@ -165,71 +170,120 @@ bool is_nitro_connect_usb(bool print_failed, CaptureData* capture_data, CaptureD
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t _is_nitro_emulator_get_video_in_size() {
|
||||
static uint64_t _is_nitro_emulator_get_video_in_size(CaptureScreensType capture_type) {
|
||||
if((capture_type == CAPTURE_SCREENS_TOP) || (capture_type == CAPTURE_SCREENS_BOTTOM))
|
||||
return sizeof(ISNitroEmulatorVideoInputData) / 2;
|
||||
return sizeof(ISNitroEmulatorVideoInputData);
|
||||
}
|
||||
|
||||
static uint64_t get_capture_size() {
|
||||
return sizeof(ISNitroCaptureReceived);
|
||||
}
|
||||
|
||||
uint64_t usb_is_nitro_emulator_get_video_in_size(CaptureData* capture_data) {
|
||||
return _is_nitro_emulator_get_video_in_size();
|
||||
return _is_nitro_emulator_get_video_in_size(capture_data->status.capture_type);
|
||||
}
|
||||
|
||||
static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames) {
|
||||
static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames, CaptureScreensType capture_type) {
|
||||
ISNitroEmulatorVideoInputData video_in_buffer;
|
||||
for (int i = start_frames; i < num_frames; i++) {
|
||||
int ret = ReadFrame(handle, (uint8_t*)&video_in_buffer, _is_nitro_emulator_get_video_in_size());
|
||||
int ret = ReadFrame(handle, (uint8_t*)&video_in_buffer, _is_nitro_emulator_get_video_in_size(capture_type));
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
static int StartCapture(libusb_device_handle *handle, int *out_frame_count) {
|
||||
static int set_capture_mode(libusb_device_handle *handle, CaptureScreensType capture_type) {
|
||||
uint8_t capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_BOTH;
|
||||
if(capture_type == CAPTURE_SCREENS_TOP)
|
||||
capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_TOP;
|
||||
if(capture_type == CAPTURE_SCREENS_BOTTOM)
|
||||
capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_BOTTOM;
|
||||
return UpdateFrameForwardConfig(handle, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24 | capture_mode_flag | IS_NITRO_FORWARD_CONFIG_RATE_FULL);
|
||||
}
|
||||
|
||||
static int StartCapture(libusb_device_handle *handle, uint16_t &out_frame_count, float &single_frame_time, CaptureScreensType capture_type) {
|
||||
int ret = DisableLca2(handle);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = UpdateFrameForwardConfig(handle, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24 | IS_NITRO_FORWARD_CONFIG_MODE_BOTH | IS_NITRO_FORWARD_CONFIG_RATE_FULL);
|
||||
ret = set_capture_mode(handle, capture_type);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = SetForwardFrameCount(handle, NUM_CONSECUTIVE_FRAMES);
|
||||
ret = SetForwardFrameCount(handle, FRAME_BUFFER_SIZE);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_DISABLE);
|
||||
// Reset this in case it's high. At around 0xFFFF, reading from the USB DMA seems to fail...
|
||||
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_RESTART | IS_NITRO_FORWARD_ENABLE_ENABLE);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_ENABLE);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
// Get to the closest next frame
|
||||
auto clock_start = std::chrono::high_resolution_clock::now();
|
||||
uint16_t oldFrameCount;
|
||||
uint16_t newFrameCount;
|
||||
ret = GetFrameCounter(handle, &oldFrameCount);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_ENABLE | IS_NITRO_FORWARD_ENABLE_RESTART);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
uint16_t newFrameCount;
|
||||
while (true) {
|
||||
newFrameCount = oldFrameCount;
|
||||
while(newFrameCount == oldFrameCount) {
|
||||
ret = GetFrameCounter(handle, &newFrameCount);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
if (newFrameCount < 64 && newFrameCount != oldFrameCount && newFrameCount != oldFrameCount + 1) {
|
||||
const auto curr_time = std::chrono::high_resolution_clock::now();
|
||||
const std::chrono::duration<double> diff = curr_time - clock_start;
|
||||
// If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up
|
||||
if(diff.count() > 0.2)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = StartUsbCaptureDma(handle);
|
||||
// Get to the next modulo 32 frame.
|
||||
// We also do this to measure the time that is needed for each frame...
|
||||
// To do so, a minimum of 4 frames is required (FRAME_BUFFER_SIZE - 1 + 4)
|
||||
clock_start = std::chrono::high_resolution_clock::now();
|
||||
ret = GetFrameCounter(handle, &oldFrameCount);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = drain_frames(handle, newFrameCount, 0);
|
||||
*out_frame_count = newFrameCount;
|
||||
uint16_t targetFrameCount = (newFrameCount + FRAME_BUFFER_SIZE + 3) & (~(FRAME_BUFFER_SIZE - 1));
|
||||
while(oldFrameCount != targetFrameCount) {
|
||||
ret = GetFrameCounter(handle, &oldFrameCount);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
// Placing a sleep of some kind here would be much better...
|
||||
// Though this is only executed for a small time when first connecting...
|
||||
const auto curr_time = std::chrono::high_resolution_clock::now();
|
||||
const std::chrono::duration<double> diff = curr_time - clock_start;
|
||||
// If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up
|
||||
if(diff.count() > 1.0)
|
||||
break;
|
||||
}
|
||||
const auto curr_time = std::chrono::high_resolution_clock::now();
|
||||
const std::chrono::duration<double> diff = curr_time - clock_start;
|
||||
// Sometimes the upper 8 bits aren't updated... Use only the lower 8 bits.
|
||||
newFrameCount &= 0xFF;
|
||||
oldFrameCount &= 0xFF;
|
||||
int frame_diff = ((int)oldFrameCount) - ((int)newFrameCount);
|
||||
if(frame_diff < 0)
|
||||
frame_diff += 1 << 8;
|
||||
out_frame_count = oldFrameCount;
|
||||
// Determine how much time a single frame takes. We'll use it for sleeps
|
||||
if(frame_diff == 0)
|
||||
single_frame_time = 0;
|
||||
else
|
||||
single_frame_time = diff.count() / frame_diff;
|
||||
|
||||
// Start the actual DMA
|
||||
if(single_frame_time > 0) {
|
||||
ret = StartUsbCaptureDma(handle);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames) {
|
||||
static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) {
|
||||
int ret = 0;
|
||||
if(do_drain_frames)
|
||||
ret = drain_frames(handle, NUM_CONSECUTIVE_FRAMES, start_frames);
|
||||
ret = drain_frames(handle, FRAME_BUFFER_SIZE, start_frames, capture_type);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
ret = StopUsbCaptureDma(handle);
|
||||
@ -238,38 +292,98 @@ static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int st
|
||||
return UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_DISABLE);
|
||||
}
|
||||
|
||||
int reset_capture_frames(libusb_device_handle* handle, int &frame_counter, uint16_t &total_frame_counter) {
|
||||
total_frame_counter += 1;
|
||||
frame_counter += 1;
|
||||
static void frame_wait(float single_frame_time, std::chrono::time_point<std::chrono::high_resolution_clock> clock_last_reset, int curr_frame_counter, int last_frame_counter) {
|
||||
if(curr_frame_counter == 0)
|
||||
return;
|
||||
auto curr_time = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> diff = curr_time - clock_last_reset;
|
||||
float expected_time = single_frame_time * curr_frame_counter;
|
||||
// If the current time is too low, sleep a bit to make sure we don't overrun the framerate counter
|
||||
// Don't do it regardless of the situation, and only in small increments...
|
||||
// Otherwise there is the risk of sleeping too much
|
||||
while((diff.count() < expected_time) && ((expected_time - diff.count()) > (single_frame_time / 4)) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1)))) {
|
||||
default_sleep((expected_time - diff.count()) / 4);
|
||||
curr_time = std::chrono::high_resolution_clock::now();
|
||||
diff = curr_time - clock_last_reset;
|
||||
}
|
||||
}
|
||||
|
||||
if(frame_counter == NUM_CONSECUTIVE_FRAMES) {
|
||||
int reset_capture_frames(libusb_device_handle* handle, uint16_t &curr_frame_counter, uint16_t &last_frame_counter, float &single_frame_time, std::chrono::time_point<std::chrono::high_resolution_clock> &clock_last_reset, CaptureScreensType &curr_capture_type, CaptureScreensType wanted_capture_type, int multiplier) {
|
||||
curr_frame_counter += 1;
|
||||
|
||||
if(curr_frame_counter == FRAME_BUFFER_SIZE) {
|
||||
int ret = StopUsbCaptureDma(handle);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
uint16_t internalFrameCount = -1;
|
||||
int diff;
|
||||
do {
|
||||
if (internalFrameCount != -1)
|
||||
default_sleep(8000);
|
||||
ret = GetFrameCounter(handle, &internalFrameCount);
|
||||
// If the user requests a mode change, accomodate them.
|
||||
// Though it may lag for a bit...
|
||||
if(wanted_capture_type != curr_capture_type) {
|
||||
curr_capture_type = wanted_capture_type;
|
||||
ret = set_capture_mode(handle, curr_capture_type);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
diff = internalFrameCount - total_frame_counter;
|
||||
if(diff > 32768)
|
||||
diff -= 1 << 16;
|
||||
} while(diff <= 0);
|
||||
*/
|
||||
uint16_t internalFrameCount = 0;
|
||||
uint16_t full_internalFrameCount = 0;
|
||||
int frame_diff = 0;
|
||||
int diff_target = FRAME_BUFFER_SIZE * multiplier;
|
||||
do {
|
||||
// Check how many frames have passed...
|
||||
ret = GetFrameCounter(handle, &internalFrameCount);
|
||||
full_internalFrameCount = internalFrameCount;
|
||||
// Sometimes the upper 8 bits aren't updated... Use only the lower 8 bits.
|
||||
internalFrameCount &= 0xFF;
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
frame_diff = internalFrameCount - last_frame_counter;
|
||||
if(frame_diff < 0)
|
||||
frame_diff += 1 << 8;
|
||||
// If the frames haven't advanced, the DS is either turned off or sleeping. If so, avoid locking up
|
||||
if(frame_diff == 0)
|
||||
break;
|
||||
const auto curr_time = std::chrono::high_resolution_clock::now();
|
||||
const std::chrono::duration<double> diff = curr_time - clock_last_reset;
|
||||
// If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up
|
||||
if(diff.count() > (1.0 * multiplier)) {
|
||||
frame_diff = 0;
|
||||
break;
|
||||
}
|
||||
// Exit if enough frames have passed, or if there currently is some delay.
|
||||
// Exiting early makes it possible to catch up to the DMA, if we're behind.
|
||||
} while((frame_diff < diff_target) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1))));
|
||||
// Determine how much time a single frame takes. We'll use it for sleeps
|
||||
const auto curr_time = std::chrono::high_resolution_clock::now();
|
||||
const std::chrono::duration<double> diff = curr_time - clock_last_reset;
|
||||
if(frame_diff == 0)
|
||||
single_frame_time = 0;
|
||||
else
|
||||
single_frame_time = diff.count() / (frame_diff / ((float)multiplier));
|
||||
clock_last_reset = curr_time;
|
||||
|
||||
// Save the current frame counter's 8 LSB
|
||||
last_frame_counter = internalFrameCount;
|
||||
|
||||
// If we're nearing 0xFFFF for the frame counter, reset it.
|
||||
// It's a problematic value for DMA reading
|
||||
if(frame_diff && (full_internalFrameCount >= 0xF000)) {
|
||||
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_RESTART | IS_NITRO_FORWARD_ENABLE_ENABLE);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
clock_last_reset = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_ENABLE);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
frame_counter = 0;
|
||||
ret = StartUsbCaptureDma(handle);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
curr_frame_counter = 0;
|
||||
|
||||
// Start the actual DMA
|
||||
if(single_frame_time > 0) {
|
||||
ret = StartUsbCaptureDma(handle);
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
@ -278,42 +392,55 @@ void is_nitro_capture_main_loop(CaptureData* capture_data) {
|
||||
if(!usb_is_initialized())
|
||||
return;
|
||||
libusb_device_handle *handle = (libusb_device_handle*)capture_data->handle;
|
||||
int frame_counter = 0;
|
||||
int ret = StartCapture(handle, &frame_counter);
|
||||
uint16_t total_frame_counter = frame_counter;
|
||||
uint16_t last_frame_counter = 0;
|
||||
float single_frame_time = 0;
|
||||
uint16_t curr_frame_counter = 0;
|
||||
CaptureScreensType curr_capture_type = capture_data->status.capture_type;
|
||||
int ret = StartCapture(handle, last_frame_counter, single_frame_time, curr_capture_type);
|
||||
if(ret < 0) {
|
||||
capture_error_print(true, capture_data, "Capture Start: Failed");
|
||||
return;
|
||||
}
|
||||
int inner_curr_in = 0;
|
||||
auto clock_start = std::chrono::high_resolution_clock::now();
|
||||
auto clock_last_reset = std::chrono::high_resolution_clock::now();
|
||||
|
||||
while(capture_data->status.connected && capture_data->status.running) {
|
||||
ret = ReadFrame(handle, (uint8_t*)&capture_data->capture_buf[inner_curr_in], _is_nitro_emulator_get_video_in_size());
|
||||
if(ret < 0) {
|
||||
capture_error_print(true, capture_data, "Disconnected: Read error");
|
||||
break;
|
||||
}
|
||||
frame_wait(single_frame_time, clock_last_reset, curr_frame_counter, last_frame_counter);
|
||||
|
||||
const auto curr_time = std::chrono::high_resolution_clock::now();
|
||||
const std::chrono::duration<double> diff = curr_time - clock_start;
|
||||
ret = reset_capture_frames(handle, frame_counter, total_frame_counter);
|
||||
if(single_frame_time > 0) {
|
||||
ret = ReadFrame(handle, (uint8_t*)&capture_data->capture_buf[inner_curr_in], _is_nitro_emulator_get_video_in_size(curr_capture_type));
|
||||
if(ret < 0) {
|
||||
capture_error_print(true, capture_data, "Disconnected: Read error");
|
||||
break;
|
||||
}
|
||||
// Output to the other threads...
|
||||
const auto curr_time = std::chrono::high_resolution_clock::now();
|
||||
const std::chrono::duration<double> diff = curr_time - clock_start;
|
||||
clock_start = curr_time;
|
||||
capture_data->time_in_buf[inner_curr_in] = diff.count();
|
||||
capture_data->read[inner_curr_in] = _is_nitro_emulator_get_video_in_size(curr_capture_type);
|
||||
capture_data->capture_type[inner_curr_in] = curr_capture_type;
|
||||
|
||||
inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS;
|
||||
if(capture_data->status.cooldown_curr_in)
|
||||
capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1;
|
||||
capture_data->status.curr_in = inner_curr_in;
|
||||
capture_data->status.video_wait.unlock();
|
||||
capture_data->status.audio_wait.unlock();
|
||||
}
|
||||
else {
|
||||
capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM;
|
||||
default_sleep(20);
|
||||
}
|
||||
capture_data->status.curr_delay = last_frame_counter % FRAME_BUFFER_SIZE;
|
||||
ret = reset_capture_frames(handle, curr_frame_counter, last_frame_counter, single_frame_time, clock_last_reset, curr_capture_type, capture_data->status.capture_type, 1);
|
||||
if(ret < 0) {
|
||||
capture_error_print(true, capture_data, "Disconnected: Frame counter reset error");
|
||||
break;
|
||||
}
|
||||
clock_start = curr_time;
|
||||
capture_data->time_in_buf[inner_curr_in] = diff.count();
|
||||
capture_data->read[inner_curr_in] = _is_nitro_emulator_get_video_in_size();
|
||||
|
||||
inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS;
|
||||
if(capture_data->status.cooldown_curr_in)
|
||||
capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1;
|
||||
capture_data->status.curr_in = inner_curr_in;
|
||||
capture_data->status.video_wait.unlock();
|
||||
capture_data->status.audio_wait.unlock();
|
||||
}
|
||||
EndCapture(handle, true, frame_counter);
|
||||
EndCapture(handle, true, curr_frame_counter, curr_capture_type);
|
||||
}
|
||||
|
||||
void usb_is_nitro_capture_cleanup(CaptureData* capture_data) {
|
||||
@ -322,16 +449,23 @@ void usb_is_nitro_capture_cleanup(CaptureData* capture_data) {
|
||||
is_nitro_connection_end((libusb_device_handle*)capture_data->handle);
|
||||
}
|
||||
|
||||
void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureDevice* capture_device) {
|
||||
void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureScreensType capture_type) {
|
||||
if(!usb_is_initialized())
|
||||
return;
|
||||
for(int i = 0; i < IN_VIDEO_HEIGHT_DS; i++)
|
||||
for(int j = 0; j < IN_VIDEO_WIDTH_DS; j++) {
|
||||
int pixel = (i * IN_VIDEO_WIDTH_DS) + j;
|
||||
p_out->screen_data[pixel][0] = p_in->is_nitro_capture_received.video_in.screen_data[pixel][2];
|
||||
p_out->screen_data[pixel][1] = p_in->is_nitro_capture_received.video_in.screen_data[pixel][1];
|
||||
p_out->screen_data[pixel][2] = p_in->is_nitro_capture_received.video_in.screen_data[pixel][0];
|
||||
}
|
||||
int num_pixels = _is_nitro_emulator_get_video_in_size(capture_type) / 3;
|
||||
int out_start_pos = 0;
|
||||
int out_clear_pos = num_pixels;
|
||||
if(capture_type == CAPTURE_SCREENS_BOTTOM) {
|
||||
out_start_pos = num_pixels;
|
||||
out_clear_pos = 0;
|
||||
}
|
||||
if((capture_type == CAPTURE_SCREENS_BOTTOM) || (capture_type == CAPTURE_SCREENS_TOP))
|
||||
memset(p_out->screen_data[out_clear_pos], 0, num_pixels * 3);
|
||||
for(int i = 0; i < num_pixels; i++) {
|
||||
p_out->screen_data[i + out_start_pos][0] = p_in->is_nitro_capture_received.video_in.screen_data[i][2];
|
||||
p_out->screen_data[i + out_start_pos][1] = p_in->is_nitro_capture_received.video_in.screen_data[i][1];
|
||||
p_out->screen_data[i + out_start_pos][2] = p_in->is_nitro_capture_received.video_in.screen_data[i][0];
|
||||
}
|
||||
}
|
||||
|
||||
void usb_is_nitro_init() {
|
||||
|
Loading…
Reference in New Issue
Block a user