From 37890f157dce31870e4b186c8e0449ecb9bd2a5e Mon Sep 17 00:00:00 2001 From: ITotalJustice <47043333+ITotalJustice@users.noreply.github.com> Date: Wed, 25 Dec 2024 22:17:21 +0000 Subject: [PATCH] add mtp (haze) ftp (ftpsrv), update RA file assoc, nxlink now polls for connection. --- assets/romfs/assoc/2048_libretro_libnx.ini | 4 - .../assoc/DoubleCherryGB_libretro_libnx.ini | 4 + assets/romfs/assoc/ardens_libretro_libnx.ini | 4 + assets/romfs/assoc/arduous_libretro_libnx.ini | 2 +- .../romfs/assoc/atari800_libretro_libnx.ini | 2 +- assets/romfs/assoc/bluemsx_libretro_libnx.ini | 4 +- assets/romfs/assoc/citra_libretro_libnx.ini | 4 - .../assoc/dosbox_pure_libretro_libnx.ini | 4 + .../romfs/assoc/dosbox_svn_libretro_libnx.ini | 2 +- .../assoc/fbalpha2012_cps1_libretro_libnx.ini | 3 - .../assoc/fbalpha2012_cps2_libretro_libnx.ini | 3 - .../assoc/fbalpha2012_libretro_libnx.ini | 3 - .../fbalpha2012_neogeo_libretro_libnx.ini | 3 - assets/romfs/assoc/frodo_libretro_libnx.ini | 2 +- assets/romfs/assoc/fuse_libretro_libnx.ini | 2 +- assets/romfs/assoc/gme_libretro_libnx.ini | 3 - assets/romfs/assoc/gong_libretro_libnx.ini | 3 - assets/romfs/assoc/gpsp_libretro_libnx.ini | 4 + assets/romfs/assoc/handy_libretro_libnx.ini | 2 +- .../romfs/assoc/mame2000_libretro_libnx.ini | 2 +- .../assoc/mednafen_lynx_libretro_libnx.ini | 2 +- .../romfs/assoc/minivmac_libretro_libnx.ini | 3 - assets/romfs/assoc/mrboom_libretro_libnx.ini | 4 + assets/romfs/assoc/mu_libretro_libnx.ini | 3 - assets/romfs/assoc/numero_libretro_libnx.ini | 3 - .../assoc/pcsx_rearmed_libretro_libnx.ini | 2 +- .../romfs/assoc/pocketcdg_libretro_libnx.ini | 3 - assets/romfs/assoc/ppsspp_libretro_libnx.ini | 2 +- assets/romfs/assoc/px68k_libretro_libnx.ini | 2 +- assets/romfs/assoc/quasi88_libretro_libnx.ini | 1 + assets/romfs/assoc/retro8_libretro_libnx.ini | 2 +- ...ibnx.ini => stella2023_libretro_libnx.ini} | 2 +- .../assoc/superbroswar_libretro_libnx.ini | 3 - .../romfs/assoc/vircon32_libretro_libnx.ini | 4 + assets/romfs/assoc/x1_libretro_libnx.ini | 2 +- sphaira/CMakeLists.txt | 47 +++ sphaira/include/app.hpp | 9 +- sphaira/include/defines.hpp | 62 ++-- sphaira/include/evman.hpp | 2 + sphaira/include/ftpsrv_helper.hpp | 33 ++ sphaira/include/ui/menus/filebrowser.hpp | 7 +- sphaira/source/app.cpp | 81 +++++ sphaira/source/download.cpp | 2 +- sphaira/source/fs.cpp | 6 +- sphaira/source/ftpsrv_helper.cpp | 335 ++++++++++++++++++ sphaira/source/main.cpp | 3 + sphaira/source/nxlink.cpp | 90 ++--- sphaira/source/ui/menus/appstore.cpp | 2 +- sphaira/source/ui/menus/filebrowser.cpp | 237 ++++++------- sphaira/source/ui/menus/main_menu.cpp | 12 +- sphaira/source/ui/menus/themezer.cpp | 2 +- sphaira/source/ui/notification.cpp | 3 + 52 files changed, 742 insertions(+), 289 deletions(-) delete mode 100644 assets/romfs/assoc/2048_libretro_libnx.ini create mode 100644 assets/romfs/assoc/DoubleCherryGB_libretro_libnx.ini create mode 100644 assets/romfs/assoc/ardens_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/citra_libretro_libnx.ini create mode 100644 assets/romfs/assoc/dosbox_pure_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/fbalpha2012_cps1_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/fbalpha2012_cps2_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/fbalpha2012_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/fbalpha2012_neogeo_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/gme_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/gong_libretro_libnx.ini create mode 100644 assets/romfs/assoc/gpsp_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/minivmac_libretro_libnx.ini create mode 100644 assets/romfs/assoc/mrboom_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/mu_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/numero_libretro_libnx.ini delete mode 100644 assets/romfs/assoc/pocketcdg_libretro_libnx.ini rename assets/romfs/assoc/{stella_libretro_libnx.ini => stella2023_libretro_libnx.ini} (53%) delete mode 100644 assets/romfs/assoc/superbroswar_libretro_libnx.ini create mode 100644 assets/romfs/assoc/vircon32_libretro_libnx.ini create mode 100644 sphaira/include/ftpsrv_helper.hpp create mode 100644 sphaira/source/ftpsrv_helper.cpp diff --git a/assets/romfs/assoc/2048_libretro_libnx.ini b/assets/romfs/assoc/2048_libretro_libnx.ini deleted file mode 100644 index 8900e63..0000000 --- a/assets/romfs/assoc/2048_libretro_libnx.ini +++ /dev/null @@ -1,4 +0,0 @@ -[config] -path=/retroarch/cores/2048_libretro_libnx.nro -supported_extensions= -database=2048 diff --git a/assets/romfs/assoc/DoubleCherryGB_libretro_libnx.ini b/assets/romfs/assoc/DoubleCherryGB_libretro_libnx.ini new file mode 100644 index 0000000..7efa37a --- /dev/null +++ b/assets/romfs/assoc/DoubleCherryGB_libretro_libnx.ini @@ -0,0 +1,4 @@ +[config] +path=/retroarch/cores/DoubleCherryGB_libretro_libnx.nro +supported_extensions=cgb|dmg|gb|gbc|sgb +database=Nintendo - Game Boy|Nintendo - Game Boy Color diff --git a/assets/romfs/assoc/ardens_libretro_libnx.ini b/assets/romfs/assoc/ardens_libretro_libnx.ini new file mode 100644 index 0000000..fec2702 --- /dev/null +++ b/assets/romfs/assoc/ardens_libretro_libnx.ini @@ -0,0 +1,4 @@ +[config] +path=/retroarch/cores/ardens_libretro_libnx.nro +supported_extensions=hex|arduboy +database=Arduboy Inc - Arduboy diff --git a/assets/romfs/assoc/arduous_libretro_libnx.ini b/assets/romfs/assoc/arduous_libretro_libnx.ini index 7c81ce7..981a1b0 100644 --- a/assets/romfs/assoc/arduous_libretro_libnx.ini +++ b/assets/romfs/assoc/arduous_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/arduous_libretro_libnx.nro supported_extensions=hex -database=Arduboy +database=Arduboy Inc - Arduboy diff --git a/assets/romfs/assoc/atari800_libretro_libnx.ini b/assets/romfs/assoc/atari800_libretro_libnx.ini index ad36238..c722b81 100644 --- a/assets/romfs/assoc/atari800_libretro_libnx.ini +++ b/assets/romfs/assoc/atari800_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/atari800_libretro_libnx.nro -supported_extensions=xfd|atr|cdm|cas|bin|a52|zip|atx|car|rom|com|xex +supported_extensions=xfd|atr|dcm|cas|bin|a52|zip|atx|car|rom|com|xex|m3u database=Atari - 5200|Atari - 8-bit diff --git a/assets/romfs/assoc/bluemsx_libretro_libnx.ini b/assets/romfs/assoc/bluemsx_libretro_libnx.ini index 1b040a7..079eec5 100644 --- a/assets/romfs/assoc/bluemsx_libretro_libnx.ini +++ b/assets/romfs/assoc/bluemsx_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/bluemsx_libretro_libnx.nro -supported_extensions=rom|ri|mx1|mx2|col|dsk|cas|sg|sc|sf|m3u -database=Microsoft - MSX|Microsoft - MSX2|Coleco - ColecoVision|Sega - SG-1000 +supported_extensions=rom|ri|mx1|mx2|dsk|col|sg|sc|sf|cas|m3u +database=Microsoft - MSX|Microsoft - MSX2|Coleco - ColecoVision|Sega - SG-1000|Spectravideo - SVI-318 - SVI-328 diff --git a/assets/romfs/assoc/citra_libretro_libnx.ini b/assets/romfs/assoc/citra_libretro_libnx.ini deleted file mode 100644 index 8597ddd..0000000 --- a/assets/romfs/assoc/citra_libretro_libnx.ini +++ /dev/null @@ -1,4 +0,0 @@ -[config] -path=/retroarch/cores/citra_libretro_libnx.nro -supported_extensions=3ds|3dsx|elf|axf|cci|cxi|app -database=Nintendo - Nintendo 3DS diff --git a/assets/romfs/assoc/dosbox_pure_libretro_libnx.ini b/assets/romfs/assoc/dosbox_pure_libretro_libnx.ini new file mode 100644 index 0000000..cdcfe73 --- /dev/null +++ b/assets/romfs/assoc/dosbox_pure_libretro_libnx.ini @@ -0,0 +1,4 @@ +[config] +path=/retroarch/cores/dosbox_pure_libretro_libnx.nro +supported_extensions=zip|dosz|exe|com|bat|iso|chd|cue|ins|img|ima|vhd|jrc|tc|m3u|m3u8|conf|/ +database=DOS diff --git a/assets/romfs/assoc/dosbox_svn_libretro_libnx.ini b/assets/romfs/assoc/dosbox_svn_libretro_libnx.ini index 6801fa2..f075a6e 100644 --- a/assets/romfs/assoc/dosbox_svn_libretro_libnx.ini +++ b/assets/romfs/assoc/dosbox_svn_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/dosbox_svn_libretro_libnx.nro -supported_extensions=exe|com|bat|conf|cue|iso +supported_extensions=exe|com|bat|conf|cue|iso|img|/ database=DOS diff --git a/assets/romfs/assoc/fbalpha2012_cps1_libretro_libnx.ini b/assets/romfs/assoc/fbalpha2012_cps1_libretro_libnx.ini deleted file mode 100644 index 9a34990..0000000 --- a/assets/romfs/assoc/fbalpha2012_cps1_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/fbalpha2012_cps1_libretro_libnx.nro -supported_extensions=zip diff --git a/assets/romfs/assoc/fbalpha2012_cps2_libretro_libnx.ini b/assets/romfs/assoc/fbalpha2012_cps2_libretro_libnx.ini deleted file mode 100644 index 767310d..0000000 --- a/assets/romfs/assoc/fbalpha2012_cps2_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/fbalpha2012_cps2_libretro_libnx.nro -supported_extensions=zip diff --git a/assets/romfs/assoc/fbalpha2012_libretro_libnx.ini b/assets/romfs/assoc/fbalpha2012_libretro_libnx.ini deleted file mode 100644 index 7ab3910..0000000 --- a/assets/romfs/assoc/fbalpha2012_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/fbalpha2012_libretro_libnx.nro -supported_extensions=iso|zip|7z diff --git a/assets/romfs/assoc/fbalpha2012_neogeo_libretro_libnx.ini b/assets/romfs/assoc/fbalpha2012_neogeo_libretro_libnx.ini deleted file mode 100644 index b56c759..0000000 --- a/assets/romfs/assoc/fbalpha2012_neogeo_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/fbalpha2012_neogeo_libretro_libnx.nro -supported_extensions=zip diff --git a/assets/romfs/assoc/frodo_libretro_libnx.ini b/assets/romfs/assoc/frodo_libretro_libnx.ini index 73b574e..d949664 100644 --- a/assets/romfs/assoc/frodo_libretro_libnx.ini +++ b/assets/romfs/assoc/frodo_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/frodo_libretro_libnx.nro -supported_extensions=d64|t64|x64|p00|lnx|zip +supported_extensions=d64|t64|x64|p00|lnx|lyx|zip database=Commodore - 64 diff --git a/assets/romfs/assoc/fuse_libretro_libnx.ini b/assets/romfs/assoc/fuse_libretro_libnx.ini index 778fd29..874e3fe 100644 --- a/assets/romfs/assoc/fuse_libretro_libnx.ini +++ b/assets/romfs/assoc/fuse_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/fuse_libretro_libnx.nro -supported_extensions=tzx|tap|z80|rzx|scl|trd|dsk|zip +supported_extensions=tzx|tap|z80|rzx|scl|trd|dsk|dck|sna|szx|zip database=Sinclair - ZX Spectrum +3|Sinclair - ZX Spectrum diff --git a/assets/romfs/assoc/gme_libretro_libnx.ini b/assets/romfs/assoc/gme_libretro_libnx.ini deleted file mode 100644 index 980c3f2..0000000 --- a/assets/romfs/assoc/gme_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/gme_libretro_libnx.nro -supported_extensions=ay|gbs|gym|hes|kss|nsf|nsfe|sap|spc|vgm|vgz|zip diff --git a/assets/romfs/assoc/gong_libretro_libnx.ini b/assets/romfs/assoc/gong_libretro_libnx.ini deleted file mode 100644 index e767c0c..0000000 --- a/assets/romfs/assoc/gong_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/gong_libretro_libnx.nro -supported_extensions= diff --git a/assets/romfs/assoc/gpsp_libretro_libnx.ini b/assets/romfs/assoc/gpsp_libretro_libnx.ini new file mode 100644 index 0000000..881124b --- /dev/null +++ b/assets/romfs/assoc/gpsp_libretro_libnx.ini @@ -0,0 +1,4 @@ +[config] +path=/retroarch/cores/gpsp_libretro_libnx.nro +supported_extensions=gba|bin +database=Nintendo - Game Boy Advance diff --git a/assets/romfs/assoc/handy_libretro_libnx.ini b/assets/romfs/assoc/handy_libretro_libnx.ini index af97bd8..a221626 100644 --- a/assets/romfs/assoc/handy_libretro_libnx.ini +++ b/assets/romfs/assoc/handy_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/handy_libretro_libnx.nro -supported_extensions=lnx|o +supported_extensions=lnx|lyx|o database=Atari - Lynx diff --git a/assets/romfs/assoc/mame2000_libretro_libnx.ini b/assets/romfs/assoc/mame2000_libretro_libnx.ini index 3de6c5d..a58ec96 100644 --- a/assets/romfs/assoc/mame2000_libretro_libnx.ini +++ b/assets/romfs/assoc/mame2000_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/mame2000_libretro_libnx.nro -supported_extensions=zip|7z|chd +supported_extensions=zip|7z database=MAME 2000 diff --git a/assets/romfs/assoc/mednafen_lynx_libretro_libnx.ini b/assets/romfs/assoc/mednafen_lynx_libretro_libnx.ini index 8dd9abc..67d999d 100644 --- a/assets/romfs/assoc/mednafen_lynx_libretro_libnx.ini +++ b/assets/romfs/assoc/mednafen_lynx_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/mednafen_lynx_libretro_libnx.nro -supported_extensions=lnx|o +supported_extensions=lnx|lyx|o database=Atari - Lynx diff --git a/assets/romfs/assoc/minivmac_libretro_libnx.ini b/assets/romfs/assoc/minivmac_libretro_libnx.ini deleted file mode 100644 index f047720..0000000 --- a/assets/romfs/assoc/minivmac_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/minivmac_libretro_libnx.nro -supported_extensions=dsk|img|zip|hvf|cmd diff --git a/assets/romfs/assoc/mrboom_libretro_libnx.ini b/assets/romfs/assoc/mrboom_libretro_libnx.ini new file mode 100644 index 0000000..66b7c1c --- /dev/null +++ b/assets/romfs/assoc/mrboom_libretro_libnx.ini @@ -0,0 +1,4 @@ +[config] +path=/retroarch/cores/mrboom_libretro_libnx.nro +supported_extensions=desktop +database=MrBoom diff --git a/assets/romfs/assoc/mu_libretro_libnx.ini b/assets/romfs/assoc/mu_libretro_libnx.ini deleted file mode 100644 index 61ee02b..0000000 --- a/assets/romfs/assoc/mu_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/mu_libretro_libnx.nro -supported_extensions=prc|pqa|img|pdb|zip diff --git a/assets/romfs/assoc/numero_libretro_libnx.ini b/assets/romfs/assoc/numero_libretro_libnx.ini deleted file mode 100644 index 8dc5772..0000000 --- a/assets/romfs/assoc/numero_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/numero_libretro_libnx.nro -supported_extensions=8xp|8xk|8xg diff --git a/assets/romfs/assoc/pcsx_rearmed_libretro_libnx.ini b/assets/romfs/assoc/pcsx_rearmed_libretro_libnx.ini index 9861603..3c8f2e4 100644 --- a/assets/romfs/assoc/pcsx_rearmed_libretro_libnx.ini +++ b/assets/romfs/assoc/pcsx_rearmed_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/pcsx_rearmed_libretro_libnx.nro -supported_extensions=bin|cue|img|mdf|pbp|toc|cbn|m3u|ccd|chd +supported_extensions=bin|cue|img|mdf|pbp|toc|cbn|m3u|ccd|chd|iso|exe database=Sony - PlayStation diff --git a/assets/romfs/assoc/pocketcdg_libretro_libnx.ini b/assets/romfs/assoc/pocketcdg_libretro_libnx.ini deleted file mode 100644 index a2a4144..0000000 --- a/assets/romfs/assoc/pocketcdg_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/pocketcdg_libretro_libnx.nro -supported_extensions=cdg diff --git a/assets/romfs/assoc/ppsspp_libretro_libnx.ini b/assets/romfs/assoc/ppsspp_libretro_libnx.ini index 90538e2..fcad92f 100644 --- a/assets/romfs/assoc/ppsspp_libretro_libnx.ini +++ b/assets/romfs/assoc/ppsspp_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/ppsspp_libretro_libnx.nro -supported_extensions=elf|iso|cso|prx|pbp +supported_extensions=elf|iso|cso|prx|pbp|chd database=Sony - PlayStation Portable diff --git a/assets/romfs/assoc/px68k_libretro_libnx.ini b/assets/romfs/assoc/px68k_libretro_libnx.ini index a83b5d7..de1c4b3 100644 --- a/assets/romfs/assoc/px68k_libretro_libnx.ini +++ b/assets/romfs/assoc/px68k_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/px68k_libretro_libnx.nro -supported_extensions=dim|zip|img|d88|88d|hdm|dup|2hd|xdf|hdf|cmd|m3u +supported_extensions=dim|img|d88|88d|hdm|dup|2hd|xdf|hdf|cmd|m3u database=Sharp - X68000 diff --git a/assets/romfs/assoc/quasi88_libretro_libnx.ini b/assets/romfs/assoc/quasi88_libretro_libnx.ini index 2323e69..d2bcae4 100644 --- a/assets/romfs/assoc/quasi88_libretro_libnx.ini +++ b/assets/romfs/assoc/quasi88_libretro_libnx.ini @@ -1,3 +1,4 @@ [config] path=/retroarch/cores/quasi88_libretro_libnx.nro supported_extensions=d88|u88|m3u +database=NEC - PC-8001 - PC-8801 diff --git a/assets/romfs/assoc/retro8_libretro_libnx.ini b/assets/romfs/assoc/retro8_libretro_libnx.ini index cd834b7..f47c185 100644 --- a/assets/romfs/assoc/retro8_libretro_libnx.ini +++ b/assets/romfs/assoc/retro8_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/retro8_libretro_libnx.nro supported_extensions=p8|png -database=PICO8 +database=PICO-8 diff --git a/assets/romfs/assoc/stella_libretro_libnx.ini b/assets/romfs/assoc/stella2023_libretro_libnx.ini similarity index 53% rename from assets/romfs/assoc/stella_libretro_libnx.ini rename to assets/romfs/assoc/stella2023_libretro_libnx.ini index 79602a1..2ceb89d 100644 --- a/assets/romfs/assoc/stella_libretro_libnx.ini +++ b/assets/romfs/assoc/stella2023_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] -path=/retroarch/cores/stella_libretro_libnx.nro +path=/retroarch/cores/stella2023_libretro_libnx.nro supported_extensions=a26|bin database=Atari - 2600 diff --git a/assets/romfs/assoc/superbroswar_libretro_libnx.ini b/assets/romfs/assoc/superbroswar_libretro_libnx.ini deleted file mode 100644 index 143236e..0000000 --- a/assets/romfs/assoc/superbroswar_libretro_libnx.ini +++ /dev/null @@ -1,3 +0,0 @@ -[config] -path=/retroarch/cores/superbroswar_libretro_libnx.nro -supported_extensions=game diff --git a/assets/romfs/assoc/vircon32_libretro_libnx.ini b/assets/romfs/assoc/vircon32_libretro_libnx.ini new file mode 100644 index 0000000..53cbd4e --- /dev/null +++ b/assets/romfs/assoc/vircon32_libretro_libnx.ini @@ -0,0 +1,4 @@ +[config] +path=/retroarch/cores/vircon32_libretro_libnx.nro +supported_extensions=v32|V32 +database=Vircon32 diff --git a/assets/romfs/assoc/x1_libretro_libnx.ini b/assets/romfs/assoc/x1_libretro_libnx.ini index 4e2d015..df45e0b 100644 --- a/assets/romfs/assoc/x1_libretro_libnx.ini +++ b/assets/romfs/assoc/x1_libretro_libnx.ini @@ -1,4 +1,4 @@ [config] path=/retroarch/cores/x1_libretro_libnx.nro supported_extensions=dx1|zip|2d|2hd|tfd|d88|88d|hdm|xdf|dup|tap|cmd -database=Sharp X1 +database=Sharp - X1 diff --git a/sphaira/CMakeLists.txt b/sphaira/CMakeLists.txt index b10566b..6d78e42 100644 --- a/sphaira/CMakeLists.txt +++ b/sphaira/CMakeLists.txt @@ -72,6 +72,7 @@ add_executable(sphaira source/swkbd.cpp source/web.cpp source/i18n.cpp + source/ftpsrv_helper.cpp ) target_compile_definitions(sphaira PRIVATE @@ -82,6 +83,16 @@ target_compile_definitions(sphaira PRIVATE include(FetchContent) set(FETCHCONTENT_QUIET FALSE) +FetchContent_Declare(ftpsrv + GIT_REPOSITORY https://github.com/ITotalJustice/ftpsrv.git + GIT_TAG 8d5a14e +) + +FetchContent_Declare(libhaze + GIT_REPOSITORY https://github.com/ITotalJustice/libhaze.git + GIT_TAG 3244b9e +) + FetchContent_Declare(libpulsar GIT_REPOSITORY https://github.com/ITotalJustice/switch-libpulsar.git GIT_TAG d729be3 @@ -135,7 +146,16 @@ set(YYJSON_DISABLE_NON_STANDARD ON) set(YYJSON_DISABLE_UTF8_VALIDATION ON) set(YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS OFF) +set(FTPSRV_LIB_BUILD TRUE) +set(FTPSRV_LIB_SOCK_UNISTD TRUE) +set(FTPSRV_LIB_VFS_CUSTOM ${CMAKE_CURRENT_SOURCE_DIR}/include/ftpsrv_helper.hpp) +set(FTPSRV_LIB_PATH_SIZE 0x301) +set(FTPSRV_LIB_SESSIONS 32) +set(FTPSRV_LIB_BUF_SIZE 1024*64) + FetchContent_MakeAvailable( + ftpsrv + libhaze libpulsar nanovg stb @@ -143,6 +163,31 @@ FetchContent_MakeAvailable( yyjson ) +# todo: upstream cmake +add_library(libhaze + ${libhaze_SOURCE_DIR}/source/async_usb_server.cpp + ${libhaze_SOURCE_DIR}/source/device_properties.cpp + ${libhaze_SOURCE_DIR}/source/event_reactor.cpp + ${libhaze_SOURCE_DIR}/source/haze.cpp + ${libhaze_SOURCE_DIR}/source/ptp_object_database.cpp + ${libhaze_SOURCE_DIR}/source/ptp_object_heap.cpp + ${libhaze_SOURCE_DIR}/source/ptp_responder_android_operations.cpp + ${libhaze_SOURCE_DIR}/source/ptp_responder_mtp_operations.cpp + ${libhaze_SOURCE_DIR}/source/ptp_responder_ptp_operations.cpp + ${libhaze_SOURCE_DIR}/source/ptp_responder.cpp + ${libhaze_SOURCE_DIR}/source/usb_session.cpp +) +target_include_directories(libhaze PUBLIC ${libhaze_SOURCE_DIR}/include) +set_target_properties(libhaze PROPERTIES + C_STANDARD 11 + C_EXTENSIONS ON + CXX_STANDARD 20 + CXX_EXTENSIONS ON + # force optimisations in debug mode as otherwise vapor errors + # due to force_inline attribute failing... + COMPILE_OPTIONS "$<$:-Os>" +) + # todo: upstream cmake add_library(libpulsar ${libpulsar_SOURCE_DIR}/src/archive/archive_file.c @@ -195,6 +240,8 @@ set_target_properties(sphaira PROPERTIES ) target_link_libraries(sphaira PRIVATE + ftpsrv + libhaze libpulsar minIni-sphaira nanovg diff --git a/sphaira/include/app.hpp b/sphaira/include/app.hpp index 4566021..690a945 100644 --- a/sphaira/include/app.hpp +++ b/sphaira/include/app.hpp @@ -46,11 +46,12 @@ public: static auto GetVg() -> NVGcontext*; static void Push(std::shared_ptr); - // this is thread safe (todo: make it thread safe) + // this is thread safe static void Notify(std::string text, ui::NotifEntry::Side side = ui::NotifEntry::Side::RIGHT); static void Notify(ui::NotifEntry entry); static void NotifyPop(ui::NotifEntry::Side side = ui::NotifEntry::Side::RIGHT); static void NotifyClear(ui::NotifEntry::Side side = ui::NotifEntry::Side::RIGHT); + static void NotifyFlashLed(); static auto GetThemeMetaList() -> std::span; static void SetTheme(u64 theme_index); @@ -61,6 +62,8 @@ public: // returns true if we are hbmenu. static auto IsHbmenu() -> bool; + static auto GetMtpEnable() -> bool; + static auto GetFtpEnable() -> bool; static auto GetNxlinkEnable() -> bool; static auto GetLogEnable() -> bool; static auto GetReplaceHbmenuEnable() -> bool; @@ -71,6 +74,8 @@ public: static auto GetThemeMusicEnable() -> bool; static auto GetLanguage() -> long; + static void SetMtpEnable(bool enable); + static void SetFtpEnable(bool enable); static void SetNxlinkEnable(bool enable); static void SetLogEnable(bool enable); static void SetReplaceHbmenuEnable(bool enable); @@ -138,6 +143,8 @@ public: bool m_quit{}; option::OptionBool m_nxlink_enabled{INI_SECTION, "nxlink_enabled", true}; + option::OptionBool m_mtp_enabled{INI_SECTION, "mtp_enabled", false}; + option::OptionBool m_ftp_enabled{INI_SECTION, "ftp_enabled", false}; option::OptionBool m_log_enabled{INI_SECTION, "log_enabled", false}; option::OptionBool m_replace_hbmenu{INI_SECTION, "replace_hbmenu", false}; option::OptionBool m_install{INI_SECTION, "install", false}; diff --git a/sphaira/include/defines.hpp b/sphaira/include/defines.hpp index c9da42c..b1c9bd5 100644 --- a/sphaira/include/defines.hpp +++ b/sphaira/include/defines.hpp @@ -224,18 +224,18 @@ enum SvcError { }; enum FsError { - FsError_ResultPathNotFound = 0x202, - FsError_ResultPathAlreadyExists = 0x402, - FsError_ResultTargetLocked = 0xE02, + FsError_PathNotFound = 0x202, + FsError_PathAlreadyExists = 0x402, + FsError_TargetLocked = 0xE02, FsError_UsableSpaceNotEnoughMmcCalibration = 0x4602, FsError_UsableSpaceNotEnoughMmcSafe = 0x4802, FsError_UsableSpaceNotEnoughMmcUser = 0x4A02, FsError_UsableSpaceNotEnoughMmcSystem = 0x4C02, - FsError_ResultUsableSpaceNotEnoughSdCard = 0x4E02, - FsError_ResultUnsupportedSdkVersion = 0x6402, - FsError_ResultMountNameAlreadyExists = 0x7802, - FsError_ResultPartitionNotFound = 0x7D202, - FsError_ResultTargetNotFound = 0x7D402, + FsError_UsableSpaceNotEnoughSdCard = 0x4E02, + FsError_UnsupportedSdkVersion = 0x6402, + FsError_MountNameAlreadyExists = 0x7802, + FsError_PartitionNotFound = 0x7D202, + FsError_TargetNotFound = 0x7D402, FsError_PortSdCardNoDevice = 0xFA202, FsError_GameCardCardNotInserted = 0x13B002, FsError_GameCardCardNotActivated = 0x13B402, @@ -286,9 +286,9 @@ enum FsError { FsError_GameCardFsCheckHandleInGetStatusFailure = 0x171402, FsError_GameCardFsCheckHandleInCreateReadOnlyFailure = 0x172002, FsError_GameCardFsCheckHandleInCreateSecureReadOnlyFailure = 0x172202, - FsError_ResultNotImplemented = 0x177202, - FsError_ResultAlreadyExists = 0x177602, - FsError_ResultOutOfRange = 0x177A02, + FsError_NotImplemented = 0x177202, + FsError_AlreadyExists = 0x177602, + FsError_OutOfRange = 0x177A02, FsError_AllocationMemoryFailedInFatFileSystemA = 0x190202, FsError_AllocationMemoryFailedInFatFileSystemB = 0x190402, FsError_AllocationMemoryFailedInFatFileSystemC = 0x190602, @@ -348,18 +348,18 @@ enum FsError { FsError_FatFsFormatIllegalSectorsC = 0x280C02, FsError_FatFsFormatIllegalSectorsD = 0x280E02, FsError_UnexpectedInMountTableA = 0x296A02, - FsError_ResultTooLongPath = 0x2EE602, - FsError_ResultInvalidCharacter = 0x2EE802, - FsError_ResultInvalidPathFormat = 0x2EEA02, - FsError_ResultDirectoryUnobtainable = 0x2EEC02, - FsError_ResultInvalidOffset = 0x2F5A02, - FsError_ResultInvalidSize = 0x2F5C02, - FsError_ResultNullptrArgument = 0x2F5E02, - FsError_ResultInvalidAlignment = 0x2F6002, - FsError_ResultInvalidMountName = 0x2F6202, - FsError_ResultExtensionSizeTooLarge = 0x2F6402, - FsError_ResultExtensionSizeInvalid = 0x2F6602, - FsError_ResultFileExtensionWithoutOpenModeAllowAppend = 0x307202, + FsError_TooLongPath = 0x2EE602, + FsError_InvalidCharacter = 0x2EE802, + FsError_InvalidPathFormat = 0x2EEA02, + FsError_DirectoryUnobtainable = 0x2EEC02, + FsError_InvalidOffset = 0x2F5A02, + FsError_InvalidSize = 0x2F5C02, + FsError_NullptrArgument = 0x2F5E02, + FsError_InvalidAlignment = 0x2F6002, + FsError_InvalidMountName = 0x2F6202, + FsError_ExtensionSizeTooLarge = 0x2F6402, + FsError_ExtensionSizeInvalid = 0x2F6602, + FsError_FileExtensionWithoutOpenModeAllowAppend = 0x307202, FsError_UnsupportedCommitTarget = 0x313A02, FsError_UnsupportedSetSizeForNotResizableSubStorage = 0x313C02, FsError_UnsupportedSetSizeForResizableSubStorage = 0x313E02, @@ -444,14 +444,14 @@ enum FsError { FsError_UnsupportedCommitProvisionallyForDirectorySaveDataFileSystem = 0x31E002, FsError_UnsupportedWriteForZeroBitmapHashStorageFile = 0x31E202, FsError_UnsupportedSetSizeForZeroBitmapHashStorageFile = 0x31E402, - FsError_ResultNcaExternalKeyUnregisteredDeprecated = 0x326602, - FsError_ResultFileNotClosed = 0x326E02, - FsError_ResultDirectoryNotClosed = 0x327002, - FsError_ResultWriteModeFileNotClosed = 0x327202, - FsError_ResultAllocatorAlreadyRegistered = 0x327402, - FsError_ResultDefaultAllocatorAlreadyUsed = 0x327602, - FsError_ResultAllocatorAlignmentViolation = 0x327A02, - FsError_ResultUserNotExist = 0x328202, + FsError_NcaExternalKeyUnregisteredDeprecated = 0x326602, + FsError_FileNotClosed = 0x326E02, + FsError_DirectoryNotClosed = 0x327002, + FsError_WriteModeFileNotClosed = 0x327202, + FsError_AllocatorAlreadyRegistered = 0x327402, + FsError_DefaultAllocatorAlreadyUsed = 0x327602, + FsError_AllocatorAlignmentViolation = 0x327A02, + FsError_UserNotExist = 0x328202, FsError_FileNotFound = 0x339402, FsError_DirectoryNotFound = 0x339602, FsError_MappingTableFull = 0x346402, diff --git a/sphaira/include/evman.hpp b/sphaira/include/evman.hpp index cba3385..43c4d7b 100644 --- a/sphaira/include/evman.hpp +++ b/sphaira/include/evman.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "download.hpp" namespace sphaira::evman { @@ -23,6 +24,7 @@ struct ExitEventData { using EventData = std::variant< LaunchNroEventData, ExitEventData, + HazeCallbackData, NxlinkCallbackData, DownloadEventData >; diff --git a/sphaira/include/ftpsrv_helper.hpp b/sphaira/include/ftpsrv_helper.hpp new file mode 100644 index 0000000..77871ae --- /dev/null +++ b/sphaira/include/ftpsrv_helper.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +struct FtpVfsFile { + FsFile fd; + s64 off; + s64 buf_off; + s64 buf_size; + bool is_write; + bool is_valid; + u8 buf[1024 * 1024 * 1]; +}; + +struct FtpVfsDir { + FsDir dir; + bool is_valid; +}; + +struct FtpVfsDirEntry { + FsDirectoryEntry buf; +}; + +#ifdef __cplusplus + +namespace sphaira::ftpsrv { + +bool Init(); +void Exit(); + +} // namespace sphaira::ftpsrv + +#endif // __cplusplus diff --git a/sphaira/include/ui/menus/filebrowser.hpp b/sphaira/include/ui/menus/filebrowser.hpp index ce50a6f..750052f 100644 --- a/sphaira/include/ui/menus/filebrowser.hpp +++ b/sphaira/include/ui/menus/filebrowser.hpp @@ -110,19 +110,18 @@ private: void LoadAssocEntriesPath(const fs::FsPath& path); void LoadAssocEntries(); auto FindFileAssocFor() -> std::vector; - void OnIndexChange(); auto GetNewPath(const FileEntry& entry) const -> fs::FsPath { return GetNewPath(m_path, entry.name); - }; + } auto GetNewPath(u64 index) const -> fs::FsPath { return GetNewPath(m_path, GetEntry(index).name); - }; + } auto GetNewPathCurrent() const -> fs::FsPath { return GetNewPath(m_index); - }; + } auto GetSelectedEntries() const -> std::vector { if (!m_selected_count) { diff --git a/sphaira/source/app.cpp b/sphaira/source/app.cpp index 5b1ad51..e3ead37 100644 --- a/sphaira/source/app.cpp +++ b/sphaira/source/app.cpp @@ -12,10 +12,12 @@ #include "fs.hpp" #include "defines.hpp" #include "i18n.hpp" +#include "ftpsrv_helper.hpp" #include #include #include +#include #include #include #include @@ -200,7 +202,13 @@ auto GetNroIcon(const std::vector& nro_icon) -> std::vector { return nro_icon; } +void haze_callback(const HazeCallbackData *data) { + App::NotifyFlashLed(); + evman::push(*data, false); +} + void nxlink_callback(const NxlinkCallbackData *data) { + App::NotifyFlashLed(); evman::push(*data, false); } @@ -247,6 +255,9 @@ void App::Loop() { } else if constexpr(std::is_same_v) { log_write("[ExitEventData] got event\n"); m_quit = true; + } else if constexpr(std::is_same_v) { + // log_write("[ExitEventData] got event\n"); + // m_quit = true; } else if constexpr(std::is_same_v) { switch (arg.type) { case NxlinkCallbackType_Connected: @@ -330,6 +341,28 @@ void App::NotifyClear(ui::NotifEntry::Side side) { g_app->m_notif_manager.Clear(side); } +void App::NotifyFlashLed() { + static const HidsysNotificationLedPattern pattern = { + .baseMiniCycleDuration = 0x1, // 12.5ms. + .totalMiniCycles = 0x1, // 1 mini cycle(s). + .totalFullCycles = 0x1, // 1 full run(s). + .startIntensity = 0xF, // 100%. + .miniCycles = {{ + .ledIntensity = 0xF, // 100%. + .transitionSteps = 0xF, // 1 step(s). Total 12.5ms. + .finalStepDuration = 0xF, // Forced 12.5ms. + }} + }; + + s32 total; + HidsysUniquePadId unique_pad_ids[16] = {0}; + if (R_SUCCEEDED(hidsysGetUniquePadIds(unique_pad_ids, 16, &total))) { + for (int i = 0; i < total; i++) { + hidsysSetNotificationLedPattern(&pattern, unique_pad_ids[i]); + } + } +} + auto App::GetThemeMetaList() -> std::span { return g_app->m_theme_meta_entries; } @@ -383,6 +416,14 @@ auto App::GetThemeMusicEnable() -> bool { return g_app->m_theme_music.Get(); } +auto App::GetMtpEnable() -> bool { + return g_app->m_mtp_enabled.Get(); +} + +auto App::GetFtpEnable() -> bool { + return g_app->m_ftp_enabled.Get(); +} + auto App::GetLanguage() -> long { return g_app->m_language.Get(); } @@ -434,6 +475,28 @@ void App::SetThemeMusicEnable(bool enable) { PlaySoundEffect(SoundEffect::SoundEffect_Music); } +void App::SetMtpEnable(bool enable) { + if (App::GetMtpEnable() != enable) { + g_app->m_mtp_enabled.Set(enable); + if (enable) { + hazeInitialize(haze_callback); + } else { + hazeExit(); + } + } +} + +void App::SetFtpEnable(bool enable) { + if (App::GetFtpEnable() != enable) { + g_app->m_ftp_enabled.Set(enable); + if (enable) { + ftpsrv::Init(); + } else { + ftpsrv::Exit(); + } + } +} + void App::SetLanguage(long index) { if (App::GetLanguage() != index) { g_app->m_language.Set(index); @@ -860,6 +923,14 @@ App::App(const char* argv0) { log_write("hello world\n"); } + if (App::GetMtpEnable()) { + hazeInitialize(haze_callback); + } + + if (App::GetFtpEnable()) { + ftpsrv::Init(); + } + if (App::GetNxlinkEnable()) { nxlinkInitialize(nxlink_callback); } @@ -1092,6 +1163,16 @@ App::~App() { } } + if (App::GetMtpEnable()) { + log_write("closing mtp\n"); + hazeExit(); + } + + if (App::GetFtpEnable()) { + log_write("closing ftp\n"); + ftpsrv::Exit(); + } + if (App::GetNxlinkEnable()) { log_write("closing nxlink\n"); nxlinkExit(); diff --git a/sphaira/source/download.cpp b/sphaira/source/download.cpp index 4af0c72..e677989 100644 --- a/sphaira/source/download.cpp +++ b/sphaira/source/download.cpp @@ -298,7 +298,7 @@ auto DownloadInternal(CURL* curl, DataStruct& chunk, ProgressCallback pcallback, fs::CreateDirectoryRecursivelyWithPath(&chunk.fs, tmp_buf); - if (auto rc = fsFsCreateFile(&chunk.fs, tmp_buf, 0, 0); R_FAILED(rc) && rc != FsError_ResultPathAlreadyExists) { + if (auto rc = fsFsCreateFile(&chunk.fs, tmp_buf, 0, 0); R_FAILED(rc) && rc != FsError_PathAlreadyExists) { log_write("failed to create file: %s\n", tmp_buf); return false; } diff --git a/sphaira/source/fs.cpp b/sphaira/source/fs.cpp index 49a62fe..8c2cd47 100644 --- a/sphaira/source/fs.cpp +++ b/sphaira/source/fs.cpp @@ -134,7 +134,7 @@ Result CreateDirectoryRecursively(FsFileSystem* fs, const FsPath& _path, bool ig rc = CreateDirectory(path, ignore_read_only); } - if (R_FAILED(rc) && rc != FsError_ResultPathAlreadyExists) { + if (R_FAILED(rc) && rc != FsError_PathAlreadyExists) { log_write("failed to create folder: %s\n", path); return rc; } @@ -166,7 +166,7 @@ Result CreateDirectoryRecursivelyWithPath(FsFileSystem* fs, const FsPath& _path, rc = CreateDirectory(path, ignore_read_only); } - if (R_FAILED(rc) && rc != FsError_ResultPathAlreadyExists) { + if (R_FAILED(rc) && rc != FsError_PathAlreadyExists) { log_write("failed to create folder recursively: %s\n", path); return rc; } @@ -251,7 +251,7 @@ Result write_entire_file(FsFileSystem* _fs, const FsPath& path, const std::vecto FsNative fs{_fs, false}; R_TRY(fs.GetFsOpenResult()); - if (auto rc = fs.CreateFile(path, in.size(), 0, ignore_read_only); R_FAILED(rc) && rc != FsError_ResultPathAlreadyExists) { + if (auto rc = fs.CreateFile(path, in.size(), 0, ignore_read_only); R_FAILED(rc) && rc != FsError_PathAlreadyExists) { return rc; } diff --git a/sphaira/source/ftpsrv_helper.cpp b/sphaira/source/ftpsrv_helper.cpp new file mode 100644 index 0000000..ef03698 --- /dev/null +++ b/sphaira/source/ftpsrv_helper.cpp @@ -0,0 +1,335 @@ +#include "ftpsrv_helper.hpp" +#include +#include +#include "app.hpp" +#include "fs.hpp" +#include "log.hpp" + +#include +#include +#include +#include +#include + +namespace { + +FtpSrvConfig g_ftpsrv_config = {0}; +volatile bool g_should_exit = false; +bool g_is_running{false}; +Thread g_thread; +std::mutex g_mutex{}; +FsFileSystem* g_fs; + +void ftp_log_callback(enum FTP_API_LOG_TYPE type, const char* msg) { + sphaira::App::NotifyFlashLed(); +} + +void ftp_progress_callback(void) { + sphaira::App::NotifyFlashLed(); +} + +int vfs_fs_set_errno(Result rc) { + switch (rc) { + case FsError_TargetLocked: errno = EBUSY; break; + case FsError_PathNotFound: errno = ENOENT; break; + case FsError_PathAlreadyExists: errno = EEXIST; break; + case FsError_UsableSpaceNotEnoughMmcCalibration: errno = ENOSPC; break; + case FsError_UsableSpaceNotEnoughMmcSafe: errno = ENOSPC; break; + case FsError_UsableSpaceNotEnoughMmcUser: errno = ENOSPC; break; + case FsError_UsableSpaceNotEnoughMmcSystem: errno = ENOSPC; break; + case FsError_UsableSpaceNotEnoughSdCard: errno = ENOSPC; break; + case FsError_OutOfRange: errno = ESPIPE; break; + case FsError_TooLongPath: errno = ENAMETOOLONG; break; + case FsError_UnsupportedWriteForReadOnlyFileSystem: errno = EROFS; break; + default: errno = EIO; break; + } + return -1; +} + +Result flush_buffered_write(struct FtpVfsFile* f) { + Result rc; + if (R_SUCCEEDED(rc = fsFileSetSize(&f->fd, f->off + f->buf_off))) { + rc = fsFileWrite(&f->fd, f->off, f->buf, f->buf_off, FsWriteOption_None); + } + return rc; +} + +void loop(void* arg) { + while (!g_should_exit) { + ftpsrv_init(&g_ftpsrv_config); + while (!g_should_exit) { + if (ftpsrv_loop(100) != FTP_API_LOOP_ERROR_OK) { + svcSleepThread(1e+6); + break; + } + } + ftpsrv_exit(); + } +} + +} // namespace + +namespace sphaira::ftpsrv { + +bool Init() { + std::scoped_lock lock{g_mutex}; + if (g_is_running) { + return false; + } + + g_fs = fsdevGetDeviceFileSystem("sdmc"); + + g_ftpsrv_config.log_callback = ftp_log_callback; + g_ftpsrv_config.progress_callback = ftp_progress_callback; + g_ftpsrv_config.anon = true; + g_ftpsrv_config.timeout = 15; + g_ftpsrv_config.port = 5000; + + Result rc; + if (R_FAILED(rc = threadCreate(&g_thread, loop, nullptr, nullptr, 1024*64, 0x2C, 2))) { + log_write("failed to create nxlink thread: 0x%X\n", rc); + return false; + } + + if (R_FAILED(rc = threadStart(&g_thread))) { + log_write("failed to start nxlink thread: 0x%X\n", rc); + threadClose(&g_thread); + return false; + } + + return g_is_running = true; +} + +void Exit() { + std::scoped_lock lock{g_mutex}; + if (g_is_running) { + g_is_running = false; + } + g_should_exit = true; + threadWaitForExit(&g_thread); + threadClose(&g_thread); +} + +} // namespace sphaira::ftpsrv + +extern "C" { + +#define VFS_NX_BUFFER_IO 1 + +int ftp_vfs_open(struct FtpVfsFile* f, const char* path, enum FtpVfsOpenMode mode) { + u32 open_mode; + if (mode == FtpVfsOpenMode_READ) { + open_mode = FsOpenMode_Read; + f->is_write = false; + } else { + fsFsCreateFile(g_fs, path, 0, 0); + open_mode = FsOpenMode_Write | FsOpenMode_Append; + #if !VFS_NX_BUFFER_IO + open_mode |= FsOpenMode_Append; + #endif + f->is_write = true; + } + + Result rc; + if (R_FAILED(rc = fsFsOpenFile(g_fs, path, open_mode, &f->fd))) { + return vfs_fs_set_errno(rc); + } + + f->off = f->buf_off = f->buf_size = 0; + + if (mode == FtpVfsOpenMode_WRITE) { + if (R_FAILED(rc = fsFileSetSize(&f->fd, 0))) { + goto fail_close; + } + } else if (mode == FtpVfsOpenMode_APPEND) { + if (R_FAILED(rc = fsFileGetSize(&f->fd, &f->off))) { + goto fail_close; + } + } + + f->is_valid = true; + return 0; + +fail_close: + fsFileClose(&f->fd); + return vfs_fs_set_errno(rc); +} + +int ftp_vfs_read(struct FtpVfsFile* f, void* buf, size_t size) { + Result rc; + + #if VFS_NX_BUFFER_IO + if (f->buf_off == f->buf_size) { + u64 bytes_read; + if (R_FAILED(rc = fsFileRead(&f->fd, f->off, f->buf, sizeof(f->buf), FsReadOption_None, &bytes_read))) { + return vfs_fs_set_errno(rc); + } + + f->buf_off = 0; + f->buf_size = bytes_read; + } + + if (!f->buf_size) { + return 0; + } + + size = size < f->buf_size - f->buf_off ? size : f->buf_size - f->buf_off; + memcpy(buf, f->buf + f->buf_off, size); + f->off += size; + f->buf_off += size; + return size; +#else + u64 bytes_read; + if (R_FAILED(rc = fsFileRead(&f->fd, f->off, buf, size, FsReadOption_None, &bytes_read))) { + return vfs_fs_set_errno(rc); + } + f->off += bytes_read; + return bytes_read; +#endif +} + +int ftp_vfs_write(struct FtpVfsFile* f, const void* buf, size_t size) { + Result rc; + +#if VFS_NX_BUFFER_IO + const size_t ret = size; + while (size) { + if (f->buf_off + size > sizeof(f->buf)) { + const u64 sz = sizeof(f->buf) - f->buf_off; + memcpy(f->buf + f->buf_off, buf, sz); + f->buf_off += sz; + + if (R_FAILED(rc = flush_buffered_write(f))) { + return vfs_fs_set_errno(rc); + } + + buf += sz; + size -= sz; + f->off += f->buf_off; + f->buf_off = 0; + } else { + memcpy(f->buf + f->buf_off, buf, size); + f->buf_off += size; + size = 0; + } + } + + return ret; +#else + if (R_FAILED(rc = fsFileWrite(&f->fd, f->off, buf, size, FsWriteOption_None))) { + return vfs_fs_set_errno(rc); + } + f->off += size; + return size; + const size_t ret = size; +#endif +} + +// buf and size is the amount of data sent. +int ftp_vfs_seek(struct FtpVfsFile* f, const void* buf, size_t size, size_t off) { +#if VFS_NX_BUFFER_IO + if (!f->is_write) { + f->buf_off -= f->off - off; + } +#endif + f->off = off; + return 0; +} + +int ftp_vfs_close(struct FtpVfsFile* f) { + if (!ftp_vfs_isfile_open(f)) { + return -1; + } + + if (f->is_write && f->buf_off) { + flush_buffered_write(f); + } + + fsFileClose(&f->fd); + f->is_valid = false; + return 0; +} + +int ftp_vfs_isfile_open(struct FtpVfsFile* f) { + return f->is_valid; +} + +int ftp_vfs_opendir(struct FtpVfsDir* f, const char* path) { + Result rc; + if (R_FAILED(rc = fsFsOpenDirectory(g_fs, path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize, &f->dir))) { + return vfs_fs_set_errno(rc); + } + f->is_valid = true; + return 0; +} + +const char* ftp_vfs_readdir(struct FtpVfsDir* f, struct FtpVfsDirEntry* entry) { + Result rc; + s64 total_entries; + if (R_FAILED(rc = fsDirRead(&f->dir, &total_entries, 1, &entry->buf))) { + vfs_fs_set_errno(rc); + return NULL; + } + + if (total_entries <= 0) { + return NULL; + } + + return entry->buf.name; +} + +int ftp_vfs_dirlstat(struct FtpVfsDir* f, const struct FtpVfsDirEntry* entry, const char* path, struct stat* st) { + return lstat(path, st); +} + +int ftp_vfs_closedir(struct FtpVfsDir* f) { + if (!ftp_vfs_isdir_open(f)) { + return -1; + } + + fsDirClose(&f->dir); + f->is_valid = false; + return 0; +} + +int ftp_vfs_isdir_open(struct FtpVfsDir* f) { + return f->is_valid; +} + +int ftp_vfs_stat(const char* path, struct stat* st) { + return stat(path, st); +} + +int ftp_vfs_lstat(const char* path, struct stat* st) { + return lstat(path, st); +} + +int ftp_vfs_mkdir(const char* path) { + return mkdir(path, 0777); +} + +int ftp_vfs_unlink(const char* path) { + return unlink(path); +} + +int ftp_vfs_rmdir(const char* path) { + return rmdir(path); +} + +int ftp_vfs_rename(const char* src, const char* dst) { + return rename(src, dst); +} + +int ftp_vfs_readlink(const char* path, char* buf, size_t buflen) { + return -1; +} + +const char* ftp_vfs_getpwuid(const struct stat* st) { + return "unknown"; +} + +const char* ftp_vfs_getgrgid(const struct stat* st) { + return "unknown"; +} + +} // extern "C" diff --git a/sphaira/source/main.cpp b/sphaira/source/main.cpp index 1a5c72e..8bc833a 100644 --- a/sphaira/source/main.cpp +++ b/sphaira/source/main.cpp @@ -61,6 +61,8 @@ void userAppInit(void) { diagAbortWithResult(rc); if (R_FAILED(rc = setInitialize())) diagAbortWithResult(rc); + if (R_FAILED(rc = hidsysInitialize())) + diagAbortWithResult(rc); log_nxlink_init(); } @@ -68,6 +70,7 @@ void userAppInit(void) { void userAppExit(void) { log_nxlink_exit(); + hidsysExit(); setExit(); accountExit(); nifmExit(); diff --git a/sphaira/source/nxlink.cpp b/sphaira/source/nxlink.cpp index 9ca7a8a..40099d4 100644 --- a/sphaira/source/nxlink.cpp +++ b/sphaira/source/nxlink.cpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace { @@ -114,7 +115,7 @@ auto recvall(int sock, void* buf, int size) -> bool { if (errno != EWOULDBLOCK && errno != EAGAIN) { return false; } - svcSleepThread(YieldType_WithoutCoreMigration); + svcSleepThread(1e+6); } else { got += len; left -= len; @@ -132,7 +133,7 @@ auto sendall(Socket sock, const void* buf, int size) -> bool { if (errno != EWOULDBLOCK && errno != EAGAIN) { return false; } - svcSleepThread(YieldType_WithoutCoreMigration); + svcSleepThread(1e+6); } sent += len; left -= len; @@ -171,28 +172,6 @@ auto get_file_data(Socket sock, int max) -> std::vector { return buf; } -#if 0 -auto create_directories(fs::FsNative& fs, const std::string& path) -> Result { - std::size_t pos{}; - - // no sane person creates 20 directories deep - for (int i = 0; i < 20; i++) { - pos = path.find_first_of("/", pos); - if (pos == std::string::npos) { - break; - } - pos++; - - fs::FsPath safe_buf; - std::strcpy(safe_buf, path.substr(0, pos).c_str()); - const auto rc = fs.CreateDirectory(safe_buf); - R_UNLESS(R_SUCCEEDED(rc) || rc == FsError_ResultPathAlreadyExists, rc); - } - - R_SUCCEED(); -} -#endif - void loop(void* args) { log_write("in nxlink thread func\n"); const sockaddr_in servaddr{ @@ -266,21 +245,36 @@ void loop(void* args) { sockaddr_in sa_remote{}; - while (!g_quit) { - svcSleepThread(1e+8); + pollfd pfds[2]; + pfds[0].fd = sock; + pfds[0].events = POLLIN; + pfds[1].fd = sock_udp; + pfds[1].events = POLLIN; - if (poll_network_change()) { + while (!g_quit) { + auto poll_rc = poll(pfds, std::size(pfds), 1000/60); + if (poll_rc < 0) { + break; + } else if (poll_rc == 0) { + continue; + } else if ((pfds[0].revents & (POLLERR|POLLHUP|POLLNVAL)) || (pfds[1].revents & (POLLERR|POLLHUP|POLLNVAL))) { break; } - char recvbuf[256]; - socklen_t from_len = sizeof(sa_remote); - const auto udp_len = recvfrom(sock_udp, recvbuf, sizeof(recvbuf), 0, (sockaddr*)&sa_remote, &from_len); - if (udp_len > 0 && !std::strncmp(recvbuf, UDP_MAGIC_SERVER, std::strlen(UDP_MAGIC_SERVER))) { - // log_write("got udp len: %d - %.*s\n", udp_len, udp_len, recvbuf); - sa_remote.sin_family = AF_INET; - sa_remote.sin_port = htons(NXLINK_CLIENT_PORT); - sendto(sock_udp, UDP_MAGIC_CLIENT, std::strlen(UDP_MAGIC_CLIENT), 0, (sockaddr*)&sa_remote, sizeof(sa_remote)); + if (pfds[1].revents & POLLIN) { + char recvbuf[6]; + socklen_t from_len = sizeof(sa_remote); + auto udp_len = recvfrom(sock_udp, recvbuf, sizeof(recvbuf), 0, (sockaddr*)&sa_remote, &from_len); + if (udp_len == sizeof(recvbuf) && !std::strncmp(recvbuf, UDP_MAGIC_SERVER, std::strlen(UDP_MAGIC_SERVER))) { + // log_write("got udp len: %d - %.*s\n", udp_len, udp_len, recvbuf); + sa_remote.sin_family = AF_INET; + sa_remote.sin_port = htons(NXLINK_CLIENT_PORT); + udp_len = sendto(sock_udp, UDP_MAGIC_CLIENT, std::strlen(UDP_MAGIC_CLIENT), 0, (const sockaddr*)&sa_remote, sizeof(sa_remote)); + if (udp_len != std::strlen(UDP_MAGIC_CLIENT)) { + log_write("nxlink failed to send udp packet\n"); + continue; + } + } } socklen_t accept_len = sizeof(sa_remote); @@ -316,8 +310,9 @@ void loop(void* args) { } // check that we have enough space + fs::FsNativeSd fs; s64 sd_storage_space_free; - if (R_FAILED(fs::FsNativeSd().GetFreeSpace("/", &sd_storage_space_free)) || filesize >= sd_storage_space_free) { + if (R_FAILED(fs.GetFreeSpace("/", &sd_storage_space_free)) || filesize >= sd_storage_space_free) { sendall(connfd, &ERR_SPACE, sizeof(ERR_SPACE)); continue; } @@ -345,16 +340,6 @@ void loop(void* args) { path = name; } - // std::strcat(temp_path, "~"); - - fs::FsNativeSd fs; - - if (R_FAILED(rc = fs.GetFsOpenResult())) { - sendall(connfd, &ERR_FILE, sizeof(ERR_FILE)); - log_write("failed to open fs: 0x%X\n", socketGetLastResult()); - continue; - } - // if (R_FAILED(rc = create_directories(fs, path))) { if (R_FAILED(rc = fs.CreateDirectoryRecursivelyWithPath(path))) { sendall(connfd, &ERR_FILE, sizeof(ERR_FILE)); @@ -364,7 +349,7 @@ void loop(void* args) { // this is the path we will write to const auto temp_path = path + "~"; - if (R_FAILED(rc = fs.CreateFile(temp_path, file_data.size(), 0)) && rc != FsError_ResultPathAlreadyExists) { + if (R_FAILED(rc = fs.CreateFile(temp_path, file_data.size(), 0)) && rc != FsError_PathAlreadyExists) { sendall(connfd, &ERR_FILE, sizeof(ERR_FILE)); log_write("failed to create file: %X\n", rc); continue; @@ -408,7 +393,7 @@ void loop(void* args) { } } - if (R_FAILED(rc = fs.DeleteFile(path)) && rc != FsError_ResultPathNotFound) { + if (R_FAILED(rc = fs.DeleteFile(path)) && rc != FsError_PathNotFound) { log_write("failed to delete %X\n", rc); continue; } @@ -473,13 +458,14 @@ bool nxlinkInitialize(NxlinkCallback callback) { g_callback = callback; g_quit = false; - if (R_FAILED(threadCreate(&g_thread, loop, nullptr, nullptr, 1024*64, 0x2C, 2))) { - log_write("failed to create nxlink thread: 0x%X\n", socketGetLastResult()); + Result rc; + if (R_FAILED(rc = threadCreate(&g_thread, loop, nullptr, nullptr, 1024*64, 0x2C, 2))) { + log_write("failed to create nxlink thread: 0x%X\n", rc); return false; } - if (R_FAILED(threadStart(&g_thread))) { - log_write("failed to start nxlink thread: 0x%X\n", socketGetLastResult()); + if (R_FAILED(rc = threadStart(&g_thread))) { + log_write("failed to start nxlink thread: 0x%X\n", rc); threadClose(&g_thread); return false; } diff --git a/sphaira/source/ui/menus/appstore.cpp b/sphaira/source/ui/menus/appstore.cpp index 5b15f55..c92fe72 100644 --- a/sphaira/source/ui/menus/appstore.cpp +++ b/sphaira/source/ui/menus/appstore.cpp @@ -548,7 +548,7 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> bool { fs.CreateDirectoryRecursivelyWithPath(output, true); Result rc; - if (R_FAILED(rc = fs.CreateFile(output, info.uncompressed_size, 0, true)) && rc != FsError_ResultPathAlreadyExists) { + if (R_FAILED(rc = fs.CreateFile(output, info.uncompressed_size, 0, true)) && rc != FsError_PathAlreadyExists) { log_write("failed to create file: %s 0x%04X\n", output, rc); return false; } diff --git a/sphaira/source/ui/menus/filebrowser.cpp b/sphaira/source/ui/menus/filebrowser.cpp index 9417b8d..b7e93c8 100644 --- a/sphaira/source/ui/menus/filebrowser.cpp +++ b/sphaira/source/ui/menus/filebrowser.cpp @@ -30,7 +30,8 @@ #include #include #include -#include +// #include +#include namespace sphaira::ui::menu::filebrowser { namespace { @@ -61,42 +62,47 @@ constexpr std::string_view IMAGE_EXTENSIONS[] = { "png", "jpg", "jpeg", "bmp", "gif", }; -using PathPair = std::pair; -constexpr PathPair PATHS[]{ - PathPair{"3do", "The 3DO Company - 3DO"}, - PathPair{"atari800", "Atari - 8-bit"}, - PathPair{"atari2600", "Atari - 2600"}, - PathPair{"atari5200", "Atari - 5200"}, - PathPair{"atari7800", "Atari - 7800"}, - PathPair{"atarilynx", "Atari - Lynx"}, - PathPair{"atarijaguar", "Atari - Jaguar"}, - PathPair{"atarijaguarcd", ""}, - PathPair{"n3ds", "Nintendo - Nintendo 3DS"}, - PathPair{"n64", "Nintendo - Nintendo 64"}, - PathPair{"nds", "Nintendo - Nintendo DS"}, - PathPair{"fds", "Nintendo - Famicom Disk System"}, - PathPair{"nes", "Nintendo - Nintendo Entertainment System"}, - PathPair{"pokemini", "Nintendo - Pokemon Mini"}, - PathPair{"gb", "Nintendo - Game Boy"}, - PathPair{"gba", "Nintendo - Game Boy Advance"}, - PathPair{"gbc", "Nintendo - Game Boy Color"}, - PathPair{"virtualboy", "Nintendo - Virtual Boy"}, - PathPair{"gameandwatch", ""}, - PathPair{"sega32x", "Sega - 32X"}, - PathPair{"segacd", "Sega - Mega CD - Sega CD"}, - PathPair{"dreamcast", "Sega - Dreamcast"}, - PathPair{"gamegear", "Sega - Game Gear"}, - PathPair{"genesis", "Sega - Mega Drive - Genesis"}, - PathPair{"mastersystem", "Sega - Master System - Mark III"}, - PathPair{"megadrive", "Sega - Mega Drive - Genesis"}, - PathPair{"saturn", "Sega - Saturn"}, - PathPair{"sg-1000", "Sega - SG-1000"}, - PathPair{"psx", "Sony - PlayStation"}, - PathPair{"psp", "Sony - PlayStation Portable"}, - PathPair{"snes", "Nintendo - Super Nintendo Entertainment System"}, - PathPair{"pico8", "Sega - PICO"}, - PathPair{"wonderswan", "Bandai - WonderSwan"}, - PathPair{"wonderswancolor", "Bandai - WonderSwan Color"}, +struct RomDatabaseEntry { + std::string_view folder; + std::string_view database; +}; + +// using PathPair = std::pair; +constexpr RomDatabaseEntry PATHS[]{ + { "3do", "The 3DO Company - 3DO"}, + { "atari800", "Atari - 8-bit"}, + { "atari2600", "Atari - 2600"}, + { "atari5200", "Atari - 5200"}, + { "atari7800", "Atari - 7800"}, + { "atarilynx", "Atari - Lynx"}, + { "atarijaguar", "Atari - Jaguar"}, + { "atarijaguarcd", ""}, + { "n3ds", "Nintendo - Nintendo 3DS"}, + { "n64", "Nintendo - Nintendo 64"}, + { "nds", "Nintendo - Nintendo DS"}, + { "fds", "Nintendo - Famicom Disk System"}, + { "nes", "Nintendo - Nintendo Entertainment System"}, + { "pokemini", "Nintendo - Pokemon Mini"}, + { "gb", "Nintendo - Game Boy"}, + { "gba", "Nintendo - Game Boy Advance"}, + { "gbc", "Nintendo - Game Boy Color"}, + { "virtualboy", "Nintendo - Virtual Boy"}, + { "gameandwatch", ""}, + { "sega32x", "Sega - 32X"}, + { "segacd", "Sega - Mega CD - Sega CD"}, + { "dreamcast", "Sega - Dreamcast"}, + { "gamegear", "Sega - Game Gear"}, + { "genesis", "Sega - Mega Drive - Genesis"}, + { "mastersystem", "Sega - Master System - Mark III"}, + { "megadrive", "Sega - Mega Drive - Genesis"}, + { "saturn", "Sega - Saturn"}, + { "sg-1000", "Sega - SG-1000"}, + { "psx", "Sony - PlayStation"}, + { "psp", "Sony - PlayStation Portable"}, + { "snes", "Nintendo - Super Nintendo Entertainment System"}, + { "pico8", "Sega - PICO"}, + { "wonderswan", "Bandai - WonderSwan"}, + { "wonderswancolor", "Bandai - WonderSwan Color"}, }; constexpr fs::FsPath DAYBREAK_PATH{"/switch/daybreak.nro"}; @@ -127,47 +133,49 @@ auto IsExtension(std::string_view ext1, std::string_view ext2) -> bool { // tries to find database path using folder name // names are taken from retropie // retroarch database names can also be used -auto GetRomDatabaseFromPath(const std::string& path) -> std::string { +auto GetRomDatabaseFromPath(std::string_view path) -> int { if (path.length() <= 1) { - return {}; + return -1; } // this won't fail :) const auto db_name = path.substr(path.find_last_of('/') + 1); - log_write("new path: %s\n", db_name.c_str()); + // log_write("new path: %s\n", db_name.c_str()); - for (auto& [folder_name, database_name] : PATHS) { + for (int i = 0; i < std::size(PATHS); i++) { + auto& p = PATHS[i]; if (( - folder_name.length() == db_name.length() && !strncasecmp(folder_name.data(), db_name.data(), folder_name.length())) || - (database_name.length() == db_name.length() && !strncasecmp(database_name.data(), db_name.data(), database_name.length()))) { - log_write("found it :) %s\n", std::string{database_name.data(), database_name.length()}.c_str()); - return std::string{database_name.data(), database_name.length()}; + p.folder.length() == db_name.length() && !strncasecmp(p.folder.data(), db_name.data(), p.folder.length())) || + (p.database.length() == db_name.length() && !strncasecmp(p.database.data(), db_name.data(), p.database.length()))) { + log_write("found it :) %.*s\n", p.database.length(), p.database.data()); + return i; } } // if we failed, try again but with the folder about // "/roms/psx/scooby-doo/scooby-doo.bin", this will check psx const auto last_off = path.substr(0, path.find_last_of('/')); - if (const auto off = last_off.find_last_of('/'); off != std::string::npos) { + if (const auto off = last_off.find_last_of('/'); off != std::string_view::npos) { const auto db_name2 = last_off.substr(off + 1); - printf("got db: %s\n", db_name2.c_str()); - for (auto& [folder_name, database_name] : PATHS) { + // printf("got db: %s\n", db_name2.c_str()); + for (int i = 0; i < std::size(PATHS); i++) { + auto& p = PATHS[i]; if (( - folder_name.length() == db_name2.length() && !strcasecmp(folder_name.data(), db_name2.data())) || - (database_name.length() == db_name2.length() && !strcasecmp(database_name.data(), db_name2.data()))) { - log_write("found it :) %s\n", std::string{database_name.data(), database_name.length()}.c_str()); - return std::string{database_name.data(), database_name.length()}; + p.folder.length() == db_name2.length() && !strcasecmp(p.folder.data(), db_name2.data())) || + (p.database.length() == db_name2.length() && !strcasecmp(p.database.data(), db_name2.data()))) { + log_write("found it :) %.*s\n", p.database.length(), p.database.data()); + return i; } } } - return {}; + return -1; } // -auto GetRomIcon(ProgressBox* pbox, std::string filename, std::string extension, std::string database, const NroEntry& nro) { +auto GetRomIcon(ProgressBox* pbox, std::string filename, std::string extension, int db_idx, const NroEntry& nro) { // if no db entries, use nro icon - if (database.empty()) { + if (db_idx < 0) { log_write("using nro image\n"); return nro_get_icon(nro.path, nro.icon_size, nro.icon_offset); } @@ -189,7 +197,7 @@ auto GetRomIcon(ProgressBox* pbox, std::string filename, std::string extension, #define RA_THUMBNAIL_PATH "/retroarch/thumbnails/" #define RA_BOXART_EXT ".png" - const std::string system_name = database;//GetDatabaseFromExt(database, extension); + const auto system_name = std::string{PATHS[db_idx].database.data(), PATHS[db_idx].database.length()};//GetDatabaseFromExt(database, extension); auto system_name_gh = system_name + "/master"; for (auto& c : system_name_gh) { if (c == ' ') { @@ -601,7 +609,7 @@ Menu::Menu(const std::vector& nro_entries) : MenuBase{"FileBrowser"_i1 } }, true)); - if (m_entries_current.size()) { + if (m_entries_current.size() && !m_selected_count && GetEntry().IsFile() && GetEntry().file_size < 1024*64) { options->Add(std::make_shared("View as text (unfinished)"_i18n, [this](){ App::Push(std::make_shared(GetNewPathCurrent())); }, true)); @@ -864,7 +872,7 @@ void Menu::InstallForwarder() { log_write("got filename2: %s\n\n", file_name.c_str()); } - const auto database = GetRomDatabaseFromPath(m_path); + const auto db_idx = GetRomDatabaseFromPath(m_path); OwoConfig config{}; config.nro_path = assoc.path.toString(); @@ -872,7 +880,7 @@ void Menu::InstallForwarder() { config.name = nro.nacp.lang[0].name + std::string{" | "} + file_name; // config.name = file_name; config.nacp = nro.nacp; - config.icon = GetRomIcon(pbox, file_name, extension, database, nro); + config.icon = GetRomIcon(pbox, file_name, extension, db_idx, nro); return R_SUCCEEDED(App::Install(pbox, config)); })); @@ -966,24 +974,24 @@ auto Menu::Scan(const fs::FsPath& new_path, bool is_walk_up) -> Result { auto Menu::FindFileAssocFor() -> std::vector { // only support roms in correctly named folders, sorry! - const auto database_name = GetRomDatabaseFromPath(m_path); + const auto db_idx = GetRomDatabaseFromPath(m_path); const auto& entry = GetEntry(); const auto extension = entry.internal_extension.empty() ? entry.extension : entry.internal_extension; if (extension.empty()) { - log_write("failed to get extension for db: %s path: %s\n", database_name.c_str(), m_path); + // log_write("failed to get extension for db: %s path: %s\n", database_entry.c_str(), m_path); return {}; } - log_write("got extension for db: %s path: %s\n", database_name.c_str(), m_path); + // log_write("got extension for db: %s path: %s\n", database_entry.c_str(), m_path); std::vector out_entries; - if (!database_name.empty()) { + if (db_idx >= 0) { // if database isn't empty, then we are in a valid folder // search for an entry that matches the db and ext for (const auto& assoc : m_assoc_entries) { for (const auto& assoc_db : assoc.database) { - if (assoc_db == database_name) { + if (assoc_db == PATHS[db_idx].folder || assoc_db == PATHS[db_idx].database) { for (const auto& assoc_ext : assoc.ext) { if (assoc_ext == extension) { log_write("found ext: %s assoc_ext: %s assoc.ext: %s\n", assoc.path, assoc_ext.c_str(), extension.c_str()); @@ -1021,76 +1029,60 @@ void Menu::LoadAssocEntriesPath(const fs::FsPath& path) { return; } ON_SCOPE_EXIT(closedir(dir)); + fs::FsNativeSd fs; while (auto d = readdir(dir)) { if (d->d_name[0] == '.') { continue; } - // const std::string name = d->d_name; const auto ext = std::strrchr(d->d_name, '.'); - if (!ext) { - continue; - } - if (strcasecmp(ext, ".ini")) { + if (!ext || strcasecmp(ext, ".ini")) { continue; } const auto full_path = GetNewPath(path, d->d_name); - - fs::FsPath buf{}; - const auto ext_len = 1 + ini_gets("config", "supported_extensions", "", buf, sizeof(buf) - 1, full_path); - if (ext_len <= 1) { - continue; - } - - // log_write("reading ini: %s\n", name.c_str()); - FileAssocEntry assoc{}; - for (int i = 0; i < ext_len; i++) { - for (int j = i; j < ext_len; j++) { - if (buf[j] == '|' || buf[j] == '\0') { - assoc.ext.emplace_back(buf + i, j - i); - i += j - i; - break; + ini_browse([](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) { + auto assoc = static_cast(UserData); + if (!strcmp(Key, "path")) { + assoc->path = Value; + } else if (!strcmp(Key, "supported_extensions")) { + for (int i = 0; Value[i]; i++) { + for (int j = i; ; j++) { + if (Value[j] == '|' || Value[j] == '\0') { + assoc->ext.emplace_back(Value + i, j - i); + i += j - i; + break; + } + } + } + } else if (!strcmp(Key, "database")) { + for (int i = 0; Value[i]; i++) { + for (int j = i; ; j++) { + if (Value[j] == '|' || Value[j] == '\0') { + assoc->database.emplace_back(Value + i, j - i); + i += j - i; + break; + } + } } } - } + return 1; + }, &assoc, full_path); if (assoc.ext.empty()) { continue; } - // assoc.name = name.substr(0, name.find_last_of('.')); assoc.name.assign(d->d_name, ext - d->d_name); - const auto path_len = ini_gets("config", "path", "", buf, sizeof(buf) - 1, full_path); - if (path_len > 0) { - assoc.path = buf; - } - - const auto database_len = 1 + ini_gets("config", "database", "", buf, sizeof(buf) - 1, full_path); - if (database_len > 1) { - for (int i = 0; i < database_len; i++) { - for (int j = i; j < database_len; j++) { - if (buf[j] == '|' || buf[j] == '\0') { - assoc.database.emplace_back(buf + i, j - i); - i += j - i; - break; - } - } - } - } - - bool file_exists{}; - // if path isn't empty, check if the file exists + bool file_exists{}; if (!assoc.path.empty()) { - file_exists = fs::FsNativeSd().FileExists(assoc.path); + file_exists = fs.FileExists(assoc.path); } else { - log_write("\tpath is empty\n"); - #if 1 const auto nro_name = assoc.name + ".nro"; for (const auto& nro : m_nro_entries) { const auto len = std::strlen(nro.path); @@ -1103,7 +1095,6 @@ void Menu::LoadAssocEntriesPath(const fs::FsPath& path) { break; } } - #endif } // after all of that, the file doesn't exist :( @@ -1135,32 +1126,6 @@ void Menu::LoadAssocEntries() { LoadAssocEntriesPath("/config/sphaira/assoc/"); } -#if 0 -void Menu::OnIndexChange() { - if (!GetEntry().checked_internal_extension && GetEntry().extension == "zip") { - GetEntry().checked_internal_extension = true; - if (auto zfile = unzOpen64(GetNewPathCurrent())) { - ON_SCOPE_EXIT(unzClose(zfile)); - unz_global_info gi{}; - // only check first entry (i think RA does the same) - if (UNZ_OK == unzGetGlobalInfo(zfile, &gi) && gi.number_entry >= 1) { - fs::FsPath filename_inzip{}; - unz_file_info64 file_info{}; - if (UNZ_OK == unzOpenCurrentFile(zfile)) { - ON_SCOPE_EXIT(unzCloseCurrentFile(zfile)); - if (UNZ_OK == unzGetCurrentFileInfo64(zfile, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0)) { - if (auto ext = std::strrchr(filename_inzip, '.')) { - GetEntry().internal_name = filename_inzip.toString(); - GetEntry().internal_extension = ext+1; - } - } - } - } - } - } -} -#endif - void Menu::Sort() { // returns true if lhs should be before rhs const auto sort = m_sort.Get(); diff --git a/sphaira/source/ui/menus/main_menu.cpp b/sphaira/source/ui/menus/main_menu.cpp index 6823738..2239571 100644 --- a/sphaira/source/ui/menus/main_menu.cpp +++ b/sphaira/source/ui/menus/main_menu.cpp @@ -91,13 +91,13 @@ auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string v Result rc; if (file_path[strlen(file_path) -1] == '/') { - if (R_FAILED(rc = fs.CreateDirectoryRecursively(file_path)) && rc != FsError_ResultPathAlreadyExists) { + if (R_FAILED(rc = fs.CreateDirectoryRecursively(file_path)) && rc != FsError_PathAlreadyExists) { log_write("failed to create folder: %s 0x%04X\n", file_path, rc); return false; } } else { Result rc; - if (R_FAILED(rc = fs.CreateFile(file_path, info.uncompressed_size, 0)) && rc != FsError_ResultPathAlreadyExists) { + if (R_FAILED(rc = fs.CreateFile(file_path, info.uncompressed_size, 0)) && rc != FsError_PathAlreadyExists) { log_write("failed to create file: %s 0x%04X\n", file_path, rc); return false; } @@ -249,6 +249,14 @@ MainMenu::MainMenu() { auto options = std::make_shared("Network Options"_i18n, Sidebar::Side::LEFT); ON_SCOPE_EXIT(App::Push(options)); + options->Add(std::make_shared("Ftp"_i18n, App::GetFtpEnable(), [this](bool& enable){ + App::SetFtpEnable(enable); + }, "Enabled"_i18n, "Disabled"_i18n)); + + options->Add(std::make_shared("Mtp"_i18n, App::GetMtpEnable(), [this](bool& enable){ + App::SetMtpEnable(enable); + }, "Enabled"_i18n, "Disabled"_i18n)); + options->Add(std::make_shared("Nxlink"_i18n, App::GetNxlinkEnable(), [this](bool& enable){ App::SetNxlinkEnable(enable); }, "Enabled"_i18n, "Disabled"_i18n)); diff --git a/sphaira/source/ui/menus/themezer.cpp b/sphaira/source/ui/menus/themezer.cpp index a5abfae..5c51f3b 100644 --- a/sphaira/source/ui/menus/themezer.cpp +++ b/sphaira/source/ui/menus/themezer.cpp @@ -369,7 +369,7 @@ auto InstallTheme(ProgressBox* pbox, const PackListEntry& entry) -> bool { const auto file_path = fs::AppendPath(dir_path, name); Result rc; - if (R_FAILED(rc = fs.CreateFile(file_path, info.uncompressed_size, 0)) && rc != FsError_ResultPathAlreadyExists) { + if (R_FAILED(rc = fs.CreateFile(file_path, info.uncompressed_size, 0)) && rc != FsError_PathAlreadyExists) { log_write("failed to create file: %s 0x%04X\n", file_path, rc); return false; } diff --git a/sphaira/source/ui/notification.cpp b/sphaira/source/ui/notification.cpp index 567cf6c..a0b6b20 100644 --- a/sphaira/source/ui/notification.cpp +++ b/sphaira/source/ui/notification.cpp @@ -135,6 +135,9 @@ auto NotifMananger::Clear(NotifEntry::Side side) -> void { } auto NotifMananger::Clear() -> void { + mutexLock(&m_mutex); + ON_SCOPE_EXIT(mutexUnlock(&m_mutex)); + m_entries_left.clear(); m_entries_right.clear(); }