support for filtering mtp/ftp mount options. use builtin config for ftp port,user,pass.
This commit is contained in:
@@ -341,7 +341,7 @@ endif()
|
|||||||
if (ENABLE_FTPSRV)
|
if (ENABLE_FTPSRV)
|
||||||
FetchContent_Declare(ftpsrv
|
FetchContent_Declare(ftpsrv
|
||||||
GIT_REPOSITORY https://github.com/ITotalJustice/ftpsrv.git
|
GIT_REPOSITORY https://github.com/ITotalJustice/ftpsrv.git
|
||||||
GIT_TAG a7c2283
|
GIT_TAG 7770dab
|
||||||
SOURCE_SUBDIR NONE
|
SOURCE_SUBDIR NONE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -358,7 +358,7 @@ if (ENABLE_FTPSRV)
|
|||||||
USE_VFS_STORAGE=$<BOOL:TRUE>
|
USE_VFS_STORAGE=$<BOOL:TRUE>
|
||||||
# disabled as it may conflict with the gamecard menu.
|
# disabled as it may conflict with the gamecard menu.
|
||||||
USE_VFS_GC=$<BOOL:FALSE>
|
USE_VFS_GC=$<BOOL:FALSE>
|
||||||
USE_VFS_USBHSFS=$<BOOL:${ENABLE_LIBUSBHSFS}>
|
USE_VFS_USBHSFS=$<BOOL:FALSE>
|
||||||
VFS_NX_BUFFER_IO=$<BOOL:TRUE>
|
VFS_NX_BUFFER_IO=$<BOOL:TRUE>
|
||||||
# let sphaira handle init / closing of the hdd.
|
# let sphaira handle init / closing of the hdd.
|
||||||
USE_VFS_USBHSFS_INIT=$<BOOL:FALSE>
|
USE_VFS_USBHSFS_INIT=$<BOOL:FALSE>
|
||||||
@@ -378,14 +378,7 @@ if (ENABLE_FTPSRV)
|
|||||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/utils.c
|
${ftpsrv_SOURCE_DIR}/src/platform/nx/utils.c
|
||||||
)
|
)
|
||||||
|
|
||||||
if (ENABLE_LIBUSBHSFS)
|
target_link_libraries(ftpsrv_helper PUBLIC ftpsrv)
|
||||||
target_sources(ftpsrv_helper PRIVATE
|
|
||||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_stdio.c
|
|
||||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_hdd.c
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(ftpsrv_helper PUBLIC ftpsrv libusbhsfs)
|
|
||||||
target_include_directories(ftpsrv_helper PUBLIC ${ftpsrv_SOURCE_DIR}/src/platform)
|
target_include_directories(ftpsrv_helper PUBLIC ${ftpsrv_SOURCE_DIR}/src/platform)
|
||||||
|
|
||||||
target_compile_definitions(sphaira PRIVATE ENABLE_FTPSRV)
|
target_compile_definitions(sphaira PRIVATE ENABLE_FTPSRV)
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ public:
|
|||||||
static void DisplayAdvancedOptions(bool left_side = true);
|
static void DisplayAdvancedOptions(bool left_side = true);
|
||||||
static void DisplayInstallOptions(bool left_side = true);
|
static void DisplayInstallOptions(bool left_side = true);
|
||||||
static void DisplayDumpOptions(bool left_side = true);
|
static void DisplayDumpOptions(bool left_side = true);
|
||||||
|
static void DisplayFtpOptions(bool left_side = true);
|
||||||
|
static void DisplayMtpOptions(bool left_side = true);
|
||||||
|
static void DisplayHddOptions(bool left_side = true);
|
||||||
|
|
||||||
// helper for sidebar options to toggle install on/off
|
// helper for sidebar options to toggle install on/off
|
||||||
static void ShowEnableInstallPromptOption(option::OptionBool& option, bool& enable);
|
static void ShowEnableInstallPromptOption(option::OptionBool& option, bool& enable);
|
||||||
@@ -342,9 +345,38 @@ public:
|
|||||||
// todo: move this into it's own menu
|
// todo: move this into it's own menu
|
||||||
option::OptionLong m_text_scroll_speed{"accessibility", "text_scroll_speed", 1}; // normal
|
option::OptionLong m_text_scroll_speed{"accessibility", "text_scroll_speed", 1}; // normal
|
||||||
|
|
||||||
|
// ftp options.
|
||||||
|
option::OptionLong m_ftp_port{"ftp", "port", 5000};
|
||||||
|
option::OptionBool m_ftp_anon{"ftp", "anon", true};
|
||||||
|
option::OptionString m_ftp_user{"ftp", "user", ""};
|
||||||
|
option::OptionString m_ftp_pass{"ftp", "pass", ""};
|
||||||
|
option::OptionBool m_ftp_show_album{"ftp", "show_album", true};
|
||||||
|
option::OptionBool m_ftp_show_ams_contents{"ftp", "show_ams_contents", false};
|
||||||
|
option::OptionBool m_ftp_show_bis_storage{"ftp", "show_bis_storage", false};
|
||||||
|
option::OptionBool m_ftp_show_bis_fs{"ftp", "show_bis_fs", false};
|
||||||
|
option::OptionBool m_ftp_show_content_system{"ftp", "show_content_system", false};
|
||||||
|
option::OptionBool m_ftp_show_content_user{"ftp", "show_content_user", false};
|
||||||
|
option::OptionBool m_ftp_show_content_sd{"ftp", "show_content_sd", false};
|
||||||
|
// option::OptionBool m_ftp_show_content_sd0{"ftp", "show_content_sd0", false};
|
||||||
|
// option::OptionBool m_ftp_show_custom_system{"ftp", "show_custom_system", false};
|
||||||
|
// option::OptionBool m_ftp_show_custom_sd{"ftp", "show_custom_sd", false};
|
||||||
|
option::OptionBool m_ftp_show_games{"ftp", "show_games", true};
|
||||||
|
option::OptionBool m_ftp_show_install{"ftp", "show_install", true};
|
||||||
|
option::OptionBool m_ftp_show_mounts{"ftp", "show_mounts", false};
|
||||||
|
option::OptionBool m_ftp_show_switch{"ftp", "show_switch", false};
|
||||||
|
|
||||||
// mtp options.
|
// mtp options.
|
||||||
option::OptionLong m_mtp_vid{"mtp", "vid", 0x057e}; // nintendo
|
option::OptionLong m_mtp_vid{"mtp", "vid", 0x057e}; // nintendo (hidden from ui)
|
||||||
option::OptionLong m_mtp_pid{"mtp", "pid", 0x201d}; // switch
|
option::OptionLong m_mtp_pid{"mtp", "pid", 0x201d}; // switch (hidden from ui)
|
||||||
|
option::OptionBool m_mtp_allocate_file{"mtp", "allocate_file", true};
|
||||||
|
option::OptionBool m_mtp_show_album{"mtp", "show_album", true};
|
||||||
|
option::OptionBool m_mtp_show_content_sd{"mtp", "show_content_sd", false};
|
||||||
|
option::OptionBool m_mtp_show_content_system{"mtp", "show_content_system", false};
|
||||||
|
option::OptionBool m_mtp_show_content_user{"mtp", "show_content_user", false};
|
||||||
|
option::OptionBool m_mtp_show_games{"mtp", "show_games", true};
|
||||||
|
option::OptionBool m_mtp_show_install{"mtp", "show_install", true};
|
||||||
|
option::OptionBool m_mtp_show_mounts{"mtp", "show_mounts", false};
|
||||||
|
option::OptionBool m_mtp_show_speedtest{"mtp", "show_speedtest", false};
|
||||||
|
|
||||||
std::shared_ptr<fs::FsNativeSd> m_fs{};
|
std::shared_ptr<fs::FsNativeSd> m_fs{};
|
||||||
audio::SongID m_background_music{};
|
audio::SongID m_background_music{};
|
||||||
|
|||||||
@@ -44,6 +44,14 @@ public:
|
|||||||
m_depends_click = depends_click;
|
m_depends_click = depends_click;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetDirty(bool dirty = true) {
|
||||||
|
m_dirty = dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto IsDirty() const -> bool {
|
||||||
|
return m_dirty;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
auto IsEnabled() const -> bool {
|
auto IsEnabled() const -> bool {
|
||||||
if (m_depends_callback) {
|
if (m_depends_callback) {
|
||||||
@@ -69,6 +77,7 @@ private:
|
|||||||
DependsClickCallback m_depends_click{};
|
DependsClickCallback m_depends_click{};
|
||||||
ScrollingText m_scolling_title{};
|
ScrollingText m_scolling_title{};
|
||||||
ScrollingText m_scolling_value{};
|
ScrollingText m_scolling_value{};
|
||||||
|
bool m_dirty{};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -174,11 +183,14 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class SidebarEntryTextInput final : public SidebarEntryTextBase {
|
class SidebarEntryTextInput final : public SidebarEntryTextBase {
|
||||||
|
public:
|
||||||
|
using Callback = std::function<void(SidebarEntryTextInput* input)>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// uses normal keyboard.
|
// uses normal keyboard.
|
||||||
explicit SidebarEntryTextInput(const std::string& title, const std::string& value, const std::string& guide = {}, s64 len_min = -1, s64 len_max = PATH_MAX, const std::string& info = "");
|
explicit SidebarEntryTextInput(const std::string& title, const std::string& value, const std::string& guide = {}, s64 len_min = -1, s64 len_max = PATH_MAX, const std::string& info = "", const Callback& callback = nullptr);
|
||||||
// uses numpad.
|
// uses numpad.
|
||||||
explicit SidebarEntryTextInput(const std::string& title, s64 value, const std::string& guide = {}, s64 len_min = -1, s64 len_max = PATH_MAX, const std::string& info = "");
|
explicit SidebarEntryTextInput(const std::string& title, s64 value, const std::string& guide = {}, s64 len_min = -1, s64 len_max = PATH_MAX, const std::string& info = "", const Callback& callback = nullptr);
|
||||||
|
|
||||||
auto GetNumValue() const -> s64 {
|
auto GetNumValue() const -> s64 {
|
||||||
return std::stoul(GetValue());
|
return std::stoul(GetValue());
|
||||||
@@ -191,6 +203,7 @@ private:
|
|||||||
const std::string m_guide;
|
const std::string m_guide;
|
||||||
const s64 m_len_min;
|
const s64 m_len_min;
|
||||||
const s64 m_len_max;
|
const s64 m_len_max;
|
||||||
|
const Callback m_callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SidebarEntryFilePicker final : public SidebarEntryTextBase {
|
class SidebarEntryFilePicker final : public SidebarEntryTextBase {
|
||||||
@@ -210,10 +223,12 @@ class Sidebar : public Widget {
|
|||||||
public:
|
public:
|
||||||
enum class Side { LEFT, RIGHT };
|
enum class Side { LEFT, RIGHT };
|
||||||
using Items = std::vector<std::unique_ptr<SidebarEntryBase>>;
|
using Items = std::vector<std::unique_ptr<SidebarEntryBase>>;
|
||||||
|
using OnExitWhenChangedCallback = std::function<void()>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Sidebar(const std::string& title, Side side, float width = 450.f);
|
explicit Sidebar(const std::string& title, Side side, float width = 450.f);
|
||||||
explicit Sidebar(const std::string& title, const std::string& sub, Side side, float width = 450.f);
|
explicit Sidebar(const std::string& title, const std::string& sub, Side side, float width = 450.f);
|
||||||
|
~Sidebar();
|
||||||
|
|
||||||
auto Update(Controller* controller, TouchInfo* touch) -> void override;
|
auto Update(Controller* controller, TouchInfo* touch) -> void override;
|
||||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||||
@@ -227,6 +242,12 @@ public:
|
|||||||
return (T*)Add(std::make_unique<T>(std::forward<Args>(args)...));
|
return (T*)Add(std::make_unique<T>(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sets a callback that is called on exit when the any options were changed.
|
||||||
|
// the change detection isn't perfect, it just checks if the A button was pressed...
|
||||||
|
void SetOnExitWhenChanged(const OnExitWhenChangedCallback& cb) {
|
||||||
|
m_on_exit_when_changed = cb;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetIndex(s64 index);
|
void SetIndex(s64 index);
|
||||||
void SetupButtons();
|
void SetupButtons();
|
||||||
@@ -235,16 +256,18 @@ private:
|
|||||||
const std::string m_title;
|
const std::string m_title;
|
||||||
const std::string m_sub;
|
const std::string m_sub;
|
||||||
const Side m_side;
|
const Side m_side;
|
||||||
Items m_items;
|
Items m_items{};
|
||||||
s64 m_index{};
|
s64 m_index{};
|
||||||
|
|
||||||
std::unique_ptr<List> m_list;
|
std::unique_ptr<List> m_list{};
|
||||||
|
|
||||||
Vec4 m_top_bar{};
|
Vec4 m_top_bar{};
|
||||||
Vec4 m_bottom_bar{};
|
Vec4 m_bottom_bar{};
|
||||||
Vec2 m_title_pos{};
|
Vec2 m_title_pos{};
|
||||||
Vec4 m_base_pos{};
|
Vec4 m_base_pos{};
|
||||||
|
|
||||||
|
OnExitWhenChangedCallback m_on_exit_when_changed{};
|
||||||
|
|
||||||
static constexpr float m_title_size{28.f};
|
static constexpr float m_title_size{28.f};
|
||||||
// static constexpr Vec2 box_size{380.f, 70.f};
|
// static constexpr Vec2 box_size{380.f, 70.f};
|
||||||
static constexpr Vec2 m_box_size{400.f, 70.f};
|
static constexpr Vec2 m_box_size{400.f, 70.f};
|
||||||
|
|||||||
@@ -1549,9 +1549,37 @@ App::App(const char* argv0) {
|
|||||||
else if (app->m_nsz_compress_ldm.LoadFrom(Key, Value)) {}
|
else if (app->m_nsz_compress_ldm.LoadFrom(Key, Value)) {}
|
||||||
else if (app->m_nsz_compress_block.LoadFrom(Key, Value)) {}
|
else if (app->m_nsz_compress_block.LoadFrom(Key, Value)) {}
|
||||||
else if (app->m_nsz_compress_block_exponent.LoadFrom(Key, Value)) {}
|
else if (app->m_nsz_compress_block_exponent.LoadFrom(Key, Value)) {}
|
||||||
|
} else if (!std::strcmp(Section, "ftp")) {
|
||||||
|
if (app->m_ftp_port.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_anon.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_user.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_pass.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_album.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_ams_contents.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_bis_storage.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_bis_fs.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_content_system.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_content_user.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_content_sd.LoadFrom(Key, Value)) {}
|
||||||
|
// else if (app->m_ftp_show_content_sd0.LoadFrom(Key, Value)) {}
|
||||||
|
// else if (app->m_ftp_show_custom_system.LoadFrom(Key, Value)) {}
|
||||||
|
// else if (app->m_ftp_show_custom_sd.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_games.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_install.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_mounts.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_ftp_show_switch.LoadFrom(Key, Value)) {}
|
||||||
} else if (!std::strcmp(Section, "mtp")) {
|
} else if (!std::strcmp(Section, "mtp")) {
|
||||||
if (app->m_mtp_vid.LoadFrom(Key, Value)) {}
|
if (app->m_mtp_vid.LoadFrom(Key, Value)) {}
|
||||||
else if (app->m_mtp_pid.LoadFrom(Key, Value)) {}
|
else if (app->m_mtp_pid.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_mtp_allocate_file.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_mtp_show_album.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_mtp_show_content_sd.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_mtp_show_content_system.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_mtp_show_content_user.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_mtp_show_games.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_mtp_show_install.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_mtp_show_mounts.LoadFrom(Key, Value)) {}
|
||||||
|
else if (app->m_mtp_show_speedtest.LoadFrom(Key, Value)) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -2271,6 +2299,236 @@ void App::DisplayDumpOptions(bool left_side) {
|
|||||||
block_size_option->Depends(App::GetApp()->m_nsz_compress_block, "NSZ block compression is disabled."_i18n);
|
block_size_option->Depends(App::GetApp()->m_nsz_compress_block, "NSZ block compression is disabled."_i18n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::DisplayFtpOptions(bool left_side) {
|
||||||
|
// todo: prompt on exit to restart ftp server if options were changed.
|
||||||
|
auto options = std::make_unique<ui::Sidebar>("FTP Options"_i18n, left_side ? ui::Sidebar::Side::LEFT : ui::Sidebar::Side::RIGHT);
|
||||||
|
ON_SCOPE_EXIT(App::Push(std::move(options)));
|
||||||
|
|
||||||
|
options->SetOnExitWhenChanged([](){
|
||||||
|
if (App::GetFtpEnable()) {
|
||||||
|
App::Notify("Restarting FTP server..."_i18n);
|
||||||
|
App::SetFtpEnable(false);
|
||||||
|
App::SetFtpEnable(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>("Enable"_i18n, App::GetFtpEnable(), [](bool& enable){
|
||||||
|
App::SetFtpEnable(enable);
|
||||||
|
}, "Enable FTP server to run in the background."_i18n);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryTextInput>(
|
||||||
|
"Port", App::GetApp()->m_ftp_port.Get(), "Port number", 1, 5,
|
||||||
|
"Opens the FTP server on this port."_i18n,
|
||||||
|
[](auto* input){
|
||||||
|
App::GetApp()->m_ftp_port.Set(input->GetNumValue());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Anon"_i18n, App::GetApp()->m_ftp_anon,
|
||||||
|
"Allows you to login without setting a username and password.\n"
|
||||||
|
"If disabled, you must set a user name and password below!"_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryTextInput>(
|
||||||
|
"User", App::GetApp()->m_ftp_user.Get(), "Username", -1, 64,
|
||||||
|
"Sets the username, must be set if anon is disabled."_i18n,
|
||||||
|
[](auto* input){
|
||||||
|
App::GetApp()->m_ftp_user.Set(input->GetValue());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryTextInput>(
|
||||||
|
"Pass", App::GetApp()->m_ftp_pass.Get(), "Password", -1, 64,
|
||||||
|
"Sets the password, must be set if anon is disabled."_i18n,
|
||||||
|
[](auto* input){
|
||||||
|
App::GetApp()->m_ftp_pass.Set(input->GetValue());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show album"_i18n, App::GetApp()->m_ftp_show_album,
|
||||||
|
"Shows the microSD card album folder."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show Atmosphere contents"_i18n, App::GetApp()->m_ftp_show_ams_contents,
|
||||||
|
"Shows the shortcut for the /atmosphere/contents folder."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show bis storage"_i18n, App::GetApp()->m_ftp_show_bis_storage,
|
||||||
|
"Shows the bis folder which contains the following:\n"
|
||||||
|
"- BootPartition1Root.bin\n"
|
||||||
|
"- BootPartition2Root.bin\n"
|
||||||
|
"- UserDataRoot.bin\n"
|
||||||
|
"- BootConfigAndPackage2Part1.bin\n"
|
||||||
|
"- BootConfigAndPackage2Part2.bin\n"
|
||||||
|
"- BootConfigAndPackage2Part3.bin\n"
|
||||||
|
"- BootConfigAndPackage2Part4.bin\n"
|
||||||
|
"- BootConfigAndPackage2Part5.bin\n"
|
||||||
|
"- BootConfigAndPackage2Part6.bin\n"
|
||||||
|
"- CalibrationFile.bin\n"
|
||||||
|
"- SafeMode.bin\n"
|
||||||
|
"- User.bin\n"
|
||||||
|
"- System.bin\n"
|
||||||
|
"- SystemProperEncryption.bin"_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show bis file systems"_i18n, App::GetApp()->m_ftp_show_bis_fs,
|
||||||
|
"Shows the following bis file systems:\n"
|
||||||
|
"- bis_calibration_file\n"
|
||||||
|
"- bis_safe_mode\n"
|
||||||
|
"- bis_user\n"
|
||||||
|
"- bis_system"_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show system contents"_i18n, App::GetApp()->m_ftp_show_content_system,
|
||||||
|
"Shows the system contents folder."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show user contents"_i18n, App::GetApp()->m_ftp_show_content_user,
|
||||||
|
"Shows the user contents folder."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show microSD contents"_i18n, App::GetApp()->m_ftp_show_content_sd,
|
||||||
|
"Shows the microSD contents folder.\n\n"
|
||||||
|
"NOTE: This is not the normal microSD card storage, it is instead "
|
||||||
|
"the location where NCA's are stored. The normal microSD card is always mounted."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show games"_i18n, App::GetApp()->m_ftp_show_games,
|
||||||
|
"Shows the games folder.\n\n"
|
||||||
|
"This folder contains all of your installed games, allowing you to create "
|
||||||
|
"backups over FTP!\n\n"
|
||||||
|
"NOTE: This folder is read-only. You cannot delete games over FTP."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show install"_i18n, App::GetApp()->m_ftp_show_install,
|
||||||
|
"Shows the install folder.\n\n"
|
||||||
|
"This folder is used for installing games via FTP.\n\n"
|
||||||
|
"NOTE: You must open the \"FTP Install\" menu when trying to install a game!"_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show mounts"_i18n, App::GetApp()->m_ftp_show_mounts,
|
||||||
|
"Shows the mounts folder.\n\n"
|
||||||
|
"This folder is contains all of the mounts added to Sphaira, allowing you to acces them over FTP!\n"
|
||||||
|
"For example, you can access your SMB, WebDav or other FTP mounts over FTP."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show switch"_i18n, App::GetApp()->m_ftp_show_switch,
|
||||||
|
"Shows the shortcut for the /switch folder."
|
||||||
|
"This is the folder that contains all your homebrew (NRO's)."_i18n
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::DisplayMtpOptions(bool left_side) {
|
||||||
|
// todo: prompt on exit to restart ftp server if options were changed.
|
||||||
|
auto options = std::make_unique<ui::Sidebar>("MTP Options"_i18n, left_side ? ui::Sidebar::Side::LEFT : ui::Sidebar::Side::RIGHT);
|
||||||
|
ON_SCOPE_EXIT(App::Push(std::move(options)));
|
||||||
|
|
||||||
|
options->SetOnExitWhenChanged([](){
|
||||||
|
if (App::GetMtpEnable()) {
|
||||||
|
App::Notify("Restarting MTP server..."_i18n);
|
||||||
|
App::SetMtpEnable(false);
|
||||||
|
App::SetMtpEnable(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>("Enable"_i18n, App::GetMtpEnable(), [](bool& enable){
|
||||||
|
App::SetMtpEnable(enable);
|
||||||
|
}, "Enable MTP server to run in the background."_i18n);
|
||||||
|
|
||||||
|
// not sure if i want to expose this to users yet.
|
||||||
|
// its also stubbed currently.
|
||||||
|
#if 0
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Pre-allocate file"_i18n, App::GetApp()->m_mtp_allocate_file,
|
||||||
|
"Enables pre-allocating the file size before writing.\n"
|
||||||
|
"This speeds up file writes, however, this can cause timeouts if all these conditions are met:\n"
|
||||||
|
"- using Windows\n"
|
||||||
|
"- using emuMMC\n"
|
||||||
|
"- transferring a large file (>1GB)\n\n"
|
||||||
|
"This option should be left enabled, however if you use the above and experience timeouts, "
|
||||||
|
"then try again with this option disabled."_i18n
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show album"_i18n, App::GetApp()->m_mtp_show_album,
|
||||||
|
"Shows the microSD card album folder."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show microSD contents"_i18n, App::GetApp()->m_mtp_show_content_sd,
|
||||||
|
"Shows the microSD contents folder.\n\n"
|
||||||
|
"NOTE: This is not the normal microSD card storage, it is instead "
|
||||||
|
"the location where NCA's are stored. The normal microSD card is always mounted."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show system contents"_i18n, App::GetApp()->m_mtp_show_content_system,
|
||||||
|
"Shows the system contents folder."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show user contents"_i18n, App::GetApp()->m_mtp_show_content_user,
|
||||||
|
"Shows the user contents folder."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show games"_i18n, App::GetApp()->m_mtp_show_games,
|
||||||
|
"Shows the games folder.\n\n"
|
||||||
|
"This folder contains all of your installed games, allowing you to create "
|
||||||
|
"backups over MTP!\n\n"
|
||||||
|
"NOTE: This folder is read-only. You cannot delete games over MTP."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show install"_i18n, App::GetApp()->m_mtp_show_install,
|
||||||
|
"Shows the install folder.\n\n"
|
||||||
|
"This folder is used for installing games via MTP.\n\n"
|
||||||
|
"NOTE: You must open the \"MTP Install\" menu when trying to install a game!"_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show mounts"_i18n, App::GetApp()->m_mtp_show_mounts,
|
||||||
|
"Shows the mounts folder.\n\n"
|
||||||
|
"This folder is contains all of the mounts added to Sphaira, allowing you to acces them over MTP!\n"
|
||||||
|
"For example, you can access your SMB, WebDav and FTP mounts over MTP."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>(
|
||||||
|
"Show DevNull"_i18n, App::GetApp()->m_mtp_show_speedtest,
|
||||||
|
"Shows the DevNull (Speed Test) folder.\n\n"
|
||||||
|
"This folder is used for benchmarking USB uploads. "
|
||||||
|
"This ia virtual folder, nothing is actally written to disk."_i18n
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::DisplayHddOptions(bool left_side) {
|
||||||
|
auto options = std::make_unique<ui::Sidebar>("HDD Options"_i18n, left_side ? ui::Sidebar::Side::LEFT : ui::Sidebar::Side::RIGHT);
|
||||||
|
ON_SCOPE_EXIT(App::Push(std::move(options)));
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>("Enable"_i18n, App::GetHddEnable(), [](bool& enable){
|
||||||
|
App::SetHddEnable(enable);
|
||||||
|
}, "Enable mounting of connected USB/HDD devices. "
|
||||||
|
"Connected devices can be used in the FileBrowser, as well as a backup location when dumping games and saves."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<ui::SidebarEntryBool>("HDD write protect"_i18n, App::GetWriteProtect(), [](bool& enable){
|
||||||
|
App::SetWriteProtect(enable);
|
||||||
|
}, "Makes the connected HDD read-only."_i18n);
|
||||||
|
}
|
||||||
|
|
||||||
void App::ShowEnableInstallPrompt() {
|
void App::ShowEnableInstallPrompt() {
|
||||||
// warn the user the dangers of installing.
|
// warn the user the dangers of installing.
|
||||||
App::Push<ui::OptionBox>(
|
App::Push<ui::OptionBox>(
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include "utils/thread.hpp"
|
#include "utils/thread.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <minIni.h>
|
|
||||||
#include <ftpsrv.h>
|
#include <ftpsrv.h>
|
||||||
#include <ftpsrv_vfs.h>
|
#include <ftpsrv_vfs.h>
|
||||||
#include <nx/vfs_nx.h>
|
#include <nx/vfs_nx.h>
|
||||||
@@ -30,11 +29,12 @@ struct InstallSharedData {
|
|||||||
bool enabled;
|
bool enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* INI_PATH = "/config/ftpsrv/config.ini";
|
FtpSrvConfig g_ftpsrv_config{};
|
||||||
FtpSrvConfig g_ftpsrv_config = {0};
|
int g_ftpsrv_mount_flags{};
|
||||||
std::atomic_bool g_should_exit = false;
|
std::vector<VfsNxCustomPath> g_custom_vfs{};
|
||||||
bool g_is_running{false};
|
std::atomic_bool g_should_exit{};
|
||||||
Thread g_thread;
|
bool g_is_running{};
|
||||||
|
Thread g_thread{};
|
||||||
Mutex g_mutex{};
|
Mutex g_mutex{};
|
||||||
|
|
||||||
void ftp_log_callback(enum FTP_API_LOG_TYPE type, const char* msg) {
|
void ftp_log_callback(enum FTP_API_LOG_TYPE type, const char* msg) {
|
||||||
@@ -468,74 +468,12 @@ FtpVfs g_vfs_stdio = {
|
|||||||
void loop(void* arg) {
|
void loop(void* arg) {
|
||||||
log_write("[FTP] loop entered\n");
|
log_write("[FTP] loop entered\n");
|
||||||
|
|
||||||
// load config.
|
|
||||||
{
|
{
|
||||||
SCOPED_MUTEX(&g_mutex);
|
SCOPED_MUTEX(&g_mutex);
|
||||||
|
|
||||||
g_ftpsrv_config.log_callback = ftp_log_callback;
|
|
||||||
g_ftpsrv_config.progress_callback = ftp_progress_callback;
|
|
||||||
g_ftpsrv_config.anon = ini_getbool("Login", "anon", 0, INI_PATH);
|
|
||||||
int user_len = ini_gets("Login", "user", "", g_ftpsrv_config.user, sizeof(g_ftpsrv_config.user), INI_PATH);
|
|
||||||
int pass_len = ini_gets("Login", "pass", "", g_ftpsrv_config.pass, sizeof(g_ftpsrv_config.pass), INI_PATH);
|
|
||||||
g_ftpsrv_config.port = ini_getl("Network", "port", 5000, INI_PATH); // 5000 to keep compat with older sphaira
|
|
||||||
g_ftpsrv_config.timeout = ini_getl("Network", "timeout", 0, INI_PATH);
|
|
||||||
g_ftpsrv_config.use_localtime = ini_getbool("Misc", "use_localtime", 0, INI_PATH);
|
|
||||||
bool log_enabled = ini_getbool("Log", "log", 0, INI_PATH);
|
|
||||||
|
|
||||||
// get nx config
|
|
||||||
bool mount_devices = ini_getbool("Nx", "mount_devices", 1, INI_PATH);
|
|
||||||
bool mount_bis = ini_getbool("Nx", "mount_bis", 0, INI_PATH);
|
|
||||||
bool save_writable = ini_getbool("Nx", "save_writable", 0, INI_PATH);
|
|
||||||
g_ftpsrv_config.port = ini_getl("Nx", "app_port", g_ftpsrv_config.port, INI_PATH); // compat
|
|
||||||
|
|
||||||
// get Nx-App overrides
|
|
||||||
g_ftpsrv_config.anon = ini_getbool("Nx-App", "anon", g_ftpsrv_config.anon, INI_PATH);
|
|
||||||
user_len = ini_gets("Nx-App", "user", g_ftpsrv_config.user, g_ftpsrv_config.user, sizeof(g_ftpsrv_config.user), INI_PATH);
|
|
||||||
pass_len = ini_gets("Nx-App", "pass", g_ftpsrv_config.pass, g_ftpsrv_config.pass, sizeof(g_ftpsrv_config.pass), INI_PATH);
|
|
||||||
g_ftpsrv_config.port = ini_getl("Nx-App", "port", g_ftpsrv_config.port, INI_PATH);
|
|
||||||
g_ftpsrv_config.timeout = ini_getl("Nx-App", "timeout", g_ftpsrv_config.timeout, INI_PATH);
|
|
||||||
g_ftpsrv_config.use_localtime = ini_getbool("Nx-App", "use_localtime", g_ftpsrv_config.use_localtime, INI_PATH);
|
|
||||||
log_enabled = ini_getbool("Nx-App", "log", log_enabled, INI_PATH);
|
|
||||||
mount_devices = ini_getbool("Nx-App", "mount_devices", mount_devices, INI_PATH);
|
|
||||||
mount_bis = ini_getbool("Nx-App", "mount_bis", mount_bis, INI_PATH);
|
|
||||||
save_writable = ini_getbool("Nx-App", "save_writable", save_writable, INI_PATH);
|
|
||||||
|
|
||||||
g_should_exit = false;
|
g_should_exit = false;
|
||||||
mount_devices = true;
|
|
||||||
g_ftpsrv_config.timeout = 0;
|
|
||||||
|
|
||||||
if (!g_ftpsrv_config.port) {
|
|
||||||
g_ftpsrv_config.port = 5000;
|
|
||||||
log_write("[FTP] no port config, defaulting to 5000\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep compat with older sphaira
|
|
||||||
if (!user_len && !pass_len) {
|
|
||||||
g_ftpsrv_config.anon = true;
|
|
||||||
log_write("[FTP] no user pass, defaulting to anon\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
fsdev_wrapMountSdmc();
|
fsdev_wrapMountSdmc();
|
||||||
|
vfs_nx_init(g_custom_vfs.data(), std::size(g_custom_vfs), g_ftpsrv_mount_flags, false);
|
||||||
static const VfsNxCustomPath custom_vfs[] = {
|
|
||||||
{
|
|
||||||
.name = "games",
|
|
||||||
.user = NULL,
|
|
||||||
.func = &g_vfs_stdio,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "mounts",
|
|
||||||
.user = NULL,
|
|
||||||
.func = &g_vfs_stdio,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "install",
|
|
||||||
.user = NULL,
|
|
||||||
.func = &g_vfs_install,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
vfs_nx_init(custom_vfs, std::size(custom_vfs), mount_devices, save_writable, mount_bis, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ON_SCOPE_EXIT(
|
ON_SCOPE_EXIT(
|
||||||
@@ -566,13 +504,90 @@ bool Init() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (R_FAILED(fsdev_wrapMountSdmc())) {
|
g_ftpsrv_mount_flags = 0;
|
||||||
// log_write("[FTP] cannot mount sdmc\n");
|
g_ftpsrv_config = {};
|
||||||
// return false;
|
g_custom_vfs.clear();
|
||||||
// }
|
|
||||||
|
|
||||||
// todo: replace everything with ini_browse for faster loading.
|
auto app = App::GetApp();
|
||||||
// or load everything in the init thread.
|
g_ftpsrv_config.log_callback = ftp_log_callback;
|
||||||
|
g_ftpsrv_config.progress_callback = ftp_progress_callback;
|
||||||
|
g_ftpsrv_config.anon = app->m_ftp_anon.Get();
|
||||||
|
std::strncpy(g_ftpsrv_config.user, app->m_ftp_user.Get().c_str(), sizeof(g_ftpsrv_config.user) - 1);
|
||||||
|
std::strncpy(g_ftpsrv_config.pass, app->m_ftp_pass.Get().c_str(), sizeof(g_ftpsrv_config.pass) - 1);
|
||||||
|
g_ftpsrv_config.port = app->m_ftp_port.Get();
|
||||||
|
|
||||||
|
if (app->m_ftp_show_album.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_ALBUM;
|
||||||
|
}
|
||||||
|
if (app->m_ftp_show_ams_contents.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_AMS_CONTENTS;
|
||||||
|
}
|
||||||
|
if (app->m_ftp_show_bis_storage.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_BIS_STORAGE;
|
||||||
|
}
|
||||||
|
if (app->m_ftp_show_bis_fs.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_BIS_FS;
|
||||||
|
}
|
||||||
|
if (app->m_ftp_show_content_system.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_CONTENT_SYSTEM;
|
||||||
|
}
|
||||||
|
if (app->m_ftp_show_content_user.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_CONTENT_USER;
|
||||||
|
}
|
||||||
|
if (app->m_ftp_show_content_sd.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_CONTENT_SDCARD;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
if (app->m_ftp_show_content_sd0.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_CONTENT_SDCARD0;
|
||||||
|
}
|
||||||
|
if (app->m_ftp_show_custom_system.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_CUSTOM_SYSTEM;
|
||||||
|
}
|
||||||
|
if (app->m_ftp_show_custom_sd.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_CUSTOM_SDCARD;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (app->m_ftp_show_switch.Get()) {
|
||||||
|
g_ftpsrv_mount_flags |= VfsNxMountFlag_SWITCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app->m_ftp_show_install.Get()) {
|
||||||
|
g_custom_vfs.push_back({
|
||||||
|
.name = "install",
|
||||||
|
.user = &g_shared_data,
|
||||||
|
.func = &g_vfs_install,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app->m_ftp_show_games.Get()) {
|
||||||
|
g_custom_vfs.push_back({
|
||||||
|
.name = "games",
|
||||||
|
.user = NULL,
|
||||||
|
.func = &g_vfs_stdio,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app->m_ftp_show_mounts.Get()) {
|
||||||
|
g_custom_vfs.push_back({
|
||||||
|
.name = "mounts",
|
||||||
|
.user = NULL,
|
||||||
|
.func = &g_vfs_stdio,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
g_ftpsrv_config.timeout = 0;
|
||||||
|
|
||||||
|
if (!g_ftpsrv_config.port) {
|
||||||
|
g_ftpsrv_config.port = 5000;
|
||||||
|
log_write("[FTP] no port config, defaulting to 5000\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep compat with older sphaira
|
||||||
|
if (!std::strlen(g_ftpsrv_config.user) && !std::strlen(g_ftpsrv_config.pass)) {
|
||||||
|
g_ftpsrv_config.anon = true;
|
||||||
|
log_write("[FTP] no user pass, defaulting to anon\n");
|
||||||
|
}
|
||||||
|
|
||||||
Result rc;
|
Result rc;
|
||||||
if (R_FAILED(rc = utils::CreateThread(&g_thread, loop, nullptr))) {
|
if (R_FAILED(rc = utils::CreateThread(&g_thread, loop, nullptr))) {
|
||||||
@@ -602,7 +617,9 @@ void Exit() {
|
|||||||
threadWaitForExit(&g_thread);
|
threadWaitForExit(&g_thread);
|
||||||
threadClose(&g_thread);
|
threadClose(&g_thread);
|
||||||
|
|
||||||
memset(&g_ftpsrv_config, 0, sizeof(g_ftpsrv_config));
|
std::memset(&g_ftpsrv_config, 0, sizeof(g_ftpsrv_config));
|
||||||
|
g_custom_vfs.clear();
|
||||||
|
g_ftpsrv_mount_flags = 0;
|
||||||
|
|
||||||
log_write("[FTP] exitied\n");
|
log_write("[FTP] exitied\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -669,12 +669,40 @@ bool Init() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add default mount of the sd card.
|
||||||
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsNativeSd>(), "", "microSD card"));
|
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsNativeSd>(), "", "microSD card"));
|
||||||
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsNativeImage>(FsImageDirectoryId_Sd), "Album", "Album (Image SD)"));
|
|
||||||
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsStdio>(true, "games:/"), "Games", "Games"));
|
if (App::GetApp()->m_mtp_show_album.Get()) {
|
||||||
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsStdio>(true, "mounts:/"), "Mounts", "Mounts"));
|
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsNativeImage>(FsImageDirectoryId_Sd), "Album", "Album (Image SD)"));
|
||||||
g_fs_entries.emplace_back(std::make_shared<FsDevNullProxy>("DevNull", "DevNull (Speed Test)"));
|
}
|
||||||
g_fs_entries.emplace_back(std::make_shared<FsInstallProxy>("install", "Install (NSP, XCI, NSZ, XCZ)"));
|
|
||||||
|
if (App::GetApp()->m_mtp_show_content_sd.Get()) {
|
||||||
|
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsNativeContentStorage>(FsContentStorageId_SdCard), "ContentsM", "Contents (microSD card)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (App::GetApp()->m_mtp_show_content_system.Get()) {
|
||||||
|
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsNativeContentStorage>(FsContentStorageId_System), "ContentsS", "Contents (System)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (App::GetApp()->m_mtp_show_content_user.Get()) {
|
||||||
|
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsNativeContentStorage>(FsContentStorageId_User), "ContentsU", "Contents (User)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (App::GetApp()->m_mtp_show_games.Get()) {
|
||||||
|
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsStdio>(true, "games:/"), "Games", "Games"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (App::GetApp()->m_mtp_show_install.Get()) {
|
||||||
|
g_fs_entries.emplace_back(std::make_shared<FsInstallProxy>("install", "Install (NSP, XCI, NSZ, XCZ)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (App::GetApp()->m_mtp_show_mounts.Get()) {
|
||||||
|
g_fs_entries.emplace_back(std::make_shared<FsProxy>(std::make_unique<fs::FsStdio>(true, "mounts:/"), "Mounts", "Mounts"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (App::GetApp()->m_mtp_show_speedtest.Get()) {
|
||||||
|
g_fs_entries.emplace_back(std::make_shared<FsDevNullProxy>("DevNull", "DevNull (Speed Test)"));
|
||||||
|
}
|
||||||
|
|
||||||
g_should_exit = false;
|
g_should_exit = false;
|
||||||
if (!haze::Initialize(haze_callback, g_fs_entries, App::GetApp()->m_mtp_vid.Get(), App::GetApp()->m_mtp_pid.Get())) {
|
if (!haze::Initialize(haze_callback, g_fs_entries, App::GetApp()->m_mtp_vid.Get(), App::GetApp()->m_mtp_pid.Get())) {
|
||||||
|
|||||||
@@ -337,15 +337,19 @@ MainMenu::MainMenu() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
options->Add<SidebarEntryBool>("FTP"_i18n, App::GetFtpEnable(), [](bool& enable){
|
options->Add<SidebarEntryCallback>("FTP"_i18n, [](){ App::DisplayFtpOptions(); },
|
||||||
App::SetFtpEnable(enable);
|
"Enable / modify the FTP server settings such as port, user/pass and the folders that are shown.\n\n"
|
||||||
}, "Enable FTP server to run in the background.\n\n"
|
"NOTE: Changing any of the options will automatically restart the FTP server when exiting the options menu."_i18n
|
||||||
"The default port is 5000 with no user/pass set. "
|
);
|
||||||
"You can change this behaviour in /config/ftpsrv/config.ini"_i18n);
|
|
||||||
|
|
||||||
options->Add<SidebarEntryBool>("MTP"_i18n, App::GetMtpEnable(), [](bool& enable){
|
options->Add<SidebarEntryCallback>("MTP"_i18n, [](){ App::DisplayMtpOptions(); },
|
||||||
App::SetMtpEnable(enable);
|
"Enable / modify the MTP responder settings such as the folders that are shown.\n\n"
|
||||||
}, "Enable MTP server to run in the background."_i18n);
|
"NOTE: Changing any of the options will automatically restart the MTP server when exiting the options menu."_i18n
|
||||||
|
);
|
||||||
|
|
||||||
|
options->Add<SidebarEntryCallback>("HDD"_i18n, [](){
|
||||||
|
App::DisplayHddOptions();
|
||||||
|
}, "Enable / modify the HDD mount options."_i18n);
|
||||||
|
|
||||||
options->Add<SidebarEntryBool>("NXlink"_i18n, App::GetNxlinkEnable(), [](bool& enable){
|
options->Add<SidebarEntryBool>("NXlink"_i18n, App::GetNxlinkEnable(), [](bool& enable){
|
||||||
App::SetNxlinkEnable(enable);
|
App::SetNxlinkEnable(enable);
|
||||||
@@ -353,14 +357,6 @@ MainMenu::MainMenu() {
|
|||||||
"NXlink is used to send .nro's from PC to the switch\n\n"
|
"NXlink is used to send .nro's from PC to the switch\n\n"
|
||||||
"If you are not a developer, you can disable this option."_i18n);
|
"If you are not a developer, you can disable this option."_i18n);
|
||||||
|
|
||||||
options->Add<SidebarEntryBool>("HDD"_i18n, App::GetHddEnable(), [](bool& enable){
|
|
||||||
App::SetHddEnable(enable);
|
|
||||||
}, "Enable mounting of connected USB/HDD devices. "
|
|
||||||
"Connected devices can be used in the FileBrowser, as well as a backup location when dumping games and saves."_i18n);
|
|
||||||
|
|
||||||
options->Add<SidebarEntryBool>("HDD write protect"_i18n, App::GetWriteProtect(), [](bool& enable){
|
|
||||||
App::SetWriteProtect(enable);
|
|
||||||
}, "Makes the connected HDD read-only."_i18n);
|
|
||||||
}, "Toggle FTP, MTP, HDD and NXlink\n\n"
|
}, "Toggle FTP, MTP, HDD and NXlink\n\n"
|
||||||
"If Sphaira has a update available, you can download it from this menu"_i18n);
|
"If Sphaira has a update available, you can download it from this menu"_i18n);
|
||||||
|
|
||||||
|
|||||||
@@ -124,24 +124,27 @@ SidebarEntryBool::SidebarEntryBool(const std::string& title, bool option, const
|
|||||||
} else {
|
} else {
|
||||||
m_option ^= 1;
|
m_option ^= 1;
|
||||||
m_callback(m_option);
|
m_callback(m_option);
|
||||||
|
SetDirty();
|
||||||
} }
|
} }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SidebarEntryBool::SidebarEntryBool(const std::string& title, bool& option, const std::string& info, const std::string& true_str, const std::string& false_str)
|
SidebarEntryBool::SidebarEntryBool(const std::string& title, bool& option, const std::string& info, const std::string& true_str, const std::string& false_str)
|
||||||
: SidebarEntryBool{title, option, Callback{}, info, true_str, false_str} {
|
: SidebarEntryBool{title, option, Callback{}, info, true_str, false_str} {
|
||||||
m_callback = [&option](bool&){
|
m_callback = [this, &option](bool&){
|
||||||
option ^= 1;
|
option ^= 1;
|
||||||
|
SetDirty();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
SidebarEntryBool::SidebarEntryBool(const std::string& title, option::OptionBool& option, const Callback& cb, const std::string& info, const std::string& true_str, const std::string& false_str)
|
SidebarEntryBool::SidebarEntryBool(const std::string& title, option::OptionBool& option, const Callback& cb, const std::string& info, const std::string& true_str, const std::string& false_str)
|
||||||
: SidebarEntryBool{title, option.Get(), Callback{}, info, true_str, false_str} {
|
: SidebarEntryBool{title, option.Get(), Callback{}, info, true_str, false_str} {
|
||||||
m_callback = [&option, cb](bool& v_out){
|
m_callback = [this, &option, cb](bool& v_out){
|
||||||
if (cb) {
|
if (cb) {
|
||||||
cb(v_out);
|
cb(v_out);
|
||||||
}
|
}
|
||||||
option.Set(v_out);
|
option.Set(v_out);
|
||||||
|
SetDirty();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,6 +169,7 @@ SidebarEntrySlider::SidebarEntrySlider(const std::string& title, float value, fl
|
|||||||
DependsClick();
|
DependsClick();
|
||||||
} else {
|
} else {
|
||||||
m_value = std::clamp(m_value - m_inc, m_min, m_max);
|
m_value = std::clamp(m_value - m_inc, m_min, m_max);
|
||||||
|
SetDirty();
|
||||||
// m_callback(m_option);
|
// m_callback(m_option);
|
||||||
} }
|
} }
|
||||||
});
|
});
|
||||||
@@ -174,6 +178,7 @@ SidebarEntrySlider::SidebarEntrySlider(const std::string& title, float value, fl
|
|||||||
DependsClick();
|
DependsClick();
|
||||||
} else {
|
} else {
|
||||||
m_value = std::clamp(m_value + m_inc, m_min, m_max);
|
m_value = std::clamp(m_value + m_inc, m_min, m_max);
|
||||||
|
SetDirty();
|
||||||
// m_callback(m_option);
|
// m_callback(m_option);
|
||||||
} }
|
} }
|
||||||
});
|
});
|
||||||
@@ -240,6 +245,8 @@ SidebarEntryArray::SidebarEntryArray(const std::string& title, const Items& item
|
|||||||
App::Push<PopupList>(
|
App::Push<PopupList>(
|
||||||
m_title, m_items, index, m_index
|
m_title, m_items, index, m_index
|
||||||
);
|
);
|
||||||
|
|
||||||
|
SetDirty();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,6 +282,7 @@ SidebarEntryArray::SidebarEntryArray(const std::string& title, const Items& item
|
|||||||
} else {
|
} else {
|
||||||
// m_callback(m_index);
|
// m_callback(m_index);
|
||||||
m_list_callback();
|
m_list_callback();
|
||||||
|
SetDirty();
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -291,6 +299,7 @@ SidebarEntryTextBase::SidebarEntryTextBase(const std::string& title, const std::
|
|||||||
SetAction(Button::A, Action{"OK"_i18n, [this](){
|
SetAction(Button::A, Action{"OK"_i18n, [this](){
|
||||||
if (m_callback) {
|
if (m_callback) {
|
||||||
m_callback();
|
m_callback();
|
||||||
|
SetDirty();
|
||||||
}
|
}
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
@@ -300,26 +309,35 @@ void SidebarEntryTextBase::Draw(NVGcontext* vg, Theme* theme, const Vec4& root_p
|
|||||||
SidebarEntryBase::DrawEntry(vg, theme, m_title, m_value, true);
|
SidebarEntryBase::DrawEntry(vg, theme, m_title, m_value, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SidebarEntryTextInput::SidebarEntryTextInput(const std::string& title, const std::string& value, const std::string& guide, s64 len_min, s64 len_max, const std::string& info)
|
SidebarEntryTextInput::SidebarEntryTextInput(const std::string& title, const std::string& value, const std::string& guide, s64 len_min, s64 len_max, const std::string& info, const Callback& callback)
|
||||||
: SidebarEntryTextBase{title, value, {}, info}
|
: SidebarEntryTextBase{title, value, {}, info}
|
||||||
, m_guide{guide}
|
, m_guide{guide}
|
||||||
, m_len_min{len_min}
|
, m_len_min{len_min}
|
||||||
, m_len_max{len_max} {
|
, m_len_max{len_max}
|
||||||
|
, m_callback{callback} {
|
||||||
|
|
||||||
SetCallback([this](){
|
SetCallback([this](){
|
||||||
std::string out;
|
std::string out;
|
||||||
if (R_SUCCEEDED(swkbd::ShowText(out, m_guide.c_str(), GetValue().c_str(), m_len_min, m_len_max))) {
|
if (R_SUCCEEDED(swkbd::ShowText(out, m_guide.c_str(), GetValue().c_str(), m_len_min, m_len_max))) {
|
||||||
SetValue(out);
|
SetValue(out);
|
||||||
|
|
||||||
|
if (m_callback) {
|
||||||
|
m_callback(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SidebarEntryTextInput::SidebarEntryTextInput(const std::string& title, s64 value, const std::string& guide, s64 len_min, s64 len_max, const std::string& info)
|
SidebarEntryTextInput::SidebarEntryTextInput(const std::string& title, s64 value, const std::string& guide, s64 len_min, s64 len_max, const std::string& info, const Callback& callback)
|
||||||
: SidebarEntryTextInput{title, std::to_string(value), guide, len_min, len_max, info} {
|
: SidebarEntryTextInput{title, std::to_string(value), guide, len_min, len_max, info, callback} {
|
||||||
SetCallback([this](){
|
SetCallback([this](){
|
||||||
s64 out = std::stoul(GetValue());
|
s64 out = std::stoul(GetValue());
|
||||||
if (R_SUCCEEDED(swkbd::ShowNumPad(out, m_guide.c_str(), GetValue().c_str(), m_len_min, m_len_max))) {
|
if (R_SUCCEEDED(swkbd::ShowNumPad(out, m_guide.c_str(), GetValue().c_str(), m_len_min, m_len_max))) {
|
||||||
SetValue(std::to_string(out));
|
SetValue(std::to_string(out));
|
||||||
|
|
||||||
|
if (m_callback) {
|
||||||
|
m_callback(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -331,6 +349,7 @@ SidebarEntryFilePicker::SidebarEntryFilePicker(const std::string& title, const s
|
|||||||
App::Push<menu::filebrowser::picker::Menu>(
|
App::Push<menu::filebrowser::picker::Menu>(
|
||||||
[this](const fs::FsPath& path) {
|
[this](const fs::FsPath& path) {
|
||||||
SetValue(path);
|
SetValue(path);
|
||||||
|
SetDirty();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
m_filter
|
m_filter
|
||||||
@@ -370,6 +389,17 @@ Sidebar::Sidebar(const std::string& title, const std::string& sub, Side side, fl
|
|||||||
m_list->SetScrollBarPos(GetX() + GetW() - 20, m_base_pos.y - 10, pos.h - m_base_pos.y + 48);
|
m_list->SetScrollBarPos(GetX() + GetW() - 20, m_base_pos.y - 10, pos.h - m_base_pos.y + 48);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sidebar::~Sidebar() {
|
||||||
|
if (m_on_exit_when_changed) {
|
||||||
|
for (const auto& item : m_items) {
|
||||||
|
if (item->IsDirty()) {
|
||||||
|
m_on_exit_when_changed();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto Sidebar::Update(Controller* controller, TouchInfo* touch) -> void {
|
auto Sidebar::Update(Controller* controller, TouchInfo* touch) -> void {
|
||||||
Widget::Update(controller, touch);
|
Widget::Update(controller, touch);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user