Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f01dbf7c67 | ||
|
|
7c273f30f3 | ||
|
|
37890f157d | ||
|
|
a2c9b63dfd | ||
|
|
3df676df0f | ||
|
|
276ee36bfe | ||
|
|
17b622833a | ||
|
|
536c169255 | ||
|
|
38640ea696 | ||
|
|
372399a27d | ||
|
|
483b2b3ce0 | ||
|
|
e0040b625e | ||
|
|
10f079e881 | ||
|
|
4a058d3caf | ||
|
|
986ffdcd9c | ||
|
|
97085ef282 | ||
|
|
d02fbcf282 | ||
|
|
c8ae2a7872 | ||
|
|
79da00e098 | ||
|
|
0edd7c400f | ||
|
|
aa03256fd4 | ||
|
|
8f1084b24f | ||
|
|
55c952a51f | ||
|
|
dd6371997c | ||
|
|
c8c4a273c9 | ||
|
|
0570c14343 | ||
|
|
66f2171995 | ||
|
|
3178f11596 | ||
|
|
e2d9db8928 | ||
|
|
945d1f3ae6 |
@@ -42,5 +42,12 @@ function(dkp_fatal_if_not_found var package)
|
||||
endif()
|
||||
endfunction(dkp_fatal_if_not_found var package)
|
||||
|
||||
# disable exceptions and rtti in order to shrink final binary size.
|
||||
add_compile_options(
|
||||
"$<$<COMPILE_LANGUAGE:C>:-fno-exceptions>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>"
|
||||
)
|
||||
|
||||
add_subdirectory(hbl)
|
||||
add_subdirectory(sphaira)
|
||||
|
||||
11
README.md
11
README.md
@@ -2,8 +2,6 @@
|
||||
|
||||
A homebrew menu for the switch.
|
||||
|
||||
It was built for my usage, as such, features that may seem out of place are included because i found them useful.
|
||||
|
||||
[See the gbatemp thread for more details / discussion](https://gbatemp.net/threads/sphaira-hbmenu-replacement.664523/).
|
||||
|
||||
## showcase
|
||||
@@ -26,6 +24,14 @@ please include:
|
||||
- FW version
|
||||
- The bug itself and how to reproduce it
|
||||
|
||||
## ftp
|
||||
|
||||
ftp can be enabled via the network menu and listens on port 5000, no username or password is required.
|
||||
|
||||
## mtp
|
||||
|
||||
mtp can be enabled via the network menu.
|
||||
|
||||
## file assoc
|
||||
|
||||
sphaira has file assoc support. lets say your app supports loading .png files, then you could write an assoc file, then when using the file browser, clicking on a .png file will launch your app along with the .png file as argv[1]. This was primarly added for rom loading support for emulators / frontends such as retroarch, melonds, mgba etc.
|
||||
@@ -52,4 +58,5 @@ see `assets/romfs/assoc/` for more examples of file assoc entries
|
||||
- minIni
|
||||
- gbatemp
|
||||
- hb-appstore
|
||||
- haze
|
||||
- everyone who has contributed to this project!
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/2048_libretro_libnx.nro
|
||||
supported_extensions=
|
||||
database=2048
|
||||
4
assets/romfs/assoc/DoubleCherryGB_libretro_libnx.ini
Normal file
4
assets/romfs/assoc/DoubleCherryGB_libretro_libnx.ini
Normal file
@@ -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
|
||||
4
assets/romfs/assoc/ardens_libretro_libnx.ini
Normal file
4
assets/romfs/assoc/ardens_libretro_libnx.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
[config]
|
||||
path=/retroarch/cores/ardens_libretro_libnx.nro
|
||||
supported_extensions=hex|arduboy
|
||||
database=Arduboy Inc - Arduboy
|
||||
@@ -1,4 +1,4 @@
|
||||
[config]
|
||||
path=/retroarch/cores/arduous_libretro_libnx.nro
|
||||
supported_extensions=hex
|
||||
database=Arduboy
|
||||
database=Arduboy Inc - Arduboy
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
4
assets/romfs/assoc/dosbox_pure_libretro_libnx.ini
Normal file
4
assets/romfs/assoc/dosbox_pure_libretro_libnx.ini
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/fbalpha2012_cps1_libretro_libnx.nro
|
||||
supported_extensions=zip
|
||||
@@ -1,3 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/fbalpha2012_cps2_libretro_libnx.nro
|
||||
supported_extensions=zip
|
||||
@@ -1,3 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/fbalpha2012_libretro_libnx.nro
|
||||
supported_extensions=iso|zip|7z
|
||||
@@ -1,3 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/fbalpha2012_neogeo_libretro_libnx.nro
|
||||
supported_extensions=zip
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -1,3 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/gong_libretro_libnx.nro
|
||||
supported_extensions=
|
||||
4
assets/romfs/assoc/gpsp_libretro_libnx.ini
Normal file
4
assets/romfs/assoc/gpsp_libretro_libnx.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
[config]
|
||||
path=/retroarch/cores/gpsp_libretro_libnx.nro
|
||||
supported_extensions=gba|bin
|
||||
database=Nintendo - Game Boy Advance
|
||||
@@ -1,4 +1,4 @@
|
||||
[config]
|
||||
path=/retroarch/cores/handy_libretro_libnx.nro
|
||||
supported_extensions=lnx|o
|
||||
supported_extensions=lnx|lyx|o
|
||||
database=Atari - Lynx
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[config]
|
||||
path=/retroarch/cores/mame2000_libretro_libnx.nro
|
||||
supported_extensions=zip|7z|chd
|
||||
supported_extensions=zip|7z
|
||||
database=MAME 2000
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/minivmac_libretro_libnx.nro
|
||||
supported_extensions=dsk|img|zip|hvf|cmd
|
||||
4
assets/romfs/assoc/mrboom_libretro_libnx.ini
Normal file
4
assets/romfs/assoc/mrboom_libretro_libnx.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
[config]
|
||||
path=/retroarch/cores/mrboom_libretro_libnx.nro
|
||||
supported_extensions=desktop
|
||||
database=MrBoom
|
||||
@@ -1,3 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/mu_libretro_libnx.nro
|
||||
supported_extensions=prc|pqa|img|pdb|zip
|
||||
@@ -1,3 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/numero_libretro_libnx.nro
|
||||
supported_extensions=8xp|8xk|8xg
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/pocketcdg_libretro_libnx.nro
|
||||
supported_extensions=cdg
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
[config]
|
||||
path=/retroarch/cores/quasi88_libretro_libnx.nro
|
||||
supported_extensions=d88|u88|m3u
|
||||
database=NEC - PC-8001 - PC-8801
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[config]
|
||||
path=/retroarch/cores/retro8_libretro_libnx.nro
|
||||
supported_extensions=p8|png
|
||||
database=PICO8
|
||||
database=PICO-8
|
||||
|
||||
@@ -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
|
||||
@@ -1,3 +0,0 @@
|
||||
[config]
|
||||
path=/retroarch/cores/superbroswar_libretro_libnx.nro
|
||||
supported_extensions=game
|
||||
4
assets/romfs/assoc/vircon32_libretro_libnx.ini
Normal file
4
assets/romfs/assoc/vircon32_libretro_libnx.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
[config]
|
||||
path=/retroarch/cores/vircon32_libretro_libnx.nro
|
||||
supported_extensions=v32|V32
|
||||
database=Vircon32
|
||||
@@ -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
|
||||
|
||||
@@ -1,114 +1,230 @@
|
||||
{
|
||||
"Launch": "Start",
|
||||
"[Applet Mode]": "[Applet-Modus]",
|
||||
"No Internet": "Keine Internetverbindung",
|
||||
"Files": "Dateien",
|
||||
"Apps": "Apps",
|
||||
"Store": "Store",
|
||||
"Menu": "Menü",
|
||||
"Options": "Optionen",
|
||||
"Homebrew Options": "Homebrew-Optionen",
|
||||
"Sort By": "Sortieren nach",
|
||||
"Sort Options": "Sortieroptionen",
|
||||
"Updated": "Aktualisiert",
|
||||
"Size": "Größe",
|
||||
"Alphabetical": "Alphabetisch",
|
||||
"Decending": "Absteigend",
|
||||
"Ascending": "Aufsteigend",
|
||||
"Sort": "Sortieren",
|
||||
"Order": "Befehl",
|
||||
"Info": "Info",
|
||||
"Delete": "Löschen",
|
||||
"Hide Sphaira": "Sphaira verstecken",
|
||||
"Are you sure you want to delete ": "Mit dem Löschvorgang fortfahren?",
|
||||
"Install Forwarder": "Forwarder installieren",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ACHTUNG: Die Installation von Forwardern führt zu einem Ban!",
|
||||
"OK": "OK",
|
||||
"Back": "Zurück",
|
||||
"Install": "Installieren",
|
||||
"Fs": "Fs",
|
||||
"App": "App",
|
||||
"Menu": "Menu",
|
||||
"Homebrew": "Homebrew",
|
||||
"FileBrowser": "DateiBrowser",
|
||||
"Select": "Auswählen",
|
||||
"Open": "Öffnen",
|
||||
"Theme Options": "Themenoptionen",
|
||||
"Select Theme": "Wählen Sie Theme aus",
|
||||
"Shuffle": "Shuffle",
|
||||
"Music": "Musik",
|
||||
"Show Hidden": "Versteckte anzeigen",
|
||||
"Folders First": "Ordner zuerst",
|
||||
"Hidden Last": "Zuletzt versteckt",
|
||||
"Launch": "Starten",
|
||||
"Info": "Info",
|
||||
"Install": "Installieren",
|
||||
"Delete": "Löschen",
|
||||
"Restart": "Neustart",
|
||||
"Changelog": "Changelog",
|
||||
"Details": "Details",
|
||||
"Update": "Update",
|
||||
"Remove": "Entfernen",
|
||||
"Download": "Download",
|
||||
"Next Page": "Nächste Seite",
|
||||
"Prev Page": "Vorherige Seite",
|
||||
"Unstar": "Favorit entfernen",
|
||||
"Star": "Favorit",
|
||||
"System memory": "System-Speicher",
|
||||
"microSD card": "microSD-Karte",
|
||||
"Yes": "Ja",
|
||||
"No": "Nein",
|
||||
"Network Options": "Netzwerkoptionen",
|
||||
"Enabled": "Aktiviert",
|
||||
"Disabled": "Deaktiviert",
|
||||
|
||||
"Sort By": "Sortieren nach",
|
||||
"Sort Options": "Sortieroptionen",
|
||||
"Filter": "Filter",
|
||||
"Sort": "Sortieren",
|
||||
"Order": "Reihenfolge",
|
||||
"Search": "Suchen",
|
||||
"Updated": "Aktualisiert",
|
||||
"Updated (Star)": "Aktualisiert (Favoriten)",
|
||||
"Downloads": "Downloads",
|
||||
"Size": "Größe",
|
||||
"Size (Star)": "Größe (Favoriten)",
|
||||
"Alphabetical": "Alphabetisch",
|
||||
"Alphabetical (Star)": "Alphabetisch (Favoriten)",
|
||||
"Likes": "Likes",
|
||||
"ID": "ID",
|
||||
"Decending": "Absteigend",
|
||||
"Descending (down)": "Absteigend",
|
||||
"Desc": "Abst.",
|
||||
"Ascending": "Aufsteigend",
|
||||
"Ascending (Up)": "Aufsteigend",
|
||||
"Asc": "Aufst.",
|
||||
|
||||
"Menu Options": "Menü-Optionen",
|
||||
"Header": "Header",
|
||||
"Theme": "Theme",
|
||||
"Theme Options": "Theme-Optionen",
|
||||
"Select Theme": "Theme auswählen",
|
||||
"Shuffle": "Zufällig",
|
||||
"Music": "Musik",
|
||||
"Network": "Netzwerk",
|
||||
"Network Options": "Netzwerk-Optionen",
|
||||
"Nxlink": "Nxlink",
|
||||
"Check for update": "Nach Updates suchen",
|
||||
"File Options": "Dateioptionen",
|
||||
"Nxlink Connected": "Nxlink verbunden",
|
||||
"Nxlink Upload": "Nxlink Upload",
|
||||
"Nxlink Finished": "Nxlink abgeschlossen",
|
||||
"Switch-Handheld!": "Switch-Handheld!",
|
||||
"Switch-Docked!": "Switch-Dock-Modus!",
|
||||
"Language": "Sprache",
|
||||
"Auto": "Auto",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "Logging",
|
||||
"Replace hbmenu on exit": "hbmenu beim Beenden ersetzen",
|
||||
"Misc": "Sonstiges",
|
||||
"Misc Options": "Weitere Optionen",
|
||||
"Web": "Web",
|
||||
"Install forwarders": "Forwarder installieren",
|
||||
"Install location": "Installationsort",
|
||||
"Show install warning": "Installationswarnung anzeigen",
|
||||
|
||||
"FileBrowser": "Datei-Browser",
|
||||
"%zd files": "%zd Dateien",
|
||||
"%zd dirs": "%zd Ordner",
|
||||
"File Options": "Datei-Optionen",
|
||||
"Show Hidden": "Versteckte anzeigen",
|
||||
"Folders First": "Ordner zuerst",
|
||||
"Hidden Last": "Versteckte zuletzt",
|
||||
"Cut": "Ausschneiden",
|
||||
"Copy": "Kopieren",
|
||||
"Paste": "Einfügen",
|
||||
"Paste ": "Einfügen ",
|
||||
" file(s)?": " Datei(en)?",
|
||||
"Rename": "Umbenennen",
|
||||
"Set New File Name": "Neuen Dateinamen eingeben",
|
||||
"Advanced": "Erweitert",
|
||||
"Advanced Options": "Erweiterte Optionen",
|
||||
"Create File": "Datei erstellen",
|
||||
"Set File Name": "Dateinamen eingeben",
|
||||
"Create Folder": "Ordner erstellen",
|
||||
"View as text": "Als Text anzeigen",
|
||||
"View as text (unfinished)": "Als Text anzeigen (unfertig)",
|
||||
"Set Archive Bit": "Archivbit setzen",
|
||||
"Set Folder Name": "Ordnernamen eingeben",
|
||||
"View as text (unfinished)": "Als Text anzeigen (Beta)",
|
||||
"Empty...": "Leer...",
|
||||
"Open with DayBreak?": "Mit DayBreak öffnen?",
|
||||
"Launch ": "Starten ",
|
||||
"Launch option for: ": "Startoption für: ",
|
||||
"Select launcher for: ": "Launcher auswählen für: ",
|
||||
|
||||
"Homebrew": "Homebrew",
|
||||
"Homebrew Options": "Homebrew-Optionen",
|
||||
"Hide Sphaira": "Sphaira ausblenden",
|
||||
"Install Forwarder": "Forwarder installieren",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "WARNUNG: Installation von Forwardern führt zum Ban!",
|
||||
"Installing Forwarder": "Installiere Forwarder",
|
||||
"Creating Program": "Erstelle Programm",
|
||||
"Creating Control": "Erstelle Control",
|
||||
"Creating Meta": "Erstelle Meta",
|
||||
"Writing Nca": "Schreibe NCA",
|
||||
"Updating ncm databse": "Aktualisiere NCM-Datenbank",
|
||||
"Pushing application record": "Übertrage Anwendungsdaten",
|
||||
"Installed!": "Installiert!",
|
||||
"Failed to install forwarder": "Forwarder-Installation fehlgeschlagen",
|
||||
"Unstarred ": "Favorit entfernt ",
|
||||
"Starred ": "Favorit hinzugefügt ",
|
||||
|
||||
"AppStore": "AppStore",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filter: %s | Sortierung: %s | Reihenfolge: %s",
|
||||
"AppStore Options": "AppStore-Optionen",
|
||||
"All": "Alle",
|
||||
"Games": "Spiele",
|
||||
"Emulators": "Emulatoren",
|
||||
"Tools": "Werkzeuge",
|
||||
"Advanced": "Erweitert",
|
||||
"Tools": "Tools",
|
||||
"Themes": "Themes",
|
||||
"Legacy": "Legacy",
|
||||
"Misc": "Sonstiges",
|
||||
"Downloads": "Downloads",
|
||||
"Filter": "Filter",
|
||||
"Search": "Suchen",
|
||||
"Menu Options": "Menüoptionen",
|
||||
"Header": "Header",
|
||||
"Theme": "Theme",
|
||||
"Network": "Netzwerk",
|
||||
"Logging": "Logging",
|
||||
"Enabled": "Aktiviert",
|
||||
"Disabled": "Deaktiviert",
|
||||
"Replace hbmenu on exit": "Ersetzen Sie hbmenu beim Beenden",
|
||||
"Misc Options": "Verschiedene Optionen",
|
||||
"Themezer": "Themezer",
|
||||
"Irs": "Irs",
|
||||
"Web": "Web",
|
||||
"Download": "Herunterladen",
|
||||
"Next Page": "Nächste Seite",
|
||||
"Prev Page": "Vorherige Seite",
|
||||
"Pad ": "Unterlage ",
|
||||
"version: %s": "Version: %s",
|
||||
"updated: %s": "Aktualisiert: %s",
|
||||
"category: %s": "Kategorie: %s",
|
||||
"extracted: %.2f MiB": "Entpackt: %.2f MiB",
|
||||
"app_dls: %s": "Downloads: %s",
|
||||
"More by Author": "Mehr vom Entwickler",
|
||||
"Leave Feedback": "Feedback geben",
|
||||
|
||||
"Irs": "IR-Sensor",
|
||||
"Ambient Noise Level: ": "Umgebungsrauschen: ",
|
||||
"Controller": "Controller",
|
||||
"Pad ": "Pad ",
|
||||
" (Available)": " (Verfügbar)",
|
||||
" (Unsupported)": " (Nicht unterstützt)",
|
||||
" (Unconnected)": " (Nicht verbunden)",
|
||||
"HandHeld": "Handheld",
|
||||
" (Available)": " (Verfügbar)",
|
||||
"0 (Sideways)": "0 (Seitwärts)",
|
||||
"90 (Flat)": "90 (flach)",
|
||||
"180 (-Sideways)": "180 (-Seitwärts)",
|
||||
"270 (Upside down)": "270 (verkehrt herum)",
|
||||
"Rotation": "Rotation",
|
||||
"0 (Sideways)": "0° (Seitlich)",
|
||||
"90 (Flat)": "90° (Flach)",
|
||||
"180 (-Sideways)": "180° (-Seitlich)",
|
||||
"270 (Upside down)": "270° (Kopfüber)",
|
||||
"Colour": "Farbe",
|
||||
"Grey": "Grau",
|
||||
"Ironbow": "Eisenbogen",
|
||||
"Ironbow": "Ironbow",
|
||||
"Green": "Grün",
|
||||
"Red": "Rot",
|
||||
"Blue": "Blau",
|
||||
"Light Target": "Lichtziel",
|
||||
"All leds": "Alle LEDs",
|
||||
"Bright group": "Helle Gruppe",
|
||||
"Dim group": "Dunkle Gruppe",
|
||||
"None": "Keiner",
|
||||
"Normal image": "Normales Bild",
|
||||
"Negative image": "Negatives Bild",
|
||||
"320x240": "320x240",
|
||||
"160x120": "160x120",
|
||||
"80x60": "80x60",
|
||||
"40x30": "40x30",
|
||||
"20x15": "20x15",
|
||||
"Controller": "Controller",
|
||||
"Rotation": "Drehung",
|
||||
"Colour": "Farbe",
|
||||
"Light Target": "Leichtes Ziel",
|
||||
"Gain": "Gain",
|
||||
"Negative Image": "Negatives Bild",
|
||||
"None": "Keine",
|
||||
"Gain": "Verstärkung",
|
||||
"Negative Image": "Negativ-Bild",
|
||||
"Normal image": "Normal-Bild",
|
||||
"Negative image": "Negativ-Bild",
|
||||
"Format": "Format",
|
||||
"Trimming Format": "Zuschneideformat",
|
||||
"External Light Filter": "Externer Lichtfilter",
|
||||
"Load Default": "Standardoptionen laden",
|
||||
"No Internet": "Kein Internet",
|
||||
"[Applet Mode]": "[Applet-Modus]",
|
||||
"Language": "Sprache"
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "Beschnitt-Format",
|
||||
"External Light Filter": "Externes Lichtfilter",
|
||||
"Load Default": "Standard laden",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Themezer-Optionen",
|
||||
"Nsfw": "NSFW",
|
||||
"Page": "Seite",
|
||||
"Page %zu / %zu": "Seite %zu / %zu",
|
||||
"Enter Page Number": "Seitenzahl eingeben",
|
||||
"Bad Page": "Ungültige Seite",
|
||||
"Download theme?": "Theme herunterladen?",
|
||||
|
||||
"Installing ": "Installiere ",
|
||||
"Uninstalling ": "Deinstalliere ",
|
||||
"Deleting ": "Lösche ",
|
||||
"Deleting": "Lösche",
|
||||
"Pasting ": "Füge ein ",
|
||||
"Pasting": "Füge ein",
|
||||
"Removing ": "Entferne ",
|
||||
"Scanning ": "Scanne ",
|
||||
"Creating ": "Erstelle ",
|
||||
"Copying ": "Kopiere ",
|
||||
"Trying to load ": "Lade ",
|
||||
"Downloading ": "Lade herunter ",
|
||||
"Checking MD5": "Prüfe MD5",
|
||||
"Loading...": "Lade...",
|
||||
"Loading": "Lade",
|
||||
"Empty!": "Leer!",
|
||||
"Not Ready...": "Nicht bereit...",
|
||||
"Error loading page!": "Fehler beim Laden!",
|
||||
"Update avaliable: ": "Update verfügbar: ",
|
||||
"Download update: ": "Update herunterladen: ",
|
||||
"Updated to ": "Aktualisiert auf ",
|
||||
"Restart Sphaira?": "Sphaira neustarten?",
|
||||
"Failed to download update": "Update-Download fehlgeschlagen",
|
||||
"Delete Selected files?": "Ausgewählte Dateien löschen?",
|
||||
"Completely remove ": "Vollständig entfernen ",
|
||||
"Are you sure you want to delete ": "Wirklich löschen ",
|
||||
"Are you sure you wish to cancel?": "Wirklich abbrechen?",
|
||||
"If this message appears repeatedly, please open an issue.": "Bei wiederholtem Auftreten bitte Issue erstellen."
|
||||
}
|
||||
|
||||
@@ -1,114 +1,230 @@
|
||||
{
|
||||
"Launch" : "Launch",
|
||||
"Options" : "Options",
|
||||
"Homebrew Options" : "Homebrew Options",
|
||||
"Sort By" : "Sort By",
|
||||
"Sort Options" : "Sort Options",
|
||||
"Updated" : "Updated",
|
||||
"Size" : "Size",
|
||||
"Alphabetical" : "Alphabetical",
|
||||
"Decending" : "Decending",
|
||||
"Ascending" : "Ascending",
|
||||
"Sort" : "Sort",
|
||||
"Order" : "Order",
|
||||
"Info" : "Info",
|
||||
"Delete" : "Delete",
|
||||
"Hide Sphaira" : "Hide Sphaira",
|
||||
"Are you sure you want to delete " : "Are you sure you want to delete ",
|
||||
"Install Forwarder" : "Install Forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!" : "WARNING: Installing forwarders will lead to a ban!",
|
||||
"Back" : "Back",
|
||||
"Install" : "Install",
|
||||
"Fs" : "Fs",
|
||||
"App" : "App",
|
||||
"Menu" : "Menu",
|
||||
"Homebrew" : "Homebrew",
|
||||
"FileBrowser" : "FileBrowser",
|
||||
"Open" : "Open",
|
||||
"Theme Options" : "Theme Options",
|
||||
"Select Theme" : "Select Theme",
|
||||
"Shuffle" : "Shuffle",
|
||||
"Music" : "Music",
|
||||
"Show Hidden" : "Show Hidden",
|
||||
"Folders First" : "Folders First",
|
||||
"Hidden Last" : "Hidden Last",
|
||||
"Yes" : "Yes",
|
||||
"No" : "No",
|
||||
"Network Options" : "Network Options",
|
||||
"Nxlink" : "Nxlink",
|
||||
"Check for update" : "Check for update",
|
||||
"File Options" : "File Options",
|
||||
"Cut" : "Cut",
|
||||
"Copy" : "Copy",
|
||||
"Rename" : "Rename",
|
||||
"Advanced Options" : "Create File",
|
||||
"Create File" : "Create File",
|
||||
"Create Folder" : "Create Folder",
|
||||
"View as text" : "View as text",
|
||||
"View as text (unfinished)" : "View as text (unfinished)",
|
||||
"Set Archive Bit" : "Set Archive Bit",
|
||||
"AppStore Options" : "AppStore Options",
|
||||
"All" : "All",
|
||||
"Games" : "Games",
|
||||
"Emulators" : "Emulators",
|
||||
"Tools" : "Tools",
|
||||
"Advanced" : "Advanced",
|
||||
"Themes" : "Themes",
|
||||
"Legacy" : "Legacy",
|
||||
"Misc" : "Misc",
|
||||
"Downloads" : "Downloads",
|
||||
"Filter" : "Filter",
|
||||
"Search" : "Search",
|
||||
"Menu Options" : "Menu Options",
|
||||
"Header" : "Header",
|
||||
"Theme" : "Theme",
|
||||
"Network" : "Network",
|
||||
"Logging" : "Logging",
|
||||
"Enabled" : "Enabled",
|
||||
"Disabled" : "Disabled",
|
||||
"Replace hbmenu on exit" : "Replace hbmenu on exit",
|
||||
"Misc Options" : "Misc Options",
|
||||
"Themezer" : "Themezer",
|
||||
"Irs" : "Irs",
|
||||
"Web" : "Web",
|
||||
"Download" : "Download",
|
||||
"Next Page" : "Next Page",
|
||||
"Prev Page" : "Prev Page",
|
||||
"Pad " : "Pad ",
|
||||
" (Unconnected)" : " (Unconnected)",
|
||||
"HandHeld" : "HandHeld",
|
||||
" (Available)" : " (Available)",
|
||||
"0 (Sideways)" : "0 (Sideways)",
|
||||
"90 (Flat)" : "90 (Flat)",
|
||||
"180 (-Sideways)" : "180 (-Sideways)",
|
||||
"270 (Upside down)" : "270 (Upside down)",
|
||||
"Grey" : "Grey",
|
||||
"Ironbow" : "Ironbow",
|
||||
"Green" : "Green",
|
||||
"Red" : "Red",
|
||||
"Blue" : "Blue",
|
||||
"All leds" : "All leds",
|
||||
"Bright group" : "Bright group",
|
||||
"Dim group" : "Dim group",
|
||||
"None" : "None",
|
||||
"Normal image" : "Normal image",
|
||||
"Negative image" : "Negative image",
|
||||
"320x240" : "320x240",
|
||||
"160x120" : "160x120",
|
||||
"80x60" : "80x60",
|
||||
"40x30" : "40x30",
|
||||
"20x15" : "20x15",
|
||||
"Controller" : "Controller",
|
||||
"Rotation" : "Rotation",
|
||||
"Colour" : "Colour",
|
||||
"Light Target" : "Light Target",
|
||||
"Gain" : "Gain",
|
||||
"Negative Image" : "Negative Image",
|
||||
"Format" : "Format",
|
||||
"Trimming Format" : "Trimming Format",
|
||||
"External Light Filter" : "External Light Filter",
|
||||
"Load Default" : "Load Default",
|
||||
"No Internet" : "No Internet",
|
||||
"[Applet Mode]" : "[Applet Mode]",
|
||||
"Language": "Language"
|
||||
}
|
||||
"[Applet Mode]": "[Applet Mode]",
|
||||
"No Internet": "No Internet",
|
||||
"Files": "Files",
|
||||
"Apps": "Apps",
|
||||
"Store": "Store",
|
||||
"Menu": "Menu",
|
||||
"Options": "Options",
|
||||
"OK": "OK",
|
||||
"Back": "Back",
|
||||
"Select": "Select",
|
||||
"Open": "Open",
|
||||
"Launch": "Launch",
|
||||
"Info": "Info",
|
||||
"Install": "Install",
|
||||
"Delete": "Delete",
|
||||
"Restart": "Restart",
|
||||
"Changelog": "Changelog",
|
||||
"Details": "Details",
|
||||
"Update": "Update",
|
||||
"Remove": "Remove",
|
||||
"Download": "Download",
|
||||
"Next Page": "Next Page",
|
||||
"Prev Page": "Prev Page",
|
||||
"Unstar": "Unstar",
|
||||
"Star": "Star",
|
||||
"System memory": "System memory",
|
||||
"microSD card": "microSD card",
|
||||
"Yes": "Yes",
|
||||
"No": "No",
|
||||
"Enabled": "Enabled",
|
||||
"Disabled": "Disabled",
|
||||
|
||||
"Sort By": "Sort By",
|
||||
"Sort Options": "Sort Options",
|
||||
"Filter": "Filter",
|
||||
"Sort": "Sort",
|
||||
"Order": "Order",
|
||||
"Search": "Search",
|
||||
"Updated": "Updated",
|
||||
"Updated (Star)": "Updated (Star)",
|
||||
"Downloads": "Downloads",
|
||||
"Size": "Size",
|
||||
"Size (Star)": "Size (Star)",
|
||||
"Alphabetical": "Alphabetical",
|
||||
"Alphabetical (Star)": "Alphabetical (Star)",
|
||||
"Likes": "Likes",
|
||||
"ID": "ID",
|
||||
"Decending": "Decending",
|
||||
"Descending (down)": "Descending (down)",
|
||||
"Desc": "Desc",
|
||||
"Ascending": "Ascending",
|
||||
"Ascending (Up)": "Ascending (Up)",
|
||||
"Asc": "Asc",
|
||||
|
||||
"Menu Options": "Menu Options",
|
||||
"Header": "Header",
|
||||
"Theme": "Theme",
|
||||
"Theme Options": "Theme Options",
|
||||
"Select Theme": "Select Theme",
|
||||
"Shuffle": "Shuffle",
|
||||
"Music": "Music",
|
||||
"Network": "Network",
|
||||
"Network Options": "Network Options",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "Nxlink Connected",
|
||||
"Nxlink Upload": "Nxlink Upload",
|
||||
"Nxlink Finished": "Nxlink Finished",
|
||||
"Switch-Handheld!": "Switch-Handheld!",
|
||||
"Switch-Docked!": "Switch-Docked!",
|
||||
"Language": "Language",
|
||||
"Auto": "Auto",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "Logging",
|
||||
"Replace hbmenu on exit": "Replace hbmenu on exit",
|
||||
"Misc": "Misc",
|
||||
"Misc Options": "Misc Options",
|
||||
"Web": "Web",
|
||||
"Install forwarders": "Install forwarders",
|
||||
"Install location": "Install location",
|
||||
"Show install warning": "Show install warning",
|
||||
|
||||
"FileBrowser": "FileBrowser",
|
||||
"%zd files": "%zd files",
|
||||
"%zd dirs": "%zd dirs",
|
||||
"File Options": "File Options",
|
||||
"Show Hidden": "Show Hidden",
|
||||
"Folders First": "Folders First",
|
||||
"Hidden Last": "Hidden Last",
|
||||
"Cut": "Cut",
|
||||
"Copy": "Copy",
|
||||
"Paste": "Paste",
|
||||
"Paste ": "Paste ",
|
||||
" file(s)?": " file(s)?",
|
||||
"Rename": "Rename",
|
||||
"Set New File Name": "Set New File Name",
|
||||
"Advanced": "Advanced",
|
||||
"Advanced Options": "Advanced Options",
|
||||
"Create File": "Create File",
|
||||
"Set File Name": "Set File Name",
|
||||
"Create Folder": "Create Folder",
|
||||
"Set Folder Name": "Set Folder Name",
|
||||
"View as text (unfinished)": "View as text (unfinished)",
|
||||
"Empty...": "Empty...",
|
||||
"Open with DayBreak?": "Open with DayBreak?",
|
||||
"Launch ": "Launch ",
|
||||
"Launch option for: ": "Launch option for: ",
|
||||
"Select launcher for: ": "Select launcher for: ",
|
||||
|
||||
"Homebrew": "Homebrew",
|
||||
"Homebrew Options": "Homebrew Options",
|
||||
"Hide Sphaira": "Hide Sphaira",
|
||||
"Install Forwarder": "Install Forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "WARNING: Installing forwarders will lead to a ban!",
|
||||
"Installing Forwarder": "Installing Forwarder",
|
||||
"Creating Program": "Creating Program",
|
||||
"Creating Control": "Creating Control",
|
||||
"Creating Meta": "Creating Meta",
|
||||
"Writing Nca": "Writing Nca",
|
||||
"Updating ncm databse": "Updating ncm databse",
|
||||
"Pushing application record": "Pushing application record",
|
||||
"Installed!": "Installed!",
|
||||
"Failed to install forwarder": "Failed to install forwarder",
|
||||
"Unstarred ": "Unstarred ",
|
||||
"Starred ": "Starred ",
|
||||
|
||||
"AppStore": "AppStore",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filter: %s | Sort: %s | Order: %s",
|
||||
"AppStore Options": "AppStore Options",
|
||||
"All": "All",
|
||||
"Games": "Games",
|
||||
"Emulators": "Emulators",
|
||||
"Tools": "Tools",
|
||||
"Themes": "Themes",
|
||||
"Legacy": "Legacy",
|
||||
"version: %s": "version: %s",
|
||||
"updated: %s": "updated: %s",
|
||||
"category: %s": "category: %s",
|
||||
"extracted: %.2f MiB": "extracted: %.2f MiB",
|
||||
"app_dls: %s": "app_dls: %s",
|
||||
"More by Author": "More by Author",
|
||||
"Leave Feedback": "Leave Feedback",
|
||||
|
||||
"Irs": "Irs",
|
||||
"Ambient Noise Level: ": "Ambient Noise Level: ",
|
||||
"Controller": "Controller",
|
||||
"Pad ": "Pad ",
|
||||
" (Available)": " (Available)",
|
||||
" (Unsupported)": " (Unsupported)",
|
||||
" (Unconnected)": " (Unconnected)",
|
||||
"HandHeld": "HandHeld",
|
||||
"Rotation": "Rotation",
|
||||
"0 (Sideways)": "0 (Sideways)",
|
||||
"90 (Flat)": "90 (Flat)",
|
||||
"180 (-Sideways)": "180 (-Sideways)",
|
||||
"270 (Upside down)": "270 (Upside down)",
|
||||
"Colour": "Colour",
|
||||
"Grey": "Grey",
|
||||
"Ironbow": "Ironbow",
|
||||
"Green": "Green",
|
||||
"Red": "Red",
|
||||
"Blue": "Blue",
|
||||
"Light Target": "Light Target",
|
||||
"All leds": "All leds",
|
||||
"Bright group": "Bright group",
|
||||
"Dim group": "Dim group",
|
||||
"None": "None",
|
||||
"Gain": "Gain",
|
||||
"Negative Image": "Negative Image",
|
||||
"Normal image": "Normal image",
|
||||
"Negative image": "Negative image",
|
||||
"Format": "Format",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "Trimming Format",
|
||||
"External Light Filter": "External Light Filter",
|
||||
"Load Default": "Load Default",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Themezer Options",
|
||||
"Nsfw": "Nsfw",
|
||||
"Page": "Page",
|
||||
"Page %zu / %zu": "Page %zu / %zu",
|
||||
"Enter Page Number": "Enter Page Number",
|
||||
"Bad Page": "Bad Page",
|
||||
"Download theme?": "Download theme?",
|
||||
|
||||
"Installing ": "Installing ",
|
||||
"Uninstalling ": "Uninstalling ",
|
||||
"Deleting ": "Deleting ",
|
||||
"Deleting": "Deleting",
|
||||
"Pasting ": "Pasting ",
|
||||
"Pasting": "Pasting",
|
||||
"Removing ": "Removing ",
|
||||
"Scanning ": "Scanning ",
|
||||
"Creating ": "Creating ",
|
||||
"Copying ": "Copying ",
|
||||
"Trying to load ": "Trying to load ",
|
||||
"Downloading ": "Downloading ",
|
||||
"Checking MD5": "Checking MD5",
|
||||
"Loading...": "Loading...",
|
||||
"Loading": "Loading",
|
||||
"Empty!": "Empty!",
|
||||
"Not Ready...": "Not Ready...",
|
||||
"Error loading page!": "Error loading page!",
|
||||
"Update avaliable: ": "Update avaliable: ",
|
||||
"Download update: ": "Download update: ",
|
||||
"Updated to ": "Updated to ",
|
||||
"Restart Sphaira?": "Restart Sphaira?",
|
||||
"Failed to download update": "Failed to download update",
|
||||
"Delete Selected files?": "Delete Selected files?",
|
||||
"Completely remove ": "Completely remove ",
|
||||
"Are you sure you want to delete ": "Are you sure you want to delete ",
|
||||
"Are you sure you wish to cancel?": "Are you sure you wish to cancel?",
|
||||
"If this message appears repeatedly, please open an issue.": "If this message appears repeatedly, please open an issue."
|
||||
}
|
||||
@@ -1,114 +1,230 @@
|
||||
{
|
||||
"Launch": "Lanzamiento",
|
||||
"Options": "Opciones",
|
||||
"Homebrew Options": "Opciones de elaboración casera",
|
||||
"Sort By": "Ordenar por",
|
||||
"Sort Options": "Opciones de clasificación",
|
||||
"Updated": "Actualizado",
|
||||
"Size": "Tamaño",
|
||||
"Alphabetical": "Alfabético",
|
||||
"Decending": "Descendente",
|
||||
"Ascending": "Ascendente",
|
||||
"Sort": "Clasificar",
|
||||
"Order": "Orden",
|
||||
"Info": "Información",
|
||||
"Delete": "Borrar",
|
||||
"Hide Sphaira": "Ocultar Sphaira",
|
||||
"Are you sure you want to delete ": "¿Estás seguro de que quieres eliminar? ",
|
||||
"Install Forwarder": "Instalar reenviador",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ADVERTENCIA: ¡La instalación de reenviadores dará lugar a una prohibición!",
|
||||
"Back": "Atrás",
|
||||
"Install": "Instalar",
|
||||
"Fs": "fs",
|
||||
"App": "Aplicación",
|
||||
"[Applet Mode]": "[Modo Applet]",
|
||||
"No Internet": "Sin Internet",
|
||||
"Files": "Archivos",
|
||||
"Apps": "Apps",
|
||||
"Store": "Tienda",
|
||||
"Menu": "Menú",
|
||||
"Homebrew": "cerveza casera",
|
||||
"FileBrowser": "Explorador de archivos",
|
||||
"Options": "Opciones",
|
||||
"OK": "OK",
|
||||
"Back": "Atrás",
|
||||
"Select": "Seleccionar",
|
||||
"Open": "Abierto",
|
||||
"Theme Options": "Opciones de tema",
|
||||
"Select Theme": "Seleccionar tema",
|
||||
"Shuffle": "Barajar",
|
||||
"Music": "Música",
|
||||
"Show Hidden": "Mostrar oculto",
|
||||
"Folders First": "Carpetas primero",
|
||||
"Hidden Last": "Oculto último",
|
||||
"Launch": "Ejecutar",
|
||||
"Info": "Información",
|
||||
"Install": "Instalar",
|
||||
"Delete": "Borrar",
|
||||
"Restart": "",
|
||||
"Changelog": "Log de Cambios",
|
||||
"Details": "Detalles",
|
||||
"Update": "Actualizar",
|
||||
"Remove": "Borrar",
|
||||
"Download": "Descargar",
|
||||
"Next Page": "Página siguiente",
|
||||
"Prev Page": "Página anterior",
|
||||
"Unstar": "",
|
||||
"Star": "",
|
||||
"System memory": "",
|
||||
"microSD card": "",
|
||||
"Yes": "Sí",
|
||||
"No": "No",
|
||||
"Network Options": "Opciones de red",
|
||||
"Nxlink": "nxenlace",
|
||||
"Check for update": "Buscar actualizaciones",
|
||||
"File Options": "Opciones de archivo",
|
||||
"Cut": "Cortar",
|
||||
"Enabled": "Activado",
|
||||
"Disabled": "Desactivado",
|
||||
|
||||
"Sort By": "Ordenar por",
|
||||
"Sort Options": "Opciones de clasificación",
|
||||
"Filter": "Filtrar",
|
||||
"Sort": "Clasificar",
|
||||
"Order": "Orden",
|
||||
"Search": "Buscar",
|
||||
"Updated": "Actualizado",
|
||||
"Updated (Star)": "Actualizado (Star)",
|
||||
"Downloads": "Descargas",
|
||||
"Size": "Tamaño",
|
||||
"Size (Star)": "Tamaño (Star)",
|
||||
"Alphabetical": "Alfabético",
|
||||
"Alphabetical (Star)": "Alfabético (Star)",
|
||||
"Likes": "Me Gusta",
|
||||
"ID": "ID",
|
||||
"Decending": "Descendente",
|
||||
"Descending (down)": "Descendente (abajo)",
|
||||
"Desc": "Descendente",
|
||||
"Ascending": "Ascendente",
|
||||
"Ascending (Up)": "Ascendente (arriba)",
|
||||
"Asc": "Ascendente",
|
||||
|
||||
"Menu Options": "Opciones de Menú",
|
||||
"Header": "Encabezamiento",
|
||||
"Theme": "Tema",
|
||||
"Theme Options": "Opciones de Tema",
|
||||
"Select Theme": "Seleccionar Tema",
|
||||
"Shuffle": "Barajar",
|
||||
"Music": "Música",
|
||||
"Network": "Red",
|
||||
"Network Options": "Opciones de Red",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "Nxlink Conectado",
|
||||
"Nxlink Upload": "Nxlink Subida",
|
||||
"Nxlink Finished": "Nxlink Finalizado",
|
||||
"Switch-Handheld!": "",
|
||||
"Switch-Docked!": "",
|
||||
"Language": "Idioma",
|
||||
"Auto": "Automático",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "Explotación florestal",
|
||||
"Replace hbmenu on exit": "Reemplazar hbmenu al salir",
|
||||
"Misc": "Varios",
|
||||
"Misc Options": "Opciones varias",
|
||||
"Web": "Web",
|
||||
"Install forwarders": "",
|
||||
"Install location": "",
|
||||
"Show install warning": "",
|
||||
|
||||
"FileBrowser": "Explorador de Archivos",
|
||||
"%zd files": "%zd files",
|
||||
"%zd dirs": "%zd dirs",
|
||||
"File Options": "Opciones de Tema",
|
||||
"Show Hidden": "Mostrar Oculto",
|
||||
"Folders First": "Carpetas primero",
|
||||
"Hidden Last": "Oculto último",
|
||||
"Cut": "Cortar ",
|
||||
"Copy": "Copiar",
|
||||
"Rename": "Rebautizar",
|
||||
"Advanced Options": "Crear archivo",
|
||||
"Paste": "Pegar",
|
||||
"Paste ": "Pegar ",
|
||||
" file(s)?": " ¿archivo(s)?",
|
||||
"Rename": "Renombrar",
|
||||
"Set New File Name": "Establecer Nuevo Nombre de Archivo",
|
||||
"Advanced": "Avanzado",
|
||||
"Advanced Options": "Opciones Avanzadas",
|
||||
"Create File": "Crear archivo",
|
||||
"Set File Name": "Establecer Nombre de Archivo",
|
||||
"Create Folder": "Crear carpeta",
|
||||
"View as text": "Ver como texto",
|
||||
"Set Folder Name": "Establecer Nombre de Carpeta",
|
||||
"View as text (unfinished)": "Ver como texto (sin terminar)",
|
||||
"Set Archive Bit": "Establecer bit de archivo",
|
||||
"AppStore Options": "Opciones de la tienda de aplicaciones",
|
||||
"Empty...": "Vacío...",
|
||||
"Open with DayBreak?": "Abrir con DayBreak",
|
||||
"Launch ": "",
|
||||
"Launch option for: ": "Opción de ejecución para: ",
|
||||
"Select launcher for: ": "",
|
||||
|
||||
"Homebrew": "Honebrew",
|
||||
"Homebrew Options": "Opciones de Homebrew",
|
||||
"Hide Sphaira": "Ocultar Sphaira",
|
||||
"Install Forwarder": "Instalar Forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ADVERTENCIA: ¡La instalación de fordwarders podría producir un baneo de la consola!",
|
||||
"Installing Forwarder": "Instalando Forwarder",
|
||||
"Creating Program": "Creando Program",
|
||||
"Creating Control": "Creando Control",
|
||||
"Creating Meta": "Creando Meta",
|
||||
"Writing Nca": "Creando NCA",
|
||||
"Updating ncm databse": "Actualizando base de datos ncm ",
|
||||
"Pushing application record": "",
|
||||
"Installed!": "¡Instalado!",
|
||||
"Failed to install forwarder": "Fallo al instalar forwarder",
|
||||
"Unstarred ": "",
|
||||
"Starred ": "",
|
||||
|
||||
"AppStore": "AppStore",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filtrar: %s | Clasificar: %s | Orden: %s",
|
||||
"AppStore Options": "Opciones de la AppStore",
|
||||
"All": "Todo",
|
||||
"Games": "Juegos",
|
||||
"Emulators": "Emuladores",
|
||||
"Tools": "Herramientas",
|
||||
"Advanced": "Avanzado",
|
||||
"Themes": "Temas",
|
||||
"Legacy": "Legado",
|
||||
"Misc": "Varios",
|
||||
"Downloads": "Descargas",
|
||||
"Filter": "Filtrar",
|
||||
"Search": "Buscar",
|
||||
"Menu Options": "Opciones de menú",
|
||||
"Header": "Encabezamiento",
|
||||
"Theme": "Tema",
|
||||
"Network": "Red",
|
||||
"Logging": "Explotación florestal",
|
||||
"Enabled": "Activado",
|
||||
"Disabled": "Desactivado",
|
||||
"Replace hbmenu on exit": "Reemplazar hbmenu al salir",
|
||||
"Misc Options": "Opciones varias",
|
||||
"Themezer": "Temazer",
|
||||
"Irs": "irs",
|
||||
"Web": "Web",
|
||||
"Download": "Descargar",
|
||||
"Next Page": "Página siguiente",
|
||||
"Prev Page": "Página anterior",
|
||||
"version: %s": "version: %s",
|
||||
"updated: %s": "updated: %s",
|
||||
"category: %s": "category: %s",
|
||||
"extracted: %.2f MiB": "extracted: %.2f MiB",
|
||||
"app_dls: %s": "app_dls: %s",
|
||||
"More by Author": "Mostrar mas del Autor",
|
||||
"Leave Feedback": "Dejar Mensaje",
|
||||
|
||||
"Irs": "IRS",
|
||||
"Ambient Noise Level: ": "Nivel de Ruido",
|
||||
"Controller": "Control",
|
||||
"Pad ": "Almohadilla ",
|
||||
" (Available)": " (Disponible)",
|
||||
" (Unsupported)": "",
|
||||
" (Unconnected)": " (Desconectado)",
|
||||
"HandHeld": "Portátil",
|
||||
" (Available)": " (Disponible)",
|
||||
"0 (Sideways)": "0 (de lado)",
|
||||
"90 (Flat)": "90 (plano)",
|
||||
"180 (-Sideways)": "180 (-de lado)",
|
||||
"270 (Upside down)": "270 (al revés)",
|
||||
"Rotation": "Rotación",
|
||||
"0 (Sideways)": "0 (De Lado)",
|
||||
"90 (Flat)": "90 (Plano)",
|
||||
"180 (-Sideways)": "180 (-De Lado)",
|
||||
"270 (Upside down)": "270 (Al Revés)",
|
||||
"Colour": "Color",
|
||||
"Grey": "Gris",
|
||||
"Ironbow": "arco de hierro",
|
||||
"Ironbow": "Paleta Térmica",
|
||||
"Green": "Verde",
|
||||
"Red": "Rojo",
|
||||
"Blue": "Azul",
|
||||
"All leds": "todos los leds",
|
||||
"Bright group": "grupo brillante",
|
||||
"Dim group": "grupo tenue",
|
||||
"Light Target": "Objetivo de Luz",
|
||||
"All leds": "Todos los leds",
|
||||
"Bright group": "Grupo brillante",
|
||||
"Dim group": "Grupo tenue",
|
||||
"None": "Ninguno",
|
||||
"Normal image": "imagen normal",
|
||||
"Negative image": "Imagen negativa",
|
||||
"320x240": "320x240",
|
||||
"160x120": "160x120",
|
||||
"80x60": "80x60",
|
||||
"40x30": "40x30",
|
||||
"20x15": "20x15",
|
||||
"Controller": "Controlador",
|
||||
"Rotation": "Rotación",
|
||||
"Colour": "Color",
|
||||
"Light Target": "Objetivo de luz",
|
||||
"Gain": "Ganar",
|
||||
"Negative Image": "Imagen negativa",
|
||||
"Gain": "Ganancia",
|
||||
"Negative Image": "Imagen Negativa",
|
||||
"Normal image": "Imagen Normal",
|
||||
"Negative image": "Imagen Negativa",
|
||||
"Format": "Formato",
|
||||
"Trimming Format": "Formato de recorte",
|
||||
"External Light Filter": "Filtro de luz externo",
|
||||
"Load Default": "Cargar predeterminado",
|
||||
"No Internet": "sin internet",
|
||||
"[Applet Mode]": "[Modo subprograma]",
|
||||
"Language": "Idioma"
|
||||
}
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "Formato de Recorte",
|
||||
"External Light Filter": "Filtro de Luz Externa",
|
||||
"Load Default": "Cargar Predeterminado",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Opciones de Themezer",
|
||||
"Nsfw": "NSFW",
|
||||
"Page": "Página",
|
||||
"Page %zu / %zu": "Page %zu / %zu",
|
||||
"Enter Page Number": "Ingresar Número de Página",
|
||||
"Bad Page": "Página Errónea",
|
||||
"Download theme?": "¿Descargar Tema?",
|
||||
|
||||
"Installing ": "Instalando ",
|
||||
"Uninstalling ": "Desinstalando ",
|
||||
"Deleting ": "Borrando ",
|
||||
"Deleting": "Borrando",
|
||||
"Pasting ": "Pegando ",
|
||||
"Pasting": "Pegando",
|
||||
"Removing ": "Removiendo ",
|
||||
"Scanning ": "Escaneando ",
|
||||
"Creating ": "Creando ",
|
||||
"Copying ": "Copiando ",
|
||||
"Trying to load ": "",
|
||||
"Downloading ": "Descargando ",
|
||||
"Checking MD5": "Chqueando MD5",
|
||||
"Loading...": "Cargando...",
|
||||
"Loading": "Cargando",
|
||||
"Empty!": "¡Vacío!",
|
||||
"Not Ready...": "No Listo Aún...",
|
||||
"Error loading page!": "¡Error cargando la página!",
|
||||
"Update avaliable: ": "Actualización disponible: ",
|
||||
"Download update: ": "Descargar actualización: ",
|
||||
"Updated to ": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Failed to download update": "Fallo al descargar actualización",
|
||||
"Delete Selected files?": "¿Eliminar archivos Seleccionados?",
|
||||
"Completely remove ": "Eliminar completamente",
|
||||
"Are you sure you want to delete ": "¿Estás seguro que quieres eliminar? ",
|
||||
"Are you sure you wish to cancel?": "¿Estás seguro que deseas cancelar?",
|
||||
"If this message appears repeatedly, please open an issue.": ""
|
||||
}
|
||||
@@ -1,114 +1,230 @@
|
||||
{
|
||||
"Launch": "Exécuter",
|
||||
"[Applet Mode]": "[Mode Applet]",
|
||||
"No Internet": "Pas d'Internet",
|
||||
"Files": "Fichiers",
|
||||
"Apps": "Applications",
|
||||
"Store": "Magasin",
|
||||
"Menu": "Menu",
|
||||
"Options": "Options",
|
||||
"Homebrew Options": "Options Homebrew",
|
||||
"OK": "OK",
|
||||
"Back": "Retour",
|
||||
"Select": "Sélectionner",
|
||||
"Open": "Ouvrir",
|
||||
"Launch": "Exécuter",
|
||||
"Info": "Info.",
|
||||
"Install": "Installer",
|
||||
"Delete": "Supprimer",
|
||||
"Restart": "Redémarrer",
|
||||
"Changelog": "Changelog",
|
||||
"Details": "Détails",
|
||||
"Update": "Mise à jour",
|
||||
"Remove": "Supprimer",
|
||||
"Download": "Télécharger",
|
||||
"Next Page": "Page Suiv.",
|
||||
"Prev Page": "Page Préc.",
|
||||
"Unstar": "Retirer des favories",
|
||||
"Star": "Ajouter aux favories",
|
||||
"System memory": "Mémoire système",
|
||||
"microSD card": "Carte microSD",
|
||||
"Yes": "Oui",
|
||||
"No": "Non",
|
||||
"Enabled": "Activé(e)",
|
||||
"Disabled": "Désactivé(e)",
|
||||
|
||||
"Sort By": "Tri Par",
|
||||
"Sort Options": "Options de Tri",
|
||||
"Updated": "Mis à jour",
|
||||
"Size": "Taille",
|
||||
"Alphabetical": "Alphabétique",
|
||||
"Decending": "Décroissant",
|
||||
"Ascending": "Croissant",
|
||||
"Filter": "Filtre",
|
||||
"Sort": "Tri",
|
||||
"Order": "Ordre",
|
||||
"Info": "Info.",
|
||||
"Delete": "Supprimer",
|
||||
"Hide Sphaira": "Masquer Sphaira",
|
||||
"Are you sure you want to delete ": "Êtes-vous sûr de vouloir supprimer ",
|
||||
"Install Forwarder": "Installer le Forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ATTENTION: L'installation de forwarders entraînera un ban!",
|
||||
"Back": "Retour",
|
||||
"Install": "Installer",
|
||||
"Fs": "Fs",
|
||||
"App": "App.",
|
||||
"Menu": "Menu",
|
||||
"Homebrew": "Homebrew",
|
||||
"FileBrowser": "Navigateur de Fichiers",
|
||||
"Open": "Ouvrir",
|
||||
"Search": "Recherche",
|
||||
"Updated": "Mis à jour",
|
||||
"Updated (Star)": "Mis à jour (Favories)",
|
||||
"Downloads": "Téléchargements",
|
||||
"Size": "Taille",
|
||||
"Size (Star)": "Taille (Favories)",
|
||||
"Alphabetical": "Alphabétique",
|
||||
"Alphabetical (Star)": "Alphabétique (Favories)",
|
||||
"Likes": "Likes",
|
||||
"ID": "ID",
|
||||
"Decending": "Décroissant",
|
||||
"Descending (down)": "Décroissant",
|
||||
"Desc": "Décroissant",
|
||||
"Ascending": "Croissant",
|
||||
"Ascending (Up)": "Croissant",
|
||||
"Asc": "Croissant",
|
||||
|
||||
"Menu Options": "Options des Menus",
|
||||
"Header": "En-tête",
|
||||
"Theme": "Thème",
|
||||
"Theme Options": "Options de Thème",
|
||||
"Select Theme": "Choisir un Thème",
|
||||
"Shuffle": "Aléatoire",
|
||||
"Music": "Musique",
|
||||
"Network": "Réseau",
|
||||
"Network Options": "Options Réseau",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "Nxlink Connecté",
|
||||
"Nxlink Upload": "Nxlink téléversement",
|
||||
"Nxlink Finished": "Nxlink terminé",
|
||||
"Switch-Handheld!": "Switch-Portable",
|
||||
"Switch-Docked!": "Switch-Dockée",
|
||||
"Language": "Langue",
|
||||
"Auto": "Auto",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "Journalisation",
|
||||
"Replace hbmenu on exit": "Remplacer hbmenu quand quitté",
|
||||
"Misc": "Divers",
|
||||
"Misc Options": "Options Diverses",
|
||||
"Web": "Web",
|
||||
"Install forwarders": "Installer les Forwarders",
|
||||
"Install location": "Emplacement d'installation",
|
||||
"Show install warning": "Afficher l'avertissement d'installation",
|
||||
|
||||
"FileBrowser": "Explorateur de Fichiers",
|
||||
"%zd files": "%zd fichiers",
|
||||
"%zd dirs": "%zd dossiers",
|
||||
"File Options": "Options de Fichier",
|
||||
"Show Hidden": "Afficher Masqués",
|
||||
"Folders First": "Dossiers en Premier",
|
||||
"Hidden Last": "Masqués en Dernier",
|
||||
"Yes": "Oui",
|
||||
"No": "Non",
|
||||
"Network Options": "Options Réseau",
|
||||
"Nxlink": "Nxlink",
|
||||
"Check for update": "Vérification d'une mise à jour",
|
||||
"File Options": "Options de Fichier",
|
||||
"Cut": "Couper",
|
||||
"Copy": "Copier",
|
||||
"Paste": "Coller",
|
||||
"Paste ": "Coller ",
|
||||
" file(s)?": " fichier(s)?",
|
||||
"Rename": "Renommer",
|
||||
"Set New File Name": "Nouveau Nom Du Fichier",
|
||||
"Advanced": "Avancé",
|
||||
"Advanced Options": "Options Avancées",
|
||||
"Create File": "Créer un Fichier",
|
||||
"Set File Name": "Nommer Le Fichier",
|
||||
"Create Folder": "Créer un Dossier",
|
||||
"View as text": "Afficher sous forme de texte",
|
||||
"Set Folder Name": "Nommer Le Dossier",
|
||||
"View as text (unfinished)": "Afficher sous forme de texte (inachevé)",
|
||||
"Set Archive Bit": "Définir le Bit d'Archive",
|
||||
"Empty...": "Vide...",
|
||||
"Open with DayBreak?": "Ouvrir avec DayBreak?",
|
||||
"Launch ": "Lancer ",
|
||||
"Launch option for: ": "Option de lancement pour: ",
|
||||
"Select launcher for: ": "Sélectionner le lanceur pour: ",
|
||||
|
||||
"Homebrew": "Homebrew",
|
||||
"Homebrew Options": "Options Homebrew",
|
||||
"Hide Sphaira": "Masquer Sphaira",
|
||||
"Install Forwarder": "Installer le Forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ATTENTION: L'installation de forwarders entraînera un ban!",
|
||||
"Installing Forwarder": "Installation Du Forwarder",
|
||||
"Creating Program": "Création de Program",
|
||||
"Creating Control": "Création de Control",
|
||||
"Creating Meta": "Création de Meta",
|
||||
"Writing Nca": "Ecriture NCA",
|
||||
"Updating ncm databse": "Mise à jour de ncm databse",
|
||||
"Pushing application record": "Ajout de l'enregistrement de l'application",
|
||||
"Installed!": "Installé!",
|
||||
"Failed to install forwarder": "Echec de l'installation du forwarder",
|
||||
"Unstarred ": "Retiré des favories ",
|
||||
"Starred ": "Ajouté aux favories ",
|
||||
|
||||
"AppStore": "AppStore",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filtre: %s | Tri: %s | Ordre: %s",
|
||||
"AppStore Options": "Options de l'AppStore",
|
||||
"All": "Tous",
|
||||
"Games": "Jeux",
|
||||
"Emulators": "Émulateurs",
|
||||
"Tools": "Outils",
|
||||
"Advanced": "Avancé",
|
||||
"Themes": "Thèmes",
|
||||
"Legacy": "Legacy",
|
||||
"Misc": "Divers",
|
||||
"Downloads": "Téléchargements",
|
||||
"Filter": "Filtre",
|
||||
"Search": "Recherche",
|
||||
"Menu Options": "Options des Menus",
|
||||
"Header": "En-tête",
|
||||
"Theme": "Thème",
|
||||
"Network": "Réseau",
|
||||
"Logging": "Journalisation",
|
||||
"Enabled": "Activé(e)",
|
||||
"Disabled": "Désactivé(e)",
|
||||
"Replace hbmenu on exit": "Remplacer hbmenu en sortie",
|
||||
"Misc Options": "Options Diverses",
|
||||
"Themezer": "Themezer",
|
||||
"version: %s": "version: %s",
|
||||
"updated: %s": "Mis à jour: %s",
|
||||
"category: %s": "catégorie: %s",
|
||||
"extracted: %.2f MiB": "Extrait: %.2f MiB",
|
||||
"app_dls: %s": "app_dls: %s",
|
||||
"More by Author": "Plus de cet Auteur",
|
||||
"Leave Feedback": "Laisser un avis",
|
||||
|
||||
"Irs": "Irs",
|
||||
"Web": "Web",
|
||||
"Download": "Télécharger",
|
||||
"Next Page": "Page Suiv.",
|
||||
"Prev Page": "Page Préc.",
|
||||
"Ambient Noise Level: ": "Niveau De Bruit Ambiant: ",
|
||||
"Controller": "Contrôleur",
|
||||
"Pad ": "Manette ",
|
||||
" (Available)": " (Disponible)",
|
||||
" (Unsupported)": "Non supporté",
|
||||
" (Unconnected)": " (Non connectée)",
|
||||
"HandHeld": "Portable",
|
||||
" (Available)": " (Disponible)",
|
||||
"Rotation": "Rotation",
|
||||
"0 (Sideways)": "0 (Paysage)",
|
||||
"90 (Flat)": "90 (Portrait)",
|
||||
"180 (-Sideways)": "180 (-Paysage)",
|
||||
"270 (Upside down)": "270 (Inversé)",
|
||||
"Colour": "Couleur",
|
||||
"Grey": "Gris",
|
||||
"Ironbow": "Ironbow",
|
||||
"Green": "Vert",
|
||||
"Red": "Rouge",
|
||||
"Blue": "Bleu",
|
||||
"Light Target": "Luminosité",
|
||||
"All leds": "Toutes les LED",
|
||||
"Bright group": "Groupe lumineux",
|
||||
"Dim group": "Groupe sombre",
|
||||
"None": "Aucun",
|
||||
"Normal image": "Image normale",
|
||||
"Negative image": "Négatif",
|
||||
"320x240": "320x240",
|
||||
"160x120": "160x120",
|
||||
"80x60": "80x60",
|
||||
"40x30": "40x30",
|
||||
"20x15": "20x15",
|
||||
"Controller": "Contrôleur",
|
||||
"Rotation": "Rotation",
|
||||
"Colour": "Couleur",
|
||||
"Light Target": "Luminosité",
|
||||
"Gain": "Gain",
|
||||
"Negative Image": "Négatif",
|
||||
"Normal image": "Image normale",
|
||||
"Negative image": "Négatif",
|
||||
"Format": "Format",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "Format de Découpe",
|
||||
"External Light Filter": "Filtre de Lumière Externe",
|
||||
"Load Default": "Charger par Défaut",
|
||||
"No Internet": "Pas d'Internet",
|
||||
"[Applet Mode]": "[Mode Applet]",
|
||||
"Language": "Langue"
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Options Themezer",
|
||||
"Nsfw": "Nsfw",
|
||||
"Page": "Page",
|
||||
"Page %zu / %zu": "Page %zu / %zu",
|
||||
"Enter Page Number": "Entrez Un Numéro De Page",
|
||||
"Bad Page": "Page inexistante",
|
||||
"Download theme?": "Télécharger le thème?",
|
||||
|
||||
"Installing ": "Installation ",
|
||||
"Uninstalling ": "Désinstallation ",
|
||||
"Deleting ": "Suppression ",
|
||||
"Deleting": "Suppression",
|
||||
"Pasting ": "Coller ",
|
||||
"Pasting": "Coller",
|
||||
"Removing ": "Suppression ",
|
||||
"Scanning ": "Scan ",
|
||||
"Creating ": "Création ",
|
||||
"Copying ": "Copie ",
|
||||
"Trying to load ": "Tente de charger ",
|
||||
"Downloading ": "Téléchargement ",
|
||||
"Checking MD5": "Vérification MD5",
|
||||
"Loading...": "Chargement...",
|
||||
"Loading": "Chargement",
|
||||
"Empty!": "Vide!",
|
||||
"Not Ready...": "Pas prêt",
|
||||
"Error loading page!": "Erreur du chargement de la page!",
|
||||
"Update avaliable: ": "Mise à jour disponible: ",
|
||||
"Download update: ": "Télécharger la mise à jour: ",
|
||||
"Updated to ": "Mis à jour vers ",
|
||||
"Restart Sphaira?": "Redémarrer Sphaira?",
|
||||
"Failed to download update": "Echec du téléchargement de la mise à jour",
|
||||
"Delete Selected files?": "Supprimer les fichiers sélectionnés?",
|
||||
"Completely remove ": "Supprimer totalement ",
|
||||
"Are you sure you want to delete ": "Êtes-vous sûr de vouloir supprimer ",
|
||||
"Are you sure you wish to cancel?": "Souhaitez-vous vraiment annuler?",
|
||||
"If this message appears repeatedly, please open an issue.": "Si ce message apparait en boucle veuillez ouvrir une issue."
|
||||
}
|
||||
|
||||
@@ -1,114 +1,230 @@
|
||||
{
|
||||
"Launch": "Lancia",
|
||||
"[Applet Mode]": "[Modalità applet]",
|
||||
"No Internet": "Niente Internet",
|
||||
"Files": "",
|
||||
"Apps": "",
|
||||
"Store": "",
|
||||
"Menu": "Menu",
|
||||
"Options": "Opzioni",
|
||||
"Homebrew Options": "Opzioni Homebrew",
|
||||
"OK": "",
|
||||
"Back": "Indietro",
|
||||
"Select": "",
|
||||
"Open": "Apri",
|
||||
"Launch": "Lancia",
|
||||
"Info": "Informazioni",
|
||||
"Install": "Installa",
|
||||
"Delete": "Elimina",
|
||||
"Restart": "",
|
||||
"Changelog": "",
|
||||
"Details": "",
|
||||
"Update": "",
|
||||
"Remove": "",
|
||||
"Download": "Download",
|
||||
"Next Page": "Pagina successiva",
|
||||
"Prev Page": "Pagina precedente",
|
||||
"Unstar": "",
|
||||
"Star": "",
|
||||
"System memory": "",
|
||||
"microSD card": "",
|
||||
"Yes": "Sì",
|
||||
"No": "No",
|
||||
"Enabled": "Abilitato",
|
||||
"Disabled": "Disabilitato",
|
||||
|
||||
"Sort By": "Ordina per",
|
||||
"Sort Options": "Opzioni filtro",
|
||||
"Updated": "Aggiornato",
|
||||
"Size": "Misurare",
|
||||
"Alphabetical": "Alfabetico",
|
||||
"Decending": "Decrescente",
|
||||
"Ascending": "Crescente",
|
||||
"Filter": "Filtro",
|
||||
"Sort": "Riordina",
|
||||
"Order": "Ordina",
|
||||
"Info": "Informazioni",
|
||||
"Delete": "Elimina",
|
||||
"Hide Sphaira": "Nascondi Sphaira",
|
||||
"Are you sure you want to delete ": "Sei sicuro di voler eliminare? ",
|
||||
"Install Forwarder": "Installa forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ATTENZIONE: l'installazione di forwarder porterà al ban!",
|
||||
"Back": "Indietro",
|
||||
"Install": "Installa",
|
||||
"Fs": "Fs",
|
||||
"App": "App",
|
||||
"Menu": "Menu",
|
||||
"Homebrew": "Homebrew",
|
||||
"FileBrowser": "FileBrowser",
|
||||
"Open": "Apri",
|
||||
"Search": "Ricerca",
|
||||
"Updated": "Aggiornato",
|
||||
"Updated (Star)": "",
|
||||
"Downloads": "Download",
|
||||
"Size": "Misurare",
|
||||
"Size (Star)": "",
|
||||
"Alphabetical": "Alfabetico",
|
||||
"Alphabetical (Star)": "",
|
||||
"Likes": "",
|
||||
"ID": "",
|
||||
"Decending": "Decrescente",
|
||||
"Descending (down)": "Decrescente",
|
||||
"Desc": "Decrescente",
|
||||
"Ascending": "Crescente",
|
||||
"Ascending (Up)": "Crescente",
|
||||
"Asc": "Crescente",
|
||||
|
||||
"Menu Options": "Opzioni menu",
|
||||
"Header": "Intestazione",
|
||||
"Theme": "Tema",
|
||||
"Theme Options": "Opzioni tema",
|
||||
"Select Theme": "Seleziona tema",
|
||||
"Shuffle": "Mescola",
|
||||
"Music": "Musica",
|
||||
"Network": "Rete",
|
||||
"Network Options": "Opzioni di rete",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "",
|
||||
"Nxlink Upload": "",
|
||||
"Nxlink Finished": "",
|
||||
"Switch-Handheld!": "",
|
||||
"Switch-Docked!": "",
|
||||
"Language": "Lingua",
|
||||
"Auto": "",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "Logging",
|
||||
"Replace hbmenu on exit": "Sostituisci hbmenu all'uscita",
|
||||
"Misc": "Varie",
|
||||
"Misc Options": "Opzioni varie",
|
||||
"Web": "Rete",
|
||||
"Install forwarders": "",
|
||||
"Install location": "",
|
||||
"Show install warning": "",
|
||||
|
||||
"FileBrowser": "FileBrowser",
|
||||
"%zd files": "%zd files",
|
||||
"%zd dirs": "%zd dirs",
|
||||
"File Options": "Opzioni file",
|
||||
"Show Hidden": "Mostra nascosto",
|
||||
"Folders First": "Prima le cartelle",
|
||||
"Hidden Last": "Ultimo nascosto",
|
||||
"Yes": "Sì",
|
||||
"No": "No",
|
||||
"Network Options": "Opzioni di rete",
|
||||
"Nxlink": "Nxlink",
|
||||
"Check for update": "Controlla aggiornamenti",
|
||||
"File Options": "Opzioni file",
|
||||
"Cut": "Taglia",
|
||||
"Copy": "Copia",
|
||||
"Paste": "",
|
||||
"Paste ": "",
|
||||
" file(s)?": "",
|
||||
"Rename": "Rinomina",
|
||||
"Set New File Name": "",
|
||||
"Advanced": "Avanzato",
|
||||
"Advanced Options": "Opzioni avanzate",
|
||||
"Create File": "Crea file",
|
||||
"Set File Name": "",
|
||||
"Create Folder": "Crea cartella",
|
||||
"View as text": "Visualizza come testo",
|
||||
"Set Folder Name": "",
|
||||
"View as text (unfinished)": "Visualizza come testo (non finito)",
|
||||
"Set Archive Bit": "Imposta Archive Bit",
|
||||
"Empty...": "",
|
||||
"Open with DayBreak?": "",
|
||||
"Launch ": "",
|
||||
"Launch option for: ": "",
|
||||
"Select launcher for: ": "",
|
||||
|
||||
"Homebrew": "Homebrew",
|
||||
"Homebrew Options": "Opzioni Homebrew",
|
||||
"Hide Sphaira": "Nascondi Sphaira",
|
||||
"Install Forwarder": "Installa forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ATTENZIONE: l'installazione di forwarder porterà al ban!",
|
||||
"Installing Forwarder": "",
|
||||
"Creating Program": "",
|
||||
"Creating Control": "",
|
||||
"Creating Meta": "",
|
||||
"Writing Nca": "",
|
||||
"Updating ncm databse": "",
|
||||
"Pushing application record": "",
|
||||
"Installed!": "",
|
||||
"Failed to install forwarder": "",
|
||||
"Unstarred ": "",
|
||||
"Starred ": "",
|
||||
|
||||
"AppStore": "",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filtro: %s | Riordina: %s | Ordina: %s",
|
||||
"AppStore Options": "Opzioni dell'App Store",
|
||||
"All": "Tutto",
|
||||
"Games": "Giochi",
|
||||
"Emulators": "Emulatori",
|
||||
"Tools": "Strumenti",
|
||||
"Advanced": "Avanzato",
|
||||
"Themes": "Temi",
|
||||
"Legacy": "Legacy",
|
||||
"Misc": "Varie",
|
||||
"Downloads": "Download",
|
||||
"Filter": "Filtro",
|
||||
"Search": "Ricerca",
|
||||
"Menu Options": "Opzioni menu",
|
||||
"Header": "Intestazione",
|
||||
"Theme": "Tema",
|
||||
"Network": "Rete",
|
||||
"Logging": "Logging",
|
||||
"Enabled": "Abilitato",
|
||||
"Disabled": "Disabilitato",
|
||||
"Replace hbmenu on exit": "Sostituisci hbmenu all'uscita",
|
||||
"Misc Options": "Opzioni varie",
|
||||
"Themezer": "Themezer",
|
||||
"version: %s": "version: %s",
|
||||
"updated: %s": "updated: %s",
|
||||
"category: %s": "category: %s",
|
||||
"extracted: %.2f MiB": "extracted: %.2f MiB",
|
||||
"app_dls: %s": "app_dls: %s",
|
||||
"More by Author": "",
|
||||
"Leave Feedback": "",
|
||||
|
||||
"Irs": "Irs",
|
||||
"Web": "Rete",
|
||||
"Download": "Download",
|
||||
"Next Page": "Pagina successiva",
|
||||
"Prev Page": "Pagina precedente",
|
||||
"Ambient Noise Level: ": "",
|
||||
"Controller": "Controller",
|
||||
"Pad ": "Pad ",
|
||||
" (Available)": " (Disponibile)",
|
||||
" (Unsupported)": "",
|
||||
" (Unconnected)": " (Non connesso)",
|
||||
"HandHeld": "HandHeld",
|
||||
" (Available)": " (Disponibile)",
|
||||
"Rotation": "Rotazione",
|
||||
"0 (Sideways)": "0 (Di lato)",
|
||||
"90 (Flat)": "90 (Piatto)",
|
||||
"180 (-Sideways)": "180 (-Di lato)",
|
||||
"270 (Upside down)": "270 (Capovolto)",
|
||||
"Colour": "Colore",
|
||||
"Grey": "Grigio",
|
||||
"Ironbow": "Ironbow",
|
||||
"Green": "Verde",
|
||||
"Red": "Rosso",
|
||||
"Blue": "Blu",
|
||||
"Light Target": "Bersaglio leggero",
|
||||
"All leds": "Tutti i led",
|
||||
"Bright group": "Gruppo brillante",
|
||||
"Dim group": "Gruppo debole",
|
||||
"None": "Nessuno",
|
||||
"Normal image": "Immagine normale",
|
||||
"Negative image": "Immagine negativa",
|
||||
"320x240": "320x240",
|
||||
"160x120": "160x120",
|
||||
"80x60": "80x60",
|
||||
"40x30": "40x30",
|
||||
"20x15": "20×15",
|
||||
"Controller": "Controller",
|
||||
"Rotation": "Rotazione",
|
||||
"Colour": "Colore",
|
||||
"Light Target": "Bersaglio leggero",
|
||||
"Gain": "Guadagno",
|
||||
"Negative Image": "Immagine negativa",
|
||||
"Normal image": "Immagine normale",
|
||||
"Negative image": "Immagine negativa",
|
||||
"Format": "Formato",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "Formato di ritaglio",
|
||||
"External Light Filter": "Filtro luce esterno",
|
||||
"Load Default": "Carica predefinito",
|
||||
"No Internet": "Niente Internet",
|
||||
"[Applet Mode]": "[Modalità applet]",
|
||||
"Language": "Lingua"
|
||||
}
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "",
|
||||
"Nsfw": "",
|
||||
"Page": "",
|
||||
"Page %zu / %zu": "Page %zu / %zu",
|
||||
"Enter Page Number": "",
|
||||
"Bad Page": "",
|
||||
"Download theme?": "",
|
||||
|
||||
"Installing ": "",
|
||||
"Uninstalling ": "",
|
||||
"Deleting ": "",
|
||||
"Deleting": "",
|
||||
"Pasting ": "",
|
||||
"Pasting": "",
|
||||
"Removing ": "",
|
||||
"Scanning ": "",
|
||||
"Creating ": "",
|
||||
"Copying ": "",
|
||||
"Trying to load ": "",
|
||||
"Downloading ": "",
|
||||
"Checking MD5": "",
|
||||
"Loading...": "",
|
||||
"Loading": "",
|
||||
"Empty!": "",
|
||||
"Not Ready...": "",
|
||||
"Error loading page!": "",
|
||||
"Update avaliable: ": "",
|
||||
"Download update: ": "",
|
||||
"Updated to ": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Failed to download update": "",
|
||||
"Delete Selected files?": "",
|
||||
"Completely remove ": "",
|
||||
"Are you sure you want to delete ": "Sei sicuro di voler eliminare? ",
|
||||
"Are you sure you wish to cancel?": "",
|
||||
"If this message appears repeatedly, please open an issue.": ""
|
||||
}
|
||||
@@ -1,114 +1,230 @@
|
||||
{
|
||||
"Launch": "起動",
|
||||
"[Applet Mode]": "[Appletモード]",
|
||||
"No Internet": "インターネットなし",
|
||||
"Files": "ファイル",
|
||||
"Apps": "アプリ",
|
||||
"Store": "AppStore",
|
||||
"Menu": "メニュー",
|
||||
"Options": "設定",
|
||||
"Homebrew Options": "Homebrew設定",
|
||||
"OK": "確認",
|
||||
"Back": "戻る",
|
||||
"Select": "選択",
|
||||
"Open": "開く",
|
||||
"Launch": "起動",
|
||||
"Info": "情報",
|
||||
"Install": "インストール",
|
||||
"Delete": "削除",
|
||||
"Restart": "再起動",
|
||||
"Changelog": "リリースノート",
|
||||
"Details": "詳細",
|
||||
"Update": "アップデート",
|
||||
"Remove": "除去",
|
||||
"Download": "ダウンロード",
|
||||
"Next Page": "次のページ",
|
||||
"Prev Page": "前のページ",
|
||||
"Unstar": "お気に入り解除",
|
||||
"Star": "お気に入り",
|
||||
"System memory": "システムメモリ",
|
||||
"microSD card": "SDメモリーカード",
|
||||
"Yes": "はい",
|
||||
"No": "いいえ",
|
||||
"Enabled": "",
|
||||
"Disabled": "",
|
||||
|
||||
"Sort By": "並べ替え",
|
||||
"Sort Options": "並べ替え設定",
|
||||
"Updated": "最近使った順",
|
||||
"Size": "ファイルサイズ",
|
||||
"Alphabetical": "アルファベット順",
|
||||
"Decending": "降順",
|
||||
"Ascending": "上昇",
|
||||
"Filter": "フィルター",
|
||||
"Sort": "並べ替え",
|
||||
"Order": "順番",
|
||||
"Info": "情報",
|
||||
"Delete": "消去",
|
||||
"Hide Sphaira": "Sphairaを非表示",
|
||||
"Are you sure you want to delete ": "消去してもよろしいですか ",
|
||||
"Install Forwarder": "Forwarderのインストール",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "警告: ForwarderをインストールするとBANされます。",
|
||||
"Back": "戻る",
|
||||
"Install": "インストール",
|
||||
"Fs": "ファイル",
|
||||
"App": "アプリ",
|
||||
"Menu": "メニュー",
|
||||
"Homebrew": "Homebrew",
|
||||
"FileBrowser": "ファイルブラウザ",
|
||||
"Open": "開く",
|
||||
"Search": "検索",
|
||||
"Updated": "アップデート順",
|
||||
"Updated (Star)": "アップデート順(お気に入り)",
|
||||
"Downloads": "ダウンロード順",
|
||||
"Size": "ファイルサイズ",
|
||||
"Size (Star)": "ファイルサイズ(お気に入り)",
|
||||
"Alphabetical": "アルファベット順",
|
||||
"Alphabetical (Star)": "アルファベット順(お気に入り)",
|
||||
"Likes": "いいね順",
|
||||
"ID": "デベロッパー順",
|
||||
"Decending": "降順",
|
||||
"Descending (down)": "降順",
|
||||
"Desc": "降順",
|
||||
"Ascending": "上昇",
|
||||
"Ascending (Up)": "上昇",
|
||||
"Asc": "上昇",
|
||||
|
||||
"Menu Options": "メニュー設定",
|
||||
"Header": "ヘッダー",
|
||||
"Theme": "テーマ",
|
||||
"Theme Options": "テーマ設定",
|
||||
"Select Theme": "テーマを選ぶ",
|
||||
"Shuffle": "シャッフル",
|
||||
"Music": "BGM",
|
||||
"Network": "ネットワーク",
|
||||
"Network Options": "ネットワーク設定",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "Nxlink 接続",
|
||||
"Nxlink Upload": "Nxlink アップロード",
|
||||
"Nxlink Finished": "Nxlink 終了",
|
||||
"Switch-Handheld!": "ハンドヘルド!",
|
||||
"Switch-Docked!": "ドック接続!",
|
||||
"Language": "言語",
|
||||
"Auto": "自動",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "ログの取得",
|
||||
"Replace hbmenu on exit": "終了時に hbmenu を置き換える",
|
||||
"Misc": "その他",
|
||||
"Misc Options": "その他",
|
||||
"Web": "ウェブブラウザ",
|
||||
"Install forwarders": "Forwarderのインストール機能",
|
||||
"Install location": "インストール経路",
|
||||
"Show install warning": "警告文を示す",
|
||||
|
||||
"FileBrowser": "ファイルブラウザ",
|
||||
"%zd files": "%zd個のファイル",
|
||||
"%zd dirs": "%zd個のフォルダー",
|
||||
"File Options": "ファイル設定",
|
||||
"Show Hidden": "非表示ファイルを表示",
|
||||
"Folders First": "フォルダーを優先",
|
||||
"Hidden Last": "非表示ファイルを劣後",
|
||||
"Yes": "はい",
|
||||
"No": "いいえ",
|
||||
"Network Options": "ネットワーク設定",
|
||||
"Nxlink": "Nxlink",
|
||||
"Check for update": "アップデートの確認",
|
||||
"File Options": "ファイル設定",
|
||||
"Cut": "切り取り",
|
||||
"Copy": "コピー",
|
||||
"Paste": "ペースト",
|
||||
"Paste ": " ",
|
||||
" file(s)?": "個のファイルをペーストしますか?",
|
||||
"Rename": "名前の変更",
|
||||
"Advanced Options": "ファイルの作成",
|
||||
"Set New File Name": "新しい名前を入力",
|
||||
"Advanced": "高度な",
|
||||
"Advanced Options": "高度設定",
|
||||
"Create File": "ファイルの作成",
|
||||
"Set File Name": "名前を入力",
|
||||
"Create Folder": "フォルダーの作成",
|
||||
"View as text": "テキストとして表示",
|
||||
"Set Folder Name": "名前を入力",
|
||||
"View as text (unfinished)": "テキストとして表示 (未完成)",
|
||||
"Set Archive Bit": "アーカイブビットの設定",
|
||||
"Empty...": "このフォルダーは空です",
|
||||
"Open with DayBreak?": "DayBreakで開きますか?",
|
||||
"Launch ": "起動しますか",
|
||||
"Launch option for: ": "起動設定: ",
|
||||
"Select launcher for: ": "起動ランチャーを選ぶ: ",
|
||||
|
||||
"Homebrew": "Homebrew",
|
||||
"Homebrew Options": "Homebrew設定",
|
||||
"Hide Sphaira": "Sphairaを非表示",
|
||||
"Install Forwarder": "Forwarderのインストール",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "警告: ForwarderをインストールするとBANされます。",
|
||||
"Installing Forwarder": "Forwarderのインストール中",
|
||||
"Creating Program": "プログラム作成中",
|
||||
"Creating Control": "コントロール作成中",
|
||||
"Creating Meta": "メター作成中",
|
||||
"Writing Nca": "Nca書き取り中",
|
||||
"Updating ncm databse": "ncmのDBをアップデート中",
|
||||
"Pushing application record": "アプリの記録をプッシュ中",
|
||||
"Installed!": "インストール完了",
|
||||
"Failed to install forwarder": "Forwarderのインストール失敗",
|
||||
"Unstarred ": "お気に入り解除: ",
|
||||
"Starred ": "お気に入りに登録: ",
|
||||
|
||||
"AppStore": "AppStore",
|
||||
"Filter: %s | Sort: %s | Order: %s": "フィルター: %s | 並べ替え: %s | 順番: %s",
|
||||
"AppStore Options": "AppStoreの設定",
|
||||
"All": "全て",
|
||||
"Games": "ゲーム",
|
||||
"Emulators": "エミュレータ",
|
||||
"Tools": "ツール",
|
||||
"Advanced": "高度な",
|
||||
"Themes": "テーマ",
|
||||
"Legacy": "レガシー",
|
||||
"Misc": "その他",
|
||||
"Downloads": "ダウンロード",
|
||||
"Filter": "フィルター",
|
||||
"Search": "検索",
|
||||
"Menu Options": "メニュー設定",
|
||||
"Header": "ヘッダー",
|
||||
"Theme": "テーマ",
|
||||
"Network": "ネットワーク",
|
||||
"Logging": "ログの取得",
|
||||
"Enabled": "有効",
|
||||
"Disabled": "無効",
|
||||
"Replace hbmenu on exit": "終了時に hbmenu を置き換える",
|
||||
"Misc Options": "その他",
|
||||
"Themezer": "Themezer",
|
||||
"version: %s": "バージョン: %s",
|
||||
"updated: %s": "更新日: %s",
|
||||
"category: %s": "カテゴリー: %s",
|
||||
"extracted: %.2f MiB": "容量: %.2f MiB",
|
||||
"app_dls: %s": "ダウンロード: %s",
|
||||
"More by Author": "ディベロッパーの他のアプリを見る",
|
||||
"Leave Feedback": "意見を残す",
|
||||
|
||||
"Irs": "Joy-Con IRカメラ",
|
||||
"Web": "ウェブブラウザ",
|
||||
"Download": "ダウンロード",
|
||||
"Next Page": "次のページ",
|
||||
"Prev Page": "前のページ",
|
||||
"Pad ": "パッド ",
|
||||
"Ambient Noise Level: ": "ノイズレベル: ",
|
||||
"Controller": "コントローラー",
|
||||
"Pad ": "Joy-Con ",
|
||||
" (Available)": " (利用可能)",
|
||||
" (Unsupported)": " (未対応)",
|
||||
" (Unconnected)": " (未接続)",
|
||||
"HandHeld": "ハンドヘルド",
|
||||
" (Available)": " (利用可能)",
|
||||
"0 (Sideways)": "0(横)",
|
||||
"90 (Flat)": "90(フラット)",
|
||||
"Rotation": "回転",
|
||||
"0 (Sideways)": "0 (横)",
|
||||
"90 (Flat)": "90 (フラット)",
|
||||
"180 (-Sideways)": "180 (-横)",
|
||||
"270 (Upside down)": "270(上下逆さま)",
|
||||
"270 (Upside down)": "270 (上下逆さま)",
|
||||
"Colour": "色",
|
||||
"Grey": "グレー",
|
||||
"Ironbow": "アイアンボウ",
|
||||
"Green": "緑",
|
||||
"Red": "赤",
|
||||
"Blue": "青",
|
||||
"Light Target": "ライトターゲット",
|
||||
"All leds": "すべてのLED",
|
||||
"Bright group": "明るいグループ",
|
||||
"Dim group": "薄暗いグループ",
|
||||
"None": "なし",
|
||||
"Gain": "増幅",
|
||||
"Negative Image": "ネガティブなイメージ",
|
||||
"Normal image": "通常画像",
|
||||
"Negative image": "ネガティブなイメージ",
|
||||
"Format": "解像度",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20x15",
|
||||
"Controller": "コントローラー",
|
||||
"Rotation": "回転",
|
||||
"Colour": "色",
|
||||
"Light Target": "ライトターゲット",
|
||||
"Gain": "得",
|
||||
"Negative Image": "ネガティブなイメージ",
|
||||
"Format": "形式",
|
||||
"Trimming Format": "トリミングフォーマット",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "トリミングされた解像度",
|
||||
"External Light Filter": "外光フィルター",
|
||||
"Load Default": "デフォルトをロード",
|
||||
"No Internet": "インターネットなし",
|
||||
"[Applet Mode]": "[Appletモード]",
|
||||
"Language": "言語"
|
||||
}
|
||||
"Load Default": "基本設定に戻す",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Themezer設定",
|
||||
"Nsfw": "アダルトテーマ",
|
||||
"Page": "ページ",
|
||||
"Page %zu / %zu": "ページ %zu / %zu",
|
||||
"Enter Page Number": "ページの番号を入力",
|
||||
"Bad Page": "ページが見つかりません",
|
||||
"Download theme?": "テーマをインストールしますか?",
|
||||
|
||||
"Installing ": "インストール中 ",
|
||||
"Uninstalling ": "アンインストール中 ",
|
||||
"Deleting ": "削除中 ",
|
||||
"Deleting": "削除中",
|
||||
"Pasting ": "ペースト中 ",
|
||||
"Pasting": "ペースト中",
|
||||
"Removing ": "除去中 ",
|
||||
"Scanning ": "スキャン中 ",
|
||||
"Creating ": "作成中 ",
|
||||
"Copying ": "コピー中 ",
|
||||
"Trying to load ": "サムネイルを取得中 ",
|
||||
"Downloading ": "ダウンロード中 ",
|
||||
"Checking MD5": "MD5を確認中 ",
|
||||
"Loading...": "ロード中",
|
||||
"Loading": "ロード中",
|
||||
"Empty!": "何も見つかりません",
|
||||
"Not Ready...": "準備ができていません",
|
||||
"Error loading page!": "ページのロードエラー",
|
||||
"Update avaliable: ": "アップデート可能: ",
|
||||
"Download update: ": "アップデートをダウンロード: ",
|
||||
"Updated to ": "アップデート: ",
|
||||
"Restart Sphaira?": "Sphairaを再起動しますか?",
|
||||
"Failed to download update": "アップデートのダウンロード失敗",
|
||||
"Delete Selected files?": "本当に削除しますか?",
|
||||
"Completely remove ": "除去しますか ",
|
||||
"Are you sure you want to delete ": "消去してもよろしいですか ",
|
||||
"Are you sure you wish to cancel?": "本当に取り消しますか?",
|
||||
"If this message appears repeatedly, please open an issue.": "このメッセージが繰り返し表示される場合は、問題を開いてください。"
|
||||
}
|
||||
@@ -1,114 +1,230 @@
|
||||
{
|
||||
"Launch": "실행",
|
||||
"[Applet Mode]": "[애플릿 모드]",
|
||||
"No Internet": "네트워크 연결 없음",
|
||||
"Files": "파일 탐색기",
|
||||
"Apps": "홈브류",
|
||||
"Store": "앱스토어",
|
||||
"Menu": "메뉴",
|
||||
"Options": "설정",
|
||||
"Homebrew Options": "홈브류 설정",
|
||||
"OK": "확인",
|
||||
"Back": "뒤로",
|
||||
"Select": "선택",
|
||||
"Open": "열기",
|
||||
"Launch": "실행",
|
||||
"Info": "정보",
|
||||
"Install": "설치",
|
||||
"Delete": "삭제",
|
||||
"Restart": "재시작",
|
||||
"Changelog": "변경 내역",
|
||||
"Details": "상세",
|
||||
"Update": "업데이트",
|
||||
"Remove": "제거",
|
||||
"Download": "다운로드",
|
||||
"Next Page": "다음 페이지",
|
||||
"Prev Page": "이전 페이지",
|
||||
"Unstar": "즐겨찾기 해제",
|
||||
"Star": "즐겨찾기",
|
||||
"System memory": "낸드 저장소",
|
||||
"microSD card": "SD 카드",
|
||||
"Yes": "예",
|
||||
"No": "아니요",
|
||||
"Enabled": "",
|
||||
"Disabled": "",
|
||||
|
||||
"Sort By": "정렬",
|
||||
"Sort Options": "정렬 설정",
|
||||
"Updated": "업데이트순",
|
||||
"Size": "크기순",
|
||||
"Alphabetical": "알파벳순",
|
||||
"Decending": "내림차순",
|
||||
"Ascending": "오름차순",
|
||||
"Filter": "필터",
|
||||
"Sort": "분류",
|
||||
"Order": "정렬",
|
||||
"Info": "정보",
|
||||
"Delete": "삭제",
|
||||
"Hide Sphaira": "Sphaira 숨기기",
|
||||
"Are you sure you want to delete ": "정말 삭제하시겠습니까? ",
|
||||
"Install Forwarder": "바로가기 설치",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "주의: 바로가기 설치시 BAN 위험이 있습니다!",
|
||||
"Back": "뒤로",
|
||||
"Install": "설치",
|
||||
"Fs": "파일 탐색기",
|
||||
"App": "앱",
|
||||
"Menu": "메뉴",
|
||||
"Homebrew": "홈브류",
|
||||
"FileBrowser": "파일 탐색기",
|
||||
"Open": "열기",
|
||||
"Search": "검색",
|
||||
"Updated": "업데이트순",
|
||||
"Updated (Star)": "업데이트순 (즐겨찾기)",
|
||||
"Downloads": "다운로드순",
|
||||
"Size": "크기순",
|
||||
"Size (Star)": "크기순 (즐겨찾기)",
|
||||
"Alphabetical": "알파벳순",
|
||||
"Alphabetical (Star)": "알파벳순 (즐겨찾기)",
|
||||
"Likes": "좋아요순",
|
||||
"ID": "작성자순",
|
||||
"Decending": "내림차순",
|
||||
"Descending (down)": "내림차순",
|
||||
"Desc": "내림차순",
|
||||
"Ascending": "오름차순",
|
||||
"Ascending (Up)": "오름차순",
|
||||
"Asc": "오름차순",
|
||||
|
||||
"Menu Options": "메뉴",
|
||||
"Header": "헤더",
|
||||
"Theme": "테마",
|
||||
"Theme Options": "테마 설정",
|
||||
"Select Theme": "테마 선택",
|
||||
"Shuffle": "셔플",
|
||||
"Music": "BGM",
|
||||
"Network": "네트워크",
|
||||
"Network Options": "네트워크 설정",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "Nxlink 연결됨",
|
||||
"Nxlink Upload": "Nxlink 업로드",
|
||||
"Nxlink Finished": "Nxlink 종료됨",
|
||||
"Switch-Handheld!": "휴대모드로 전환되었습니다!",
|
||||
"Switch-Docked!": "독 모드로 전환되었습니다!",
|
||||
"Language": "언어",
|
||||
"Auto": "자동",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "로깅",
|
||||
"Replace hbmenu on exit": "hbmenu sphaira 교체",
|
||||
"Misc": "기타",
|
||||
"Misc Options": "기타",
|
||||
"Web": "웹 브라우저",
|
||||
"Install forwarders": "바로가기 설치 기능",
|
||||
"Install location": "설치 경로",
|
||||
"Show install warning": "바로가기 설치 경고문 표시",
|
||||
|
||||
"FileBrowser": "파일 탐색기",
|
||||
"%zd files": "%zd개의 파일",
|
||||
"%zd dirs": "%zd개의 폴더",
|
||||
"File Options": "파일 설정",
|
||||
"Show Hidden": "숨겨진 항목 표시",
|
||||
"Folders First": "폴더 우선 정렬",
|
||||
"Hidden Last": "숨겨진 항목 후순 정렬",
|
||||
"Yes": "예",
|
||||
"No": "아니요",
|
||||
"Network Options": "네트워크 설정",
|
||||
"Nxlink": "Nxlink",
|
||||
"Check for update": "업데이트 확인",
|
||||
"File Options": "파일 설정",
|
||||
"Cut": "잘라내기",
|
||||
"Copy": "복사",
|
||||
"Paste": "붙여넣기",
|
||||
"Paste ": " ",
|
||||
" file(s)?": "개 항목을 붙여넣으시겠습니까?",
|
||||
"Rename": "이름 바꾸기",
|
||||
"Set New File Name": "새 파일명 입력",
|
||||
"Advanced": "고급",
|
||||
"Advanced Options": "고급 설정",
|
||||
"Create File": "파일 생성",
|
||||
"Set File Name": "파일명 입력",
|
||||
"Create Folder": "새 폴더",
|
||||
"View as text": "텍스트로 보기",
|
||||
"Set Folder Name": "폴더명 입력",
|
||||
"View as text (unfinished)": "텍스트로 보기 (미완성)",
|
||||
"Set Archive Bit": "아카이브 비트 설정",
|
||||
"Empty...": "비어있습니다...",
|
||||
"Open with DayBreak?": "DayBreak로 여시겠습니까?",
|
||||
"Launch ": "실행하시겠습니까 ",
|
||||
"Launch option for: ": "실행 설정: ",
|
||||
"Select launcher for: ": "실행 런처: ",
|
||||
|
||||
"Homebrew": "홈브류",
|
||||
"Homebrew Options": "홈브류 설정",
|
||||
"Hide Sphaira": "Sphaira 숨기기",
|
||||
"Install Forwarder": "바로가기 설치",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "주의: 시스낸드에서 바로가기 설치시 BAN 위험이 있습니다!",
|
||||
"Installing Forwarder": "바로가기 설치중...",
|
||||
"Creating Program": "프로그램 작성중...",
|
||||
"Creating Control": "컨트롤 작성중...",
|
||||
"Creating Meta": "메타 작성중...",
|
||||
"Writing Nca": "Nca 쓰는중...",
|
||||
"Updating ncm databse": "ncm 데이터베이스 업데이트중...",
|
||||
"Pushing application record": "응용 프로그램 기록 푸시중...",
|
||||
"Installed!": "설치 완료!",
|
||||
"Failed to install forwarder": "바로가기 설치 실패",
|
||||
"Unstarred ": "즐겨찾기 해제: ",
|
||||
"Starred ": "즐겨찾기 적용: ",
|
||||
|
||||
"AppStore": "앱스토어",
|
||||
"Filter: %s | Sort: %s | Order: %s": "필터: %s | 분류: %s | 정렬: %s",
|
||||
"AppStore Options": "앱스토어 설정",
|
||||
"All": "전체",
|
||||
"Games": "게임",
|
||||
"Emulators": "에뮬레이터",
|
||||
"Tools": "도구",
|
||||
"Advanced": "고급",
|
||||
"Themes": "테마",
|
||||
"Legacy": "레거시",
|
||||
"Misc": "기타",
|
||||
"Downloads": "다운로드순",
|
||||
"Filter": "필터",
|
||||
"Search": "검색",
|
||||
"Menu Options": "메뉴",
|
||||
"Header": "헤더",
|
||||
"Theme": "테마",
|
||||
"Network": "네트워크",
|
||||
"Logging": "로깅",
|
||||
"Enabled": "",
|
||||
"Disabled": "",
|
||||
"Replace hbmenu on exit": "종료 시 hbmenu 교체",
|
||||
"Misc Options": "기타",
|
||||
"Themezer": "Themezer",
|
||||
"version: %s": "버전: %s",
|
||||
"updated: %s": "갱신일: %s",
|
||||
"category: %s": "카테고리: %s",
|
||||
"extracted: %.2f MiB": "용량: %.2f MiB",
|
||||
"app_dls: %s": "다운로드 횟수: %s",
|
||||
"More by Author": "개발자의 다른 앱 더보기",
|
||||
"Leave Feedback": "피드백 남기기",
|
||||
|
||||
"Irs": "Joy-Con IR 카메라",
|
||||
"Web": "웹 브라우저",
|
||||
"Download": "다운로드",
|
||||
"Next Page": "다음 페이지",
|
||||
"Prev Page": "이전 페이지",
|
||||
"Pad ": "Joy-Con ",
|
||||
" (Unconnected)": " (연결 없음)",
|
||||
"HandHeld": "본체 연결",
|
||||
"Ambient Noise Level: ": "노이즈 레벨: ",
|
||||
"Controller": "컨트롤러",
|
||||
"Pad ": "조이콘 ",
|
||||
" (Available)": " (사용 가능)",
|
||||
" (Unsupported)": " (지원 안됨)",
|
||||
" (Unconnected)": " (연결 없음)",
|
||||
"HandHeld": "- 본체 연결",
|
||||
"Rotation": "화면 회전",
|
||||
"0 (Sideways)": "0 (좌회전)",
|
||||
"90 (Flat)": "90 (정방향)",
|
||||
"180 (-Sideways)": "180 (우회전)",
|
||||
"270 (Upside down)": "270 (역전)",
|
||||
"Colour": "컬러 팔레트",
|
||||
"Grey": "그레이",
|
||||
"Ironbow": "아이언보우",
|
||||
"Green": "그린",
|
||||
"Red": "레드",
|
||||
"Blue": "블루",
|
||||
"Light Target": "반사 표적",
|
||||
"All leds": "모든 LED 켜기",
|
||||
"Bright group": "Bright LED 켜기",
|
||||
"Dim group": "Dim LED 켜기",
|
||||
"None": "LED 끄기",
|
||||
"Gain": "대비",
|
||||
"Negative Image": "화상 이미지",
|
||||
"Normal image": "일반",
|
||||
"Negative image": "반전",
|
||||
"Format": "해상도",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Controller": "컨트롤러",
|
||||
"Rotation": "화면 회전",
|
||||
"Colour": "팔레트",
|
||||
"Light Target": "반사 표적",
|
||||
"Gain": "대비",
|
||||
"Negative Image": "화상 이미지",
|
||||
"Format": "해상도",
|
||||
"Trimming Format": "트리밍 해상도",
|
||||
"External Light Filter": "외부 조명 필터",
|
||||
"Load Default": "기본값으로 설정",
|
||||
"No Internet": "네트워크 연결 없음",
|
||||
"[Applet Mode]": "[애플릿 모드]",
|
||||
"Language": "언어"
|
||||
}
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Themezer 설정",
|
||||
"Nsfw": "선정성 테마",
|
||||
"Page": "페이지",
|
||||
"Page %zu / %zu": "페이지 %zu / %zu",
|
||||
"Enter Page Number": "페이지 번호 입력",
|
||||
"Bad Page": "잘못된 페이지",
|
||||
"Download theme?": "테마를 내려받으시겠습니까?",
|
||||
|
||||
"Installing ": "설치중... ",
|
||||
"Uninstalling ": "설치 제거중... ",
|
||||
"Deleting ": "삭제중... ",
|
||||
"Deleting": "삭제중...",
|
||||
"Pasting ": "붙여넣는중... ",
|
||||
"Pasting": "붙여넣는중...",
|
||||
"Removing ": "제거중... ",
|
||||
"Scanning ": "스캔중... ",
|
||||
"Creating ": "작성중... ",
|
||||
"Copying ": "복사중... ",
|
||||
"Trying to load ": "썸네일 받아오는 중... ",
|
||||
"Downloading ": "다운로드중... ",
|
||||
"Checking MD5": "MD5 확인중... ",
|
||||
"Loading...": "로딩중...",
|
||||
"Loading": "로딩중...",
|
||||
"Empty!": "찾을 수 없습니다!",
|
||||
"Not Ready...": "준비되지 않았습니다...",
|
||||
"Error loading page!": "페이지 로딩 오류!",
|
||||
"Update avaliable: ": "업데이트 가능: ",
|
||||
"Download update: ": "업데이트 다운로드: ",
|
||||
"Updated to ": "업데이트: ",
|
||||
"Restart Sphaira?": "Sphaira를 재시작 하시겠습니까?",
|
||||
"Failed to download update": "업데이트 다운로드 실패!",
|
||||
"Delete Selected files?": "정말 삭제하시겠습니까?",
|
||||
"Completely remove ": "제거하시겠습니까 ",
|
||||
"Are you sure you want to delete ": "정말 삭제하시겠습니까 ",
|
||||
"Are you sure you wish to cancel?": "정말 취소하시겠습니까?",
|
||||
"If this message appears repeatedly, please open an issue.": "해당 메시지가 반복해서 나타나는 경우, 이슈를 열어주세요."
|
||||
}
|
||||
@@ -1,114 +1,230 @@
|
||||
{
|
||||
"Launch": "Launch",
|
||||
"[Applet Mode]": "[Applet-modus]",
|
||||
"No Internet": "Geen internet",
|
||||
"Files": "",
|
||||
"Apps": "",
|
||||
"Store": "",
|
||||
"Menu": "Menu",
|
||||
"Options": "Opties",
|
||||
"Homebrew Options": "Homebrew-opties",
|
||||
"OK": "",
|
||||
"Back": "Terug",
|
||||
"Select": "",
|
||||
"Open": "Open",
|
||||
"Launch": "Launch",
|
||||
"Info": "Info",
|
||||
"Install": "Installeren",
|
||||
"Delete": "Verwijderen",
|
||||
"Restart": "",
|
||||
"Changelog": "",
|
||||
"Details": "",
|
||||
"Update": "",
|
||||
"Remove": "",
|
||||
"Download": "Downloaden",
|
||||
"Next Page": "Volgende pagina",
|
||||
"Prev Page": "Vorige pagina",
|
||||
"Unstar": "",
|
||||
"Star": "",
|
||||
"System memory": "",
|
||||
"microSD card": "",
|
||||
"Yes": "Ja",
|
||||
"No": "Nee",
|
||||
"Enabled": "Ingeschakeld",
|
||||
"Disabled": "Gehandicapt",
|
||||
|
||||
"Sort By": "Sorteer op",
|
||||
"Sort Options": "Sorteeropties",
|
||||
"Updated": "Bijgewerkt",
|
||||
"Size": "Maat",
|
||||
"Alphabetical": "Alfabetisch",
|
||||
"Decending": "Aflopend",
|
||||
"Ascending": "Oplopend",
|
||||
"Filter": "Filter",
|
||||
"Sort": "Soort",
|
||||
"Order": "Volgorde",
|
||||
"Info": "Info",
|
||||
"Delete": "Verwijderen",
|
||||
"Hide Sphaira": "Verberg Sphaira",
|
||||
"Are you sure you want to delete ": "Weet u zeker dat u wilt verwijderen ",
|
||||
"Install Forwarder": "Forwarder installeren",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "WAARSCHUWING: Het installeren van forwarders leidt tot een ban!",
|
||||
"Back": "Terug",
|
||||
"Install": "Installeren",
|
||||
"Fs": "Fs",
|
||||
"App": "App",
|
||||
"Menu": "Menu",
|
||||
"Homebrew": "Zelf brouwen",
|
||||
"FileBrowser": "Bestandsbrowser",
|
||||
"Open": "Open",
|
||||
"Search": "Zoekopdracht",
|
||||
"Updated": "Bijgewerkt",
|
||||
"Updated (Star)": "",
|
||||
"Downloads": "Downloads",
|
||||
"Size": "Maat",
|
||||
"Size (Star)": "",
|
||||
"Alphabetical": "Alfabetisch",
|
||||
"Alphabetical (Star)": "",
|
||||
"Likes": "",
|
||||
"ID": "",
|
||||
"Decending": "Aflopend",
|
||||
"Descending (down)": "Aflopend",
|
||||
"Desc": "Aflopend",
|
||||
"Ascending": "Oplopend",
|
||||
"Ascending (Up)": "Oplopend",
|
||||
"Asc": "Oplopend",
|
||||
|
||||
"Menu Options": "Menu-opties",
|
||||
"Header": "Koptekst",
|
||||
"Theme": "Thema",
|
||||
"Theme Options": "Thema Opties",
|
||||
"Select Theme": "Selecteer Thema",
|
||||
"Shuffle": "Schudden",
|
||||
"Music": "Muziek",
|
||||
"Network": "Netwerk",
|
||||
"Network Options": "Netwerkopties",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "",
|
||||
"Nxlink Upload": "",
|
||||
"Nxlink Finished": "",
|
||||
"Switch-Handheld!": "",
|
||||
"Switch-Docked!": "",
|
||||
"Language": "Taal",
|
||||
"Auto": "",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "Loggen",
|
||||
"Replace hbmenu on exit": "Vervang hbmenu bij afsluiten",
|
||||
"Misc": "Diversen",
|
||||
"Misc Options": "Diverse opties",
|
||||
"Web": "Web",
|
||||
"Install forwarders": "",
|
||||
"Install location": "",
|
||||
"Show install warning": "",
|
||||
|
||||
"FileBrowser": "Bestandsbrowser",
|
||||
"%zd files": "%zd files",
|
||||
"%zd dirs": "%zd dirs",
|
||||
"File Options": "Bestandsopties",
|
||||
"Show Hidden": "Toon verborgen",
|
||||
"Folders First": "Mappen eerst",
|
||||
"Hidden Last": "Verborgen laatste",
|
||||
"Yes": "Ja",
|
||||
"No": "Nee",
|
||||
"Network Options": "Netwerkopties",
|
||||
"Nxlink": "Nxlink",
|
||||
"Check for update": "Controleer op update",
|
||||
"File Options": "Bestandsopties",
|
||||
"Cut": "Snee",
|
||||
"Copy": "Kopiëren",
|
||||
"Paste": "",
|
||||
"Paste ": "",
|
||||
" file(s)?": "",
|
||||
"Rename": "Hernoemen",
|
||||
"Set New File Name": "",
|
||||
"Advanced": "Geavanceerd",
|
||||
"Advanced Options": "Bestand maken",
|
||||
"Create File": "Bestand maken",
|
||||
"Set File Name": "",
|
||||
"Create Folder": "Map maken",
|
||||
"View as text": "Bekijk als tekst",
|
||||
"Set Folder Name": "",
|
||||
"View as text (unfinished)": "Bekijk als tekst (onvoltooid)",
|
||||
"Set Archive Bit": "Archiefbit instellen",
|
||||
"Empty...": "",
|
||||
"Open with DayBreak?": "",
|
||||
"Launch ": "",
|
||||
"Launch option for: ": "",
|
||||
"Select launcher for: ": "",
|
||||
|
||||
"Homebrew": "Zelf brouwen",
|
||||
"Homebrew Options": "Homebrew-opties",
|
||||
"Hide Sphaira": "Verberg Sphaira",
|
||||
"Install Forwarder": "Forwarder installeren",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "WAARSCHUWING: Het installeren van forwarders leidt tot een ban!",
|
||||
"Installing Forwarder": "",
|
||||
"Creating Program": "",
|
||||
"Creating Control": "",
|
||||
"Creating Meta": "",
|
||||
"Writing Nca": "",
|
||||
"Updating ncm databse": "",
|
||||
"Pushing application record": "",
|
||||
"Installed!": "",
|
||||
"Failed to install forwarder": "",
|
||||
"Unstarred ": "",
|
||||
"Starred ": "",
|
||||
|
||||
"AppStore": "",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filter: %s | Soort: %s | Volgorde: %s",
|
||||
"AppStore Options": "AppStore-opties",
|
||||
"All": "Alle",
|
||||
"Games": "Spellen",
|
||||
"Emulators": "Emulators",
|
||||
"Tools": "Hulpmiddelen",
|
||||
"Advanced": "Geavanceerd",
|
||||
"Themes": "Thema's",
|
||||
"Legacy": "Nalatenschap",
|
||||
"Misc": "Diversen",
|
||||
"Downloads": "Downloads",
|
||||
"Filter": "Filter",
|
||||
"Search": "Zoekopdracht",
|
||||
"Menu Options": "Menu-opties",
|
||||
"Header": "Koptekst",
|
||||
"Theme": "Thema",
|
||||
"Network": "Netwerk",
|
||||
"Logging": "Loggen",
|
||||
"Enabled": "Ingeschakeld",
|
||||
"Disabled": "Gehandicapt",
|
||||
"Replace hbmenu on exit": "Vervang hbmenu bij afsluiten",
|
||||
"Misc Options": "Diverse opties",
|
||||
"Themezer": "Themamaker",
|
||||
"version: %s": "version: %s",
|
||||
"updated: %s": "updated: %s",
|
||||
"category: %s": "category: %s",
|
||||
"extracted: %.2f MiB": "extracted: %.2f MiB",
|
||||
"app_dls: %s": "app_dls: %s",
|
||||
"More by Author": "",
|
||||
"Leave Feedback": "",
|
||||
|
||||
"Irs": "Ir",
|
||||
"Web": "Web",
|
||||
"Download": "Downloaden",
|
||||
"Next Page": "Volgende pagina",
|
||||
"Prev Page": "Vorige pagina",
|
||||
"Ambient Noise Level: ": "",
|
||||
"Controller": "Controleur",
|
||||
"Pad ": "Pad ",
|
||||
" (Available)": " (Beschikbaar)",
|
||||
" (Unsupported)": "",
|
||||
" (Unconnected)": " (Niet verbonden)",
|
||||
"HandHeld": "Handbediende",
|
||||
" (Available)": " (Beschikbaar)",
|
||||
"Rotation": "Rotatie",
|
||||
"0 (Sideways)": "0 (zijwaarts)",
|
||||
"90 (Flat)": "90 (plat)",
|
||||
"180 (-Sideways)": "180 (-zijwaarts)",
|
||||
"270 (Upside down)": "270 (ondersteboven)",
|
||||
"Colour": "Kleur",
|
||||
"Grey": "Grijs",
|
||||
"Ironbow": "Ijzerboog",
|
||||
"Green": "Groente",
|
||||
"Red": "Rood",
|
||||
"Blue": "Blauw",
|
||||
"Light Target": "Licht doel",
|
||||
"All leds": "Alle leds",
|
||||
"Bright group": "Heldere groep",
|
||||
"Dim group": "Dim groep",
|
||||
"None": "Geen",
|
||||
"Normal image": "Normaal beeld",
|
||||
"Negative image": "Negatief beeld",
|
||||
"320x240": "320x240",
|
||||
"160x120": "160x120",
|
||||
"80x60": "80x60",
|
||||
"40x30": "40x30",
|
||||
"20x15": "20x15",
|
||||
"Controller": "Controleur",
|
||||
"Rotation": "Rotatie",
|
||||
"Colour": "Kleur",
|
||||
"Light Target": "Licht doel",
|
||||
"Gain": "Verdienen",
|
||||
"Negative Image": "Negatief beeld",
|
||||
"Normal image": "Normaal beeld",
|
||||
"Negative image": "Negatief beeld",
|
||||
"Format": "Formaat",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "Trimformaat",
|
||||
"External Light Filter": "Extern lichtfilter",
|
||||
"Load Default": "Standaard laden",
|
||||
"No Internet": "Geen internet",
|
||||
"[Applet Mode]": "[Applet-modus]",
|
||||
"Language": "Taal"
|
||||
}
|
||||
|
||||
"Themezer": "Themamaker",
|
||||
"Themezer Options": "",
|
||||
"Nsfw": "",
|
||||
"Page": "",
|
||||
"Page %zu / %zu": "Page %zu / %zu",
|
||||
"Enter Page Number": "",
|
||||
"Bad Page": "",
|
||||
"Download theme?": "",
|
||||
|
||||
"Installing ": "",
|
||||
"Uninstalling ": "",
|
||||
"Deleting ": "",
|
||||
"Deleting": "",
|
||||
"Pasting ": "",
|
||||
"Pasting": "",
|
||||
"Removing ": "",
|
||||
"Scanning ": "",
|
||||
"Creating ": "",
|
||||
"Copying ": "",
|
||||
"Trying to load ": "",
|
||||
"Downloading ": "",
|
||||
"Checking MD5": "",
|
||||
"Loading...": "",
|
||||
"Loading": "",
|
||||
"Empty!": "",
|
||||
"Not Ready...": "",
|
||||
"Error loading page!": "",
|
||||
"Update avaliable: ": "",
|
||||
"Download update: ": "",
|
||||
"Updated to ": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Failed to download update": "",
|
||||
"Delete Selected files?": "",
|
||||
"Completely remove ": "",
|
||||
"Are you sure you want to delete ": "Weet u zeker dat u wilt verwijderen ",
|
||||
"Are you sure you wish to cancel?": "",
|
||||
"If this message appears repeatedly, please open an issue.": ""
|
||||
}
|
||||
@@ -1,114 +1,230 @@
|
||||
{
|
||||
"Launch": "Iniciar",
|
||||
"[Applet Mode]": "[Modo Applet]",
|
||||
"No Internet": "Sem Internet",
|
||||
"Files": "",
|
||||
"Apps": "",
|
||||
"Store": "",
|
||||
"Menu": "Menu",
|
||||
"Options": "Opções",
|
||||
"Homebrew Options": "Opções do Homebrew",
|
||||
"OK": "",
|
||||
"Back": "Voltar",
|
||||
"Select": "",
|
||||
"Open": "Abrir",
|
||||
"Launch": "Iniciar",
|
||||
"Info": "Informações",
|
||||
"Install": "Instalar",
|
||||
"Delete": "Excluir",
|
||||
"Restart": "",
|
||||
"Changelog": "",
|
||||
"Details": "",
|
||||
"Update": "",
|
||||
"Remove": "",
|
||||
"Download": "Download",
|
||||
"Next Page": "Próxima página",
|
||||
"Prev Page": "Página anterior",
|
||||
"Unstar": "",
|
||||
"Star": "",
|
||||
"System memory": "",
|
||||
"microSD card": "",
|
||||
"Yes": "Sim",
|
||||
"No": "Não",
|
||||
"Enabled": "Habilitado",
|
||||
"Disabled": "Desabilitado",
|
||||
|
||||
"Sort By": "Ordenar por",
|
||||
"Sort Options": "Opções de classificação",
|
||||
"Updated": "Atualizado",
|
||||
"Size": "Tamanho",
|
||||
"Alphabetical": "Alfabético",
|
||||
"Decending": "Decrescente",
|
||||
"Ascending": "Ascendente",
|
||||
"Filter": "Filtro",
|
||||
"Sort": "Organizar",
|
||||
"Order": "Ordem",
|
||||
"Info": "Informações",
|
||||
"Delete": "Excluir",
|
||||
"Hide Sphaira": "Esconder Sphaira",
|
||||
"Are you sure you want to delete ": "Excluir ",
|
||||
"Install Forwarder": "Instalar forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "AVISO: Isso pode resultar em um banimento!",
|
||||
"Back": "Voltar",
|
||||
"Install": "Instalar",
|
||||
"Fs": "Fs",
|
||||
"App": "Aplicativo",
|
||||
"Menu": "Menu",
|
||||
"Homebrew": "Homebrew",
|
||||
"FileBrowser": "Navegador de arquivos",
|
||||
"Open": "Abrir",
|
||||
"Search": "Procurar",
|
||||
"Updated": "Atualizado",
|
||||
"Updated (Star)": "",
|
||||
"Downloads": "Downloads",
|
||||
"Size": "Tamanho",
|
||||
"Size (Star)": "",
|
||||
"Alphabetical": "Alfabético",
|
||||
"Alphabetical (Star)": "",
|
||||
"Likes": "",
|
||||
"ID": "",
|
||||
"Decending": "Decrescente",
|
||||
"Descending (down)": "Decrescente",
|
||||
"Desc": "Decrescente",
|
||||
"Ascending": "Ascendente",
|
||||
"Ascending (Up)": "Ascendente",
|
||||
"Asc": "Ascendente",
|
||||
|
||||
"Menu Options": "Opções do menu",
|
||||
"Header": "Cabeçalho",
|
||||
"Theme": "Tema",
|
||||
"Theme Options": "Opções de tema",
|
||||
"Select Theme": "Selecionar tema",
|
||||
"Shuffle": "Embaralhar",
|
||||
"Music": "Música",
|
||||
"Network": "Rede",
|
||||
"Network Options": "Opções de rede",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "",
|
||||
"Nxlink Upload": "",
|
||||
"Nxlink Finished": "",
|
||||
"Switch-Handheld!": "",
|
||||
"Switch-Docked!": "",
|
||||
"Language": "Idioma",
|
||||
"Auto": "",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "Logging",
|
||||
"Replace hbmenu on exit": "Substitua hbmenu ao sair",
|
||||
"Misc": "Diversos",
|
||||
"Misc Options": "Opções diversas",
|
||||
"Web": "Rede",
|
||||
"Install forwarders": "",
|
||||
"Install location": "",
|
||||
"Show install warning": "",
|
||||
|
||||
"FileBrowser": "Navegador de arquivos",
|
||||
"%zd files": "%zd files",
|
||||
"%zd dirs": "%zd dirs",
|
||||
"File Options": "Opções de arquivo",
|
||||
"Show Hidden": "Mostrar oculto",
|
||||
"Folders First": "Pastas primeiro",
|
||||
"Hidden Last": "Oculto por último",
|
||||
"Yes": "Sim",
|
||||
"No": "Não",
|
||||
"Network Options": "Opções de rede",
|
||||
"Nxlink": "Nxlink",
|
||||
"Check for update": "Verificar se há atualização",
|
||||
"File Options": "Opções de arquivo",
|
||||
"Cut": "Cortar",
|
||||
"Copy": "Copiar",
|
||||
"Paste": "",
|
||||
"Paste ": "",
|
||||
" file(s)?": "",
|
||||
"Rename": "Renomear",
|
||||
"Set New File Name": "",
|
||||
"Advanced": "Avançado",
|
||||
"Advanced Options": "Criar arquivo",
|
||||
"Create File": "Criar arquivo",
|
||||
"Set File Name": "",
|
||||
"Create Folder": "Criar pasta",
|
||||
"View as text": "Ver como texto",
|
||||
"Set Folder Name": "",
|
||||
"View as text (unfinished)": "Ver como texto (inacabado)",
|
||||
"Set Archive Bit": "Definir bit de arquivo",
|
||||
"Empty...": "",
|
||||
"Open with DayBreak?": "",
|
||||
"Launch ": "",
|
||||
"Launch option for: ": "",
|
||||
"Select launcher for: ": "",
|
||||
|
||||
"Homebrew": "Homebrew",
|
||||
"Homebrew Options": "Opções do Homebrew",
|
||||
"Hide Sphaira": "Esconder Sphaira",
|
||||
"Install Forwarder": "Instalar forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "AVISO: Isso pode resultar em um banimento!",
|
||||
"Installing Forwarder": "",
|
||||
"Creating Program": "",
|
||||
"Creating Control": "",
|
||||
"Creating Meta": "",
|
||||
"Writing Nca": "",
|
||||
"Updating ncm databse": "",
|
||||
"Pushing application record": "",
|
||||
"Installed!": "",
|
||||
"Failed to install forwarder": "",
|
||||
"Unstarred ": "",
|
||||
"Starred ": "",
|
||||
|
||||
"AppStore": "",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filtro: %s | Organizar: %s | Ordem: %s",
|
||||
"AppStore Options": "Opções da AppStore",
|
||||
"All": "Todos",
|
||||
"Games": "Jogos",
|
||||
"Emulators": "Emuladores",
|
||||
"Tools": "Ferramentas",
|
||||
"Advanced": "Avançado",
|
||||
"Themes": "Temas",
|
||||
"Legacy": "Legado",
|
||||
"Misc": "Diversos",
|
||||
"Downloads": "Downloads",
|
||||
"Filter": "Filtro",
|
||||
"Search": "Procurar",
|
||||
"Menu Options": "Opções do menu",
|
||||
"Header": "Cabeçalho",
|
||||
"Theme": "Tema",
|
||||
"Network": "Rede",
|
||||
"Logging": "Logging",
|
||||
"Enabled": "Habilitado",
|
||||
"Disabled": "Desabilitado",
|
||||
"Replace hbmenu on exit": "Substitua hbmenu ao sair",
|
||||
"Misc Options": "Opções diversas",
|
||||
"Themezer": "Themezer",
|
||||
"version: %s": "version: %s",
|
||||
"updated: %s": "updated: %s",
|
||||
"category: %s": "category: %s",
|
||||
"extracted: %.2f MiB": "extracted: %.2f MiB",
|
||||
"app_dls: %s": "app_dls: %s",
|
||||
"More by Author": "",
|
||||
"Leave Feedback": "",
|
||||
|
||||
"Irs": "Irs",
|
||||
"Web": "Rede",
|
||||
"Download": "Download",
|
||||
"Next Page": "Próxima página",
|
||||
"Prev Page": "Página anterior",
|
||||
"Ambient Noise Level: ": "",
|
||||
"Controller": "Controle",
|
||||
"Pad ": "Pad ",
|
||||
" (Available)": " (Disponível)",
|
||||
" (Unsupported)": "",
|
||||
" (Unconnected)": " (Desconectado)",
|
||||
"HandHeld": "Portátil",
|
||||
" (Available)": " (Disponível)",
|
||||
"Rotation": "Rotação",
|
||||
"0 (Sideways)": "0 (Lateralmente)",
|
||||
"90 (Flat)": "90 (plano)",
|
||||
"180 (-Sideways)": "180 (-Lateralmente)",
|
||||
"270 (Upside down)": "270 (De cabeça para baixo)",
|
||||
"Colour": "Cor",
|
||||
"Grey": "Cinza",
|
||||
"Ironbow": "Arco de ferro",
|
||||
"Green": "Verde",
|
||||
"Red": "Vermelho",
|
||||
"Blue": "Azul",
|
||||
"Light Target": "Alvo leve",
|
||||
"All leds": "Todos os LEDs",
|
||||
"Bright group": "Grupo claro",
|
||||
"Dim group": "Grupo escuro",
|
||||
"None": "Nenhum",
|
||||
"Normal image": "Imagem normal",
|
||||
"Negative image": "Imagem negativa",
|
||||
"320x240": "320x240",
|
||||
"160x120": "160x120",
|
||||
"80x60": "80x60",
|
||||
"40x30": "40x30",
|
||||
"20x15": "20x15",
|
||||
"Controller": "Controle",
|
||||
"Rotation": "Rotação",
|
||||
"Colour": "Cor",
|
||||
"Light Target": "Alvo leve",
|
||||
"Gain": "Ganho",
|
||||
"Negative Image": "Imagem negativa",
|
||||
"Normal image": "Imagem normal",
|
||||
"Negative image": "Imagem negativa",
|
||||
"Format": "Formatar",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "Formato de corte",
|
||||
"External Light Filter": "Filtro de luz externo",
|
||||
"Load Default": "Carregar padrão",
|
||||
"No Internet": "Sem Internet",
|
||||
"[Applet Mode]": "[Modo Applet]",
|
||||
"Language": "Idioma"
|
||||
}
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "",
|
||||
"Nsfw": "",
|
||||
"Page": "",
|
||||
"Page %zu / %zu": "Page %zu / %zu",
|
||||
"Enter Page Number": "",
|
||||
"Bad Page": "",
|
||||
"Download theme?": "",
|
||||
|
||||
"Installing ": "",
|
||||
"Uninstalling ": "",
|
||||
"Deleting ": "",
|
||||
"Deleting": "",
|
||||
"Pasting ": "",
|
||||
"Pasting": "",
|
||||
"Removing ": "",
|
||||
"Scanning ": "",
|
||||
"Creating ": "",
|
||||
"Copying ": "",
|
||||
"Trying to load ": "",
|
||||
"Downloading ": "",
|
||||
"Checking MD5": "",
|
||||
"Loading...": "",
|
||||
"Loading": "",
|
||||
"Empty!": "",
|
||||
"Not Ready...": "",
|
||||
"Error loading page!": "",
|
||||
"Update avaliable: ": "",
|
||||
"Download update: ": "",
|
||||
"Updated to ": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Failed to download update": "",
|
||||
"Delete Selected files?": "",
|
||||
"Completely remove ": "",
|
||||
"Are you sure you want to delete ": "Excluir ",
|
||||
"Are you sure you wish to cancel?": "",
|
||||
"If this message appears repeatedly, please open an issue.": ""
|
||||
}
|
||||
@@ -1,114 +1,230 @@
|
||||
{
|
||||
"[Applet Mode]": "[Режим апплета]",
|
||||
"No Internet": "Нет Интернета",
|
||||
"Files": "",
|
||||
"Apps": "",
|
||||
"Store": "",
|
||||
"Menu": "Меню",
|
||||
"Options": "Параметры темы",
|
||||
"OK": "",
|
||||
"Back": "Назад",
|
||||
"Select": "",
|
||||
"Open": "Открыть",
|
||||
"Launch": "Запуск",
|
||||
"Options": "Параметры",
|
||||
"Homebrew Options": "Варианты домашнего пивоварения",
|
||||
"Info": "Информация",
|
||||
"Install": "Установить",
|
||||
"Delete": "Удалить",
|
||||
"Restart": "",
|
||||
"Changelog": "",
|
||||
"Details": "",
|
||||
"Update": "",
|
||||
"Remove": "",
|
||||
"Download": "Скачать",
|
||||
"Next Page": "Следующая страница",
|
||||
"Prev Page": "Предыдущая страница",
|
||||
"Unstar": "",
|
||||
"Star": "",
|
||||
"System memory": "",
|
||||
"microSD card": "",
|
||||
"Yes": "Да",
|
||||
"No": "Нет",
|
||||
"Enabled": "Включено",
|
||||
"Disabled": "Отключено",
|
||||
|
||||
"Sort By": "Сортировать по",
|
||||
"Sort Options": "Параметры сортировки",
|
||||
"Updated": "Обновлено",
|
||||
"Size": "Размер",
|
||||
"Alphabetical": "Алфавитный",
|
||||
"Decending": "по убыванию",
|
||||
"Ascending": "восходящий",
|
||||
"Filter": "Фильтр",
|
||||
"Sort": "Сортировать",
|
||||
"Order": "Заказ",
|
||||
"Info": "Информация",
|
||||
"Delete": "Удалить",
|
||||
"Hide Sphaira": "Скрыть Сфаиру",
|
||||
"Are you sure you want to delete ": "Вы уверены, что хотите удалить ",
|
||||
"Install Forwarder": "Установить переадресатор",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ВНИМАНИЕ: Установка форвардеров приведет к бану!",
|
||||
"Back": "Назад",
|
||||
"Install": "Установить",
|
||||
"Fs": "Фс",
|
||||
"App": "Приложение",
|
||||
"Menu": "Меню",
|
||||
"Homebrew": "Домашнее пиво",
|
||||
"FileBrowser": "ФайлБраузер",
|
||||
"Open": "Открыть",
|
||||
"Order": "Порядок",
|
||||
"Search": "Поиск",
|
||||
"Updated": "Обновлено",
|
||||
"Updated (Star)": "",
|
||||
"Downloads": "Загрузки",
|
||||
"Size": "Размер",
|
||||
"Size (Star)": "",
|
||||
"Alphabetical": "По наименованию",
|
||||
"Alphabetical (Star)": "",
|
||||
"Likes": "",
|
||||
"ID": "",
|
||||
"Decending": "По убыванию",
|
||||
"Descending (down)": "По убыванию",
|
||||
"Desc": "По убыванию",
|
||||
"Ascending": "По возрастанию",
|
||||
"Ascending (Up)": "По возрастанию",
|
||||
"Asc": "По возрастанию",
|
||||
|
||||
"Menu Options": "Параметры меню",
|
||||
"Header": "Заголовок",
|
||||
"Theme": "Тема",
|
||||
"Theme Options": "Параметры темы",
|
||||
"Select Theme": "Выберите тему",
|
||||
"Shuffle": "Перетасовать",
|
||||
"Music": "Музыка",
|
||||
"Show Hidden": "Показать скрытое",
|
||||
"Folders First": "Папки в первую очередь",
|
||||
"Hidden Last": "Скрытый последний",
|
||||
"Yes": "Да",
|
||||
"No": "Нет",
|
||||
"Network": "Сеть",
|
||||
"Network Options": "Параметры сети",
|
||||
"Nxlink": "Нкслинк",
|
||||
"Check for update": "Проверить наличие обновлений",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "",
|
||||
"Nxlink Upload": "",
|
||||
"Nxlink Finished": "",
|
||||
"Switch-Handheld!": "",
|
||||
"Switch-Docked!": "",
|
||||
"Language": "Язык",
|
||||
"Auto": "",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "Журналирование",
|
||||
"Replace hbmenu on exit": "Заменить hbmenu при выходе",
|
||||
"Misc": "Прочее",
|
||||
"Misc Options": "Прочие параметры",
|
||||
"Web": "Интернет",
|
||||
"Install forwarders": "",
|
||||
"Install location": "",
|
||||
"Show install warning": "",
|
||||
|
||||
"FileBrowser": "Файловый менеджер",
|
||||
"%zd files": "%zd files",
|
||||
"%zd dirs": "%zd dirs",
|
||||
"File Options": "Параметры файла",
|
||||
"Cut": "Резать",
|
||||
"Show Hidden": "Показать скрытые",
|
||||
"Folders First": "Папки в первую очередь",
|
||||
"Hidden Last": "Скрытые в последнюю очередь",
|
||||
"Cut": "Вырезать",
|
||||
"Copy": "Копировать",
|
||||
"Paste": "",
|
||||
"Paste ": "",
|
||||
" file(s)?": "",
|
||||
"Rename": "Переименовать",
|
||||
"Advanced Options": "Создать файл",
|
||||
"Set New File Name": "",
|
||||
"Advanced": "Продвинутые",
|
||||
"Advanced Options": "Расширенные параметры",
|
||||
"Create File": "Создать файл",
|
||||
"Set File Name": "",
|
||||
"Create Folder": "Создать папку",
|
||||
"View as text": "Посмотреть как текст",
|
||||
"Set Folder Name": "",
|
||||
"View as text (unfinished)": "Посмотреть как текст (незакончено)",
|
||||
"Set Archive Bit": "Установить бит архива",
|
||||
"Empty...": "",
|
||||
"Open with DayBreak?": "",
|
||||
"Launch ": "",
|
||||
"Launch option for: ": "",
|
||||
"Select launcher for: ": "",
|
||||
|
||||
"Homebrew": "Homebrew",
|
||||
"Homebrew Options": "Параметры Homebrew",
|
||||
"Hide Sphaira": "Скрыть Sphaira",
|
||||
"Install Forwarder": "Установить форвардер",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ВНИМАНИЕ: Установка форвардеров приведет к бану!",
|
||||
"Installing Forwarder": "Установить форвардер",
|
||||
"Creating Program": "",
|
||||
"Creating Control": "",
|
||||
"Creating Meta": "",
|
||||
"Writing Nca": "",
|
||||
"Updating ncm databse": "",
|
||||
"Pushing application record": "",
|
||||
"Installed!": "",
|
||||
"Failed to install forwarder": "",
|
||||
"Unstarred ": "",
|
||||
"Starred ": "",
|
||||
|
||||
"AppStore": "",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Фильтр: %s | Сортировать: %s | Порядок: %s",
|
||||
"AppStore Options": "Параметры магазина приложений",
|
||||
"All": "Все",
|
||||
"Games": "Игры",
|
||||
"Emulators": "Эмуляторы",
|
||||
"Tools": "Инструменты",
|
||||
"Advanced": "Передовой",
|
||||
"Themes": "Темы",
|
||||
"Legacy": "Наследие",
|
||||
"Misc": "Разное",
|
||||
"Downloads": "Загрузки",
|
||||
"Filter": "Фильтр",
|
||||
"Search": "Поиск",
|
||||
"Menu Options": "Опции меню",
|
||||
"Header": "Заголовок",
|
||||
"Theme": "Тема",
|
||||
"Network": "Сеть",
|
||||
"Logging": "Ведение журнала",
|
||||
"Enabled": "Включено",
|
||||
"Disabled": "Неполноценный",
|
||||
"Replace hbmenu on exit": "Заменить hbmenu при выходе",
|
||||
"Misc Options": "Разные параметры",
|
||||
"Themezer": "Темезер",
|
||||
"Irs": "IRS",
|
||||
"Web": "Интернет",
|
||||
"Download": "Скачать",
|
||||
"Next Page": "Следующая страница",
|
||||
"Prev Page": "Предыдущая страница",
|
||||
"Pad ": "Подушка ",
|
||||
"Legacy": "Легаси",
|
||||
"version: %s": "version: %s",
|
||||
"updated: %s": "updated: %s",
|
||||
"category: %s": "category: %s",
|
||||
"extracted: %.2f MiB": "extracted: %.2f MiB",
|
||||
"app_dls: %s": "app_dls: %s",
|
||||
"More by Author": "",
|
||||
"Leave Feedback": "",
|
||||
|
||||
"Irs": "Irs",
|
||||
"Ambient Noise Level: ": "",
|
||||
"Controller": "Контроллер",
|
||||
"Pad ": "Pad ",
|
||||
" (Available)": " (Доступно)",
|
||||
" (Unsupported)": "",
|
||||
" (Unconnected)": " (Не подключено)",
|
||||
"HandHeld": "Ручной",
|
||||
" (Available)": " (Доступный)",
|
||||
"0 (Sideways)": "0 (вбок)",
|
||||
"90 (Flat)": "90 (квартира)",
|
||||
"HandHeld": "Портативный",
|
||||
"Rotation": "Вращение",
|
||||
"0 (Sideways)": "0 (набок)",
|
||||
"90 (Flat)": "90 (ровно)",
|
||||
"180 (-Sideways)": "180 (-вбок)",
|
||||
"270 (Upside down)": "270 (перевернутый)",
|
||||
"270 (Upside down)": "270 (перевернуто)",
|
||||
"Colour": "Цвет",
|
||||
"Grey": "Серый",
|
||||
"Ironbow": "Железный лук",
|
||||
"Ironbow": "Стальной",
|
||||
"Green": "Зеленый",
|
||||
"Red": "Красный",
|
||||
"Blue": "Синий",
|
||||
"Light Target": "Световая мишень",
|
||||
"All leds": "Все светодиоды",
|
||||
"Bright group": "Яркая группа",
|
||||
"Dim group": "Тусклая группа",
|
||||
"None": "Никто",
|
||||
"Normal image": "Обычное изображение",
|
||||
"Negative image": "Негативный имидж",
|
||||
"320x240": "320x240",
|
||||
"160x120": "160x120",
|
||||
"80x60": "80х60",
|
||||
"40x30": "40x30",
|
||||
"20x15": "20x15",
|
||||
"Controller": "Контроллер",
|
||||
"Rotation": "Вращение",
|
||||
"Colour": "Цвет",
|
||||
"Light Target": "Легкая мишень",
|
||||
"Gain": "Прирост",
|
||||
"Negative Image": "Негативное изображение",
|
||||
"Normal image": "Обычное изображение",
|
||||
"Negative image": "Негативное изображение",
|
||||
"Format": "Формат",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "Формат обрезки",
|
||||
"External Light Filter": "Внешний светофильтр",
|
||||
"Load Default": "Загрузить по умолчанию",
|
||||
"No Internet": "Нет Интернета",
|
||||
"[Applet Mode]": "[Режим апплета]",
|
||||
"Language": "Язык"
|
||||
}
|
||||
"Load Default": "Загрузить умолчания",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "",
|
||||
"Nsfw": "",
|
||||
"Page": "",
|
||||
"Page %zu / %zu": "Page %zu / %zu",
|
||||
"Enter Page Number": "",
|
||||
"Bad Page": "",
|
||||
"Download theme?": "",
|
||||
|
||||
"Installing ": "",
|
||||
"Uninstalling ": "",
|
||||
"Deleting ": "",
|
||||
"Deleting": "",
|
||||
"Pasting ": "",
|
||||
"Pasting": "",
|
||||
"Removing ": "",
|
||||
"Scanning ": "",
|
||||
"Creating ": "",
|
||||
"Copying ": "",
|
||||
"Trying to load ": "",
|
||||
"Downloading ": "",
|
||||
"Checking MD5": "",
|
||||
"Loading...": "",
|
||||
"Loading": "",
|
||||
"Empty!": "",
|
||||
"Not Ready...": "",
|
||||
"Error loading page!": "",
|
||||
"Update avaliable: ": "",
|
||||
"Download update: ": "",
|
||||
"Updated to ": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Failed to download update": "",
|
||||
"Delete Selected files?": "",
|
||||
"Completely remove ": "",
|
||||
"Are you sure you want to delete ": "Вы уверены, что хотите удалить ",
|
||||
"Are you sure you wish to cancel?": "",
|
||||
"If this message appears repeatedly, please open an issue.": ""
|
||||
}
|
||||
230
assets/romfs/i18n/se.json
Normal file
230
assets/romfs/i18n/se.json
Normal file
@@ -0,0 +1,230 @@
|
||||
{
|
||||
"[Applet Mode]": "[Applet-läge]",
|
||||
"No Internet": "Ingen internetanslutning",
|
||||
"Files": "Filer",
|
||||
"Apps": "Appar",
|
||||
"Store": "Butik",
|
||||
"Menu": "Meny",
|
||||
"Options": "Alternativ",
|
||||
"OK": "OK",
|
||||
"Back": "Tillbaka",
|
||||
"Select": "Välj",
|
||||
"Open": "Öppna",
|
||||
"Launch": "Starta",
|
||||
"Info": "Info",
|
||||
"Install": "Installera",
|
||||
"Delete": "Radera",
|
||||
"Restart": "",
|
||||
"Changelog": "Ändringslogg",
|
||||
"Details": "Detaljer",
|
||||
"Update": "Uppdatera",
|
||||
"Remove": "Ta bort",
|
||||
"Download": "Ladda ner",
|
||||
"Next Page": "Nästa sida",
|
||||
"Prev Page": "Föregående sida",
|
||||
"Unstar": "",
|
||||
"Star": "",
|
||||
"System memory": "",
|
||||
"microSD card": "",
|
||||
"Yes": "Ja",
|
||||
"No": "Nej",
|
||||
"Enabled": "Aktiverad",
|
||||
"Disabled": "Avaktiverad",
|
||||
|
||||
"Sort By": "Sortera efter",
|
||||
"Sort Options": "Sorteringsalternativ",
|
||||
"Filter": "Filter",
|
||||
"Sort": "Sortera",
|
||||
"Order": "Ordning",
|
||||
"Search": "Sök",
|
||||
"Updated": "Uppdaterad",
|
||||
"Updated (Star)": "",
|
||||
"Downloads": "Nedladdningar",
|
||||
"Size": "Storlek",
|
||||
"Size (Star)": "",
|
||||
"Alphabetical": "Alfabetisk",
|
||||
"Alphabetical (Star)": "",
|
||||
"Likes": "Gillar",
|
||||
"ID": "ID",
|
||||
"Decending": "Fallande",
|
||||
"Descending (down)": "Fallande (nedåt)",
|
||||
"Desc": "Fallande",
|
||||
"Ascending": "Stigande",
|
||||
"Ascending (Up)": "Stigande (uppåt)",
|
||||
"Asc": "Stigande",
|
||||
|
||||
"Menu Options": "Menyalternativ",
|
||||
"Header": "Rubrik",
|
||||
"Theme": "Tema",
|
||||
"Theme Options": "Temaalternativ",
|
||||
"Select Theme": "Välj tema",
|
||||
"Shuffle": "Blanda",
|
||||
"Music": "Musik",
|
||||
"Network": "Nätverk",
|
||||
"Network Options": "Nätverksalternativ",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "Nxlink ansluten",
|
||||
"Nxlink Upload": "Nxlink uppladdning",
|
||||
"Nxlink Finished": "Nxlink klar",
|
||||
"Switch-Handheld!": "",
|
||||
"Switch-Docked!": "",
|
||||
"Language": "Språk",
|
||||
"Auto": "Auto",
|
||||
"English": "Engelska",
|
||||
"Japanese": "Japanska",
|
||||
"French": "Franska",
|
||||
"German": "Tyska",
|
||||
"Italian": "Italienska",
|
||||
"Spanish": "Spanska",
|
||||
"Chinese": "Kinesiska",
|
||||
"Korean": "Koreanska",
|
||||
"Dutch": "Holländska",
|
||||
"Portuguese": "Portugisiska",
|
||||
"Russian": "Ryska",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "Loggning",
|
||||
"Replace hbmenu on exit": "Ersätt hbmenu vid avslut",
|
||||
"Misc": "Övrigt",
|
||||
"Misc Options": "Övriga alternativ",
|
||||
"Web": "Webb",
|
||||
"Install forwarders": "",
|
||||
"Install location": "",
|
||||
"Show install warning": "",
|
||||
|
||||
"FileBrowser": "Filbläddrare",
|
||||
"%zd files": "%zd filer",
|
||||
"%zd dirs": "%zd kataloger",
|
||||
"File Options": "Filalternativ",
|
||||
"Show Hidden": "Visa dolda",
|
||||
"Folders First": "Mappar först",
|
||||
"Hidden Last": "Dolda sist",
|
||||
"Cut": "Klipp ut",
|
||||
"Copy": "Kopiera",
|
||||
"Paste": "Klistra in",
|
||||
"Paste ": "Klistra in ",
|
||||
" file(s)?": " fil(er)?",
|
||||
"Rename": "Byt namn",
|
||||
"Set New File Name": "Ange nytt filnamn",
|
||||
"Advanced": "Avancerat",
|
||||
"Advanced Options": "Avancerade alternativ",
|
||||
"Create File": "Skapa fil",
|
||||
"Set File Name": "Ange filnamn",
|
||||
"Create Folder": "Skapa mapp",
|
||||
"Set Folder Name": "Ange mappnamn",
|
||||
"View as text (unfinished)": "Visa som text (ofärdig)",
|
||||
"Empty...": "Tom...",
|
||||
"Open with DayBreak?": "Öppna med DayBreak?",
|
||||
"Launch ": "",
|
||||
"Launch option for: ": "Startalternativ för: ",
|
||||
"Select launcher for: ": "",
|
||||
|
||||
"Homebrew": "Homebrew",
|
||||
"Homebrew Options": "Homebrew-alternativ",
|
||||
"Hide Sphaira": "Dölj Sphaira",
|
||||
"Install Forwarder": "Installera forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "VARNING: Att installera forwarders leder till en avstängning!",
|
||||
"Installing Forwarder": "Installerar forwarder",
|
||||
"Creating Program": "Skapar program",
|
||||
"Creating Control": "Skapar kontroll",
|
||||
"Creating Meta": "Skapar meta",
|
||||
"Writing Nca": "Skriver Nca",
|
||||
"Updating ncm databse": "Uppdaterar ncm-databas",
|
||||
"Pushing application record": "Lägger till applikationspost",
|
||||
"Installed!": "Installerad!",
|
||||
"Failed to install forwarder": "Misslyckades med att installera forwarder",
|
||||
"Unstarred ": "",
|
||||
"Starred ": "",
|
||||
|
||||
"AppStore": "AppStore",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filter: %s | Sortering: %s | Ordning: %s",
|
||||
"AppStore Options": "AppStore-alternativ",
|
||||
"All": "Alla",
|
||||
"Games": "Spel",
|
||||
"Emulators": "Emulatorer",
|
||||
"Tools": "Verktyg",
|
||||
"Themes": "Teman",
|
||||
"Legacy": "Legacy",
|
||||
"version: %s": "version: %s",
|
||||
"updated: %s": "uppdaterad: %s",
|
||||
"category: %s": "kategori: %s",
|
||||
"extracted: %.2f MiB": "extraherad: %.2f MiB",
|
||||
"app_dls: %s": "app_nedladdningar: %s",
|
||||
"More by Author": "Mer från författaren",
|
||||
"Leave Feedback": "Lämna feedback",
|
||||
|
||||
"Irs": "Irs",
|
||||
"Ambient Noise Level: ": "Omgivningsljudnivå: ",
|
||||
"Controller": "Kontroll",
|
||||
"Pad ": "Handkontroll ",
|
||||
" (Available)": " (Tillgänglig)",
|
||||
" (Unsupported)": "",
|
||||
" (Unconnected)": " (Ej ansluten)",
|
||||
"HandHeld": "Handhållen",
|
||||
"Rotation": "Rotation",
|
||||
"0 (Sideways)": "0 (Sido)",
|
||||
"90 (Flat)": "90 (Platt)",
|
||||
"180 (-Sideways)": "180 (-Sido)",
|
||||
"270 (Upside down)": "270 (Upp och ner)",
|
||||
"Colour": "Färg",
|
||||
"Grey": "Grå",
|
||||
"Ironbow": "Ironbow",
|
||||
"Green": "Grön",
|
||||
"Red": "Röd",
|
||||
"Blue": "Blå",
|
||||
"Light Target": "Ljusmål",
|
||||
"All leds": "Alla lysdioder",
|
||||
"Bright group": "Ljusstark grupp",
|
||||
"Dim group": "Dämpad grupp",
|
||||
"None": "Ingen",
|
||||
"Gain": "Förstärkning",
|
||||
"Negative Image": "Negativ bild",
|
||||
"Normal image": "Normal bild",
|
||||
"Negative image": "Negativ bild",
|
||||
"Format": "Format",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "Trimformat",
|
||||
"External Light Filter": "Extern ljusfilter",
|
||||
"Load Default": "Ladda standardinställningar",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Themezer-alternativ",
|
||||
"Nsfw": "Nsfw",
|
||||
"Page": "Sida",
|
||||
"Page %zu / %zu": "Sida %zu / %zu",
|
||||
"Enter Page Number": "Ange sidnummer",
|
||||
"Bad Page": "Ogiltig sida",
|
||||
"Download theme?": "Ladda ner tema?",
|
||||
|
||||
"Installing ": "Installerar ",
|
||||
"Uninstalling ": "Avinstallerar ",
|
||||
"Deleting ": "Raderar ",
|
||||
"Deleting": "Raderar",
|
||||
"Pasting ": "Klistrar in ",
|
||||
"Pasting": "Klistrar in",
|
||||
"Removing ": "Tar bort ",
|
||||
"Scanning ": "Skannar ",
|
||||
"Creating ": "Skapar ",
|
||||
"Copying ": "Kopierar ",
|
||||
"Trying to load ": "",
|
||||
"Downloading ": "Laddar ner ",
|
||||
"Checking MD5": "Kontrollerar MD5",
|
||||
"Loading...": "Laddar...",
|
||||
"Loading": "Laddar",
|
||||
"Empty!": "Tomt!",
|
||||
"Not Ready...": "Ej redo...",
|
||||
"Error loading page!": "Fel vid laddning av sida!",
|
||||
"Update avaliable: ": "Uppdatering tillgänglig: ",
|
||||
"Download update: ": "Ladda ner uppdatering: ",
|
||||
"Updated to ": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Failed to download update": "Misslyckades med att ladda ner uppdatering",
|
||||
"Delete Selected files?": "Radera valda filer?",
|
||||
"Completely remove ": "Ta bort helt ",
|
||||
"Are you sure you want to delete ": "Är du säker på att du vill radera ",
|
||||
"Are you sure you wish to cancel?": "Är du säker på att du vill avbryta?",
|
||||
"If this message appears repeatedly, please open an issue.": ""
|
||||
}
|
||||
@@ -1,115 +1,230 @@
|
||||
{
|
||||
"Launch": "启动",
|
||||
"[Applet Mode]": "[小程序模式]",
|
||||
"No Internet": "网络未连接",
|
||||
"Files": "文件",
|
||||
"Apps": "应用",
|
||||
"Store": "商店",
|
||||
"Menu": "菜单",
|
||||
"Options": "选项",
|
||||
"Homebrew Options": "插件选项",
|
||||
"OK": "确定",
|
||||
"Back": "返回",
|
||||
"Select": "选择",
|
||||
"Open": "打开",
|
||||
"Launch": "启动",
|
||||
"Info": "信息",
|
||||
"Install": "安装",
|
||||
"Delete": "删除",
|
||||
"Restart": "",
|
||||
"Changelog": "更新日志",
|
||||
"Details": "详情",
|
||||
"Update": "更新",
|
||||
"Remove": "删除",
|
||||
"Download": "下载",
|
||||
"Next Page": "下一页",
|
||||
"Prev Page": "上一页",
|
||||
"Unstar": "取消星标",
|
||||
"Star": "星标",
|
||||
"System memory": "主机内存",
|
||||
"microSD card": "SD卡",
|
||||
"Yes": "是",
|
||||
"No": "否",
|
||||
"Enabled": "启用",
|
||||
"Disabled": "禁用",
|
||||
|
||||
"Sort By": "排序方式",
|
||||
"Sort Options": "排序选项",
|
||||
"Updated": "最近使用",
|
||||
"Size": "大小",
|
||||
"Alphabetical": "按字母顺序",
|
||||
"Decending": "降序",
|
||||
"Ascending": "升序",
|
||||
"Filter": "筛选",
|
||||
"Sort": "排序",
|
||||
"Order": "顺序",
|
||||
"Info": "信息",
|
||||
"Delete": "删除",
|
||||
"Hide Sphaira": "在插件列表中隐藏Sphaira",
|
||||
"Are you sure you want to delete ": "您确定要删除吗 ",
|
||||
"Install Forwarder": "安装前端应用",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "警告:安装前端应用可能导致ban机!",
|
||||
"Back": "返回",
|
||||
"Install": "安装",
|
||||
"Fs": "文件系统",
|
||||
"App": "插件",
|
||||
"Menu": "菜单",
|
||||
"Homebrew": "插件列表",
|
||||
"AppStore": "插件商店",
|
||||
"FileBrowser": "文件浏览",
|
||||
"Open": "打开",
|
||||
"Search": "搜索",
|
||||
"Updated": "最近使用",
|
||||
"Updated (Star)": "最近更新(星标优先)",
|
||||
"Downloads": "下载",
|
||||
"Size": "按大小",
|
||||
"Size (Star)": "按大小(星标优先)",
|
||||
"Alphabetical": "按字母顺序",
|
||||
"Alphabetical (Star)": "按字母顺序(星标优先)",
|
||||
"Likes": "点赞量",
|
||||
"ID": "ID",
|
||||
"Decending": "降序",
|
||||
"Descending (down)": "降序",
|
||||
"Desc": "降序",
|
||||
"Ascending": "升序",
|
||||
"Ascending (Up)": "升序",
|
||||
"Asc": "升序",
|
||||
|
||||
"Menu Options": "菜单选项",
|
||||
"Header": "标题",
|
||||
"Theme": "主题",
|
||||
"Theme Options": "主题选项",
|
||||
"Select Theme": "选择主题",
|
||||
"Shuffle": "随机播放",
|
||||
"Music": "音乐",
|
||||
"Network": "网络",
|
||||
"Network Options": "网络选项",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "Nxlink 已连接",
|
||||
"Nxlink Upload": "Nxlink 上传中",
|
||||
"Nxlink Finished": "Nxlink 已结束",
|
||||
"Switch-Handheld!": "",
|
||||
"Switch-Docked!": "",
|
||||
"Language": "语言",
|
||||
"Auto": "自动",
|
||||
"English": "English",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italiano",
|
||||
"Spanish": "Español",
|
||||
"Chinese": "中文",
|
||||
"Korean": "한국어",
|
||||
"Dutch": "Dutch",
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "日志",
|
||||
"Replace hbmenu on exit": "退出后用Sphaira替换hbmenu",
|
||||
"Misc": "杂项",
|
||||
"Misc Options": "杂项设置",
|
||||
"Web": "网页浏览器",
|
||||
"Install forwarders": "允许安装前端应用",
|
||||
"Install location": "安装位置",
|
||||
"Show install warning": "显示安装警告",
|
||||
|
||||
"FileBrowser": "文件浏览",
|
||||
"%zd files": "%zd 个文件",
|
||||
"%zd dirs": "%zd 个文件夹",
|
||||
"File Options": "文件选项",
|
||||
"Show Hidden": "显示隐藏项目",
|
||||
"Folders First": "文件夹靠前",
|
||||
"Hidden Last": "隐藏项目置后",
|
||||
"Yes": "是",
|
||||
"No": "否",
|
||||
"Network Options": "网络选项",
|
||||
"Nxlink": "Nxlink开发工具",
|
||||
"Check for update": "检查更新",
|
||||
"File Options": "文件选项",
|
||||
"Cut": "剪切",
|
||||
"Copy": "复制",
|
||||
"Paste": "粘贴",
|
||||
"Paste ": "粘贴 ",
|
||||
" file(s)?": "个文件(夹)?",
|
||||
"Rename": "重命名",
|
||||
"Set New File Name": "输入新命名",
|
||||
"Advanced": "高级",
|
||||
"Advanced Options": "高级选项",
|
||||
"Create File": "新建文件",
|
||||
"Set File Name": "输入文件名",
|
||||
"Create Folder": "新建文件夹",
|
||||
"View as text": "以文本形式查看",
|
||||
"Set Folder Name": "输入文件夹名",
|
||||
"View as text (unfinished)": "以文本形式查看(未完善)",
|
||||
"Set Archive Bit": "设置存档标志",
|
||||
"AppStore Options": "插件商店选项",
|
||||
"Empty...": "空...",
|
||||
"Open with DayBreak?": "使用DayBreak打开?",
|
||||
"Launch ": "",
|
||||
"Launch option for: ": "启动选项:",
|
||||
"Select launcher for: ": "",
|
||||
|
||||
"Homebrew": "应用列表",
|
||||
"Homebrew Options": "应用选项",
|
||||
"Hide Sphaira": "在应用列表中隐藏Sphaira",
|
||||
"Install Forwarder": "安装前端应用",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "警告:安装前端应用可能导致ban机!",
|
||||
"Installing Forwarder": "正在生成前端应用",
|
||||
"Creating Program": "正在创建程序",
|
||||
"Creating Control": "正在创建控制器",
|
||||
"Creating Meta": "正在创建元数据",
|
||||
"Writing Nca": "正在写入Nca",
|
||||
"Updating ncm databse": "正在更新ncm数据库",
|
||||
"Pushing application record": "正在推送应用记录",
|
||||
"Installed!": "安装完成!",
|
||||
"Failed to install forwarder": "前端应用安装失败",
|
||||
"Unstarred ": "",
|
||||
"Starred ": "",
|
||||
|
||||
"AppStore": "应用商店",
|
||||
"Filter: %s | Sort: %s | Order: %s": "筛选: %s | 排序: %s | 顺序: %s",
|
||||
"AppStore Options": "应用商店选项",
|
||||
"All": "全部",
|
||||
"Games": "游戏",
|
||||
"Emulators": "模拟器",
|
||||
"Tools": "工具",
|
||||
"Advanced": "高级",
|
||||
"Themes": "主题",
|
||||
"Legacy": "可更新",
|
||||
"Misc": "杂项",
|
||||
"Downloads": "下载",
|
||||
"Filter": "筛选",
|
||||
"Search": "搜索",
|
||||
"Menu Options": "菜单选项",
|
||||
"Header": "标题",
|
||||
"Theme": "主题",
|
||||
"Network": "网络",
|
||||
"Logging": "日志",
|
||||
"Enabled": "启用",
|
||||
"Disabled": "禁用",
|
||||
"Replace hbmenu on exit": "退出后用Sphaira替换hbmenu",
|
||||
"Misc Options": "杂项设置",
|
||||
"Themezer": "在线主题",
|
||||
"version: %s": "版本: %s",
|
||||
"updated: %s": "更新时间: %s",
|
||||
"category: %s": "分类: %s",
|
||||
"extracted: %.2f MiB": "应用大小: %.2f MiB",
|
||||
"app_dls: %s": "下载量: %s",
|
||||
"More by Author": "作者更多作品",
|
||||
"Leave Feedback": "留言反馈",
|
||||
|
||||
"Irs": "红外成像",
|
||||
"Web": "网页浏览器",
|
||||
"Download": "下载",
|
||||
"Next Page": "下一页",
|
||||
"Prev Page": "上一页",
|
||||
"Ambient Noise Level: ": "环境噪声等级:",
|
||||
"Controller": "控制器",
|
||||
"Pad ": "手柄 ",
|
||||
" (Unconnected)": " (未连接)",
|
||||
"HandHeld": "手持式",
|
||||
" (Available)": " (可用的)",
|
||||
" (Unsupported)": "",
|
||||
" (Unconnected)": " (未连接)",
|
||||
"HandHeld": "掌机模式",
|
||||
"Rotation": "旋转",
|
||||
"0 (Sideways)": "0度",
|
||||
"90 (Flat)": "90度",
|
||||
"180 (-Sideways)": "180度",
|
||||
"270 (Upside down)": "270度",
|
||||
"Colour": "颜色",
|
||||
"Grey": "灰色",
|
||||
"Ironbow": "紫黄",
|
||||
"Green": "绿色",
|
||||
"Red": "红色",
|
||||
"Blue": "蓝色",
|
||||
"Light Target": "光源目标",
|
||||
"All leds": "全部",
|
||||
"Bright group": "亮色组",
|
||||
"Dim group": "暗色组",
|
||||
"None": "无",
|
||||
"Normal image": "正常图像",
|
||||
"Negative image": "负片图像",
|
||||
"320x240": "320x240",
|
||||
"160x120": "160x120",
|
||||
"80x60": "80x60",
|
||||
"40x30": "40x30",
|
||||
"20x15": "20x15",
|
||||
"Controller": "控制器",
|
||||
"Rotation": "旋转",
|
||||
"Colour": "颜色",
|
||||
"Light Target": "光源目标",
|
||||
"Gain": "增益",
|
||||
"Negative Image": "负片图像",
|
||||
"Normal image": "正常图像",
|
||||
"Negative image": "负片图像",
|
||||
"Format": "格式",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "裁剪格式",
|
||||
"External Light Filter": "外部光滤镜",
|
||||
"Load Default": "加载默认值",
|
||||
"No Internet": "网络未连接",
|
||||
"[Applet Mode]": "[小程序模式]",
|
||||
"Language": "语言"
|
||||
}
|
||||
|
||||
"Themezer": "在线主题",
|
||||
"Themezer Options": "在线主题选项",
|
||||
"Nsfw": "公共场合不宜的主题",
|
||||
"Page": "页面",
|
||||
"Page %zu / %zu": "页面 %zu / %zu",
|
||||
"Enter Page Number": "输入跳转的页码",
|
||||
"Bad Page": "错误的页面",
|
||||
"Download theme?": "下载该主题?",
|
||||
|
||||
"Installing ": "正在安装 ",
|
||||
"Uninstalling ": "正在卸载 ",
|
||||
"Deleting ": "正在删除 ",
|
||||
"Deleting": "正在删除",
|
||||
"Pasting ": "正在粘贴 ",
|
||||
"Pasting": "正在粘贴",
|
||||
"Removing ": "正在移除 ",
|
||||
"Scanning ": "正在扫描 ",
|
||||
"Creating ": "正在创建 ",
|
||||
"Copying ": "正在复制 ",
|
||||
"Trying to load ": "",
|
||||
"Downloading ": "正在下载 ",
|
||||
"Checking MD5": "正在校验 MD5",
|
||||
"Loading...": "加载中...",
|
||||
"Loading": "加载中",
|
||||
"Empty!": "空!",
|
||||
"Not Ready...": "尚未准备好...",
|
||||
"Error loading page!": "页面加载失败!",
|
||||
"Update avaliable: ": "有可用更新!",
|
||||
"Download update: ": "下载更新:",
|
||||
"Updated to ": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Failed to download update": "更新下载失败",
|
||||
"Delete Selected files?": "删除选中的文件?",
|
||||
"Completely remove ": "彻底删除 ",
|
||||
"Are you sure you want to delete ": "您确定要删除吗 ",
|
||||
"Are you sure you wish to cancel?": "您确定要取消吗?",
|
||||
"If this message appears repeatedly, please open an issue.": ""
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
[meta]
|
||||
name="White not finished"
|
||||
author=TotalJustice
|
||||
version=1.0.0
|
||||
preview=romfs:/theme/preview.jpg
|
||||
|
||||
[theme]
|
||||
background=0xEBEBEBff
|
||||
cursor=romfs:/theme/cursor.png
|
||||
cursor_drag=romfs:/theme/cursor_drag.png
|
||||
grid=0x46464630
|
||||
selected=0x464646ff
|
||||
selected_overlay=0x00ffc8ff
|
||||
text=0x2D2D2Dff
|
||||
text_selected=0x3A50F0ff
|
||||
|
||||
icon_audio=romfs:/theme/icon_audio.png
|
||||
icon_video=romfs:/theme/icon_video.png
|
||||
icon_image=romfs:/theme/icon_image.png
|
||||
icon_file=romfs:/theme/icon_file.png
|
||||
icon_folder=romfs:/theme/icon_folder.png
|
||||
icon_zip=romfs:/theme/icon_zip.png
|
||||
icon_nro=romfs:/theme/icon_nro.png
|
||||
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
set(sphaira_VERSION 0.3.0)
|
||||
set(sphaira_VERSION 0.5.0)
|
||||
|
||||
project(sphaira
|
||||
VERSION ${sphaira_VERSION}
|
||||
@@ -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 "$<$<CONFIG:Debug>:-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
|
||||
|
||||
@@ -42,14 +42,16 @@ public:
|
||||
void Loop();
|
||||
|
||||
static void Exit();
|
||||
static void ExitRestart();
|
||||
static auto GetVg() -> NVGcontext*;
|
||||
static void Push(std::shared_ptr<ui::Widget>);
|
||||
|
||||
// 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<ThemeMeta>;
|
||||
static void SetTheme(u64 theme_index);
|
||||
@@ -60,18 +62,26 @@ 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;
|
||||
static auto GetInstallEnable() -> bool;
|
||||
static auto GetInstallSdEnable() -> bool;
|
||||
static auto GetInstallPrompt() -> bool;
|
||||
static auto GetThemeShuffleEnable() -> bool;
|
||||
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);
|
||||
static void SetInstallEnable(bool enable);
|
||||
static void SetInstallSdEnable(bool enable);
|
||||
static void SetInstallPrompt(bool enable);
|
||||
static void SetThemeShuffleEnable(bool enable);
|
||||
static void SetThemeMusicEnable(bool enable);
|
||||
static void SetLanguage(long index);
|
||||
@@ -85,9 +95,6 @@ public:
|
||||
void Update();
|
||||
void Poll();
|
||||
|
||||
void DrawBackground();
|
||||
void DrawTouch();
|
||||
|
||||
// void DrawElement(float x, float y, float w, float h, ui::ThemeEntryID id);
|
||||
auto LoadElementImage(std::string_view value) -> ElementEntry;
|
||||
auto LoadElementColour(std::string_view value) -> ElementEntry;
|
||||
@@ -136,9 +143,13 @@ 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};
|
||||
option::OptionBool m_install_sd{INI_SECTION, "install_sd", true};
|
||||
option::OptionLong m_install_prompt{INI_SECTION, "install_prompt", true};
|
||||
option::OptionBool m_theme_shuffle{INI_SECTION, "theme_shuffle", false};
|
||||
option::OptionBool m_theme_music{INI_SECTION, "theme_music", true};
|
||||
option::OptionLong m_language{INI_SECTION, "language", 0}; // auto
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#include <switch.h>
|
||||
#include <nxlink.h>
|
||||
#include <haze.h>
|
||||
#include "download.hpp"
|
||||
|
||||
namespace sphaira::evman {
|
||||
@@ -23,6 +24,7 @@ struct ExitEventData {
|
||||
using EventData = std::variant<
|
||||
LaunchNroEventData,
|
||||
ExitEventData,
|
||||
HazeCallbackData,
|
||||
NxlinkCallbackData,
|
||||
DownloadEventData
|
||||
>;
|
||||
|
||||
33
sphaira/include/ftpsrv_helper.hpp
Normal file
33
sphaira/include/ftpsrv_helper.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
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
|
||||
@@ -7,6 +7,8 @@ namespace sphaira::i18n {
|
||||
bool init(long index);
|
||||
void exit();
|
||||
|
||||
std::string get(const char* str);
|
||||
|
||||
} // namespace sphaira::i18n
|
||||
|
||||
inline namespace literals {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <optional>
|
||||
#include "fs.hpp"
|
||||
|
||||
namespace sphaira {
|
||||
@@ -28,6 +29,7 @@ struct NroEntry {
|
||||
int image{}; // nvg image
|
||||
int x,y,w,h{}; // image
|
||||
bool is_nacp_valid{};
|
||||
std::optional<bool> has_star{std::nullopt};
|
||||
|
||||
auto GetName() const -> const char* {
|
||||
return nacp.lang[0].name;
|
||||
|
||||
@@ -110,19 +110,18 @@ private:
|
||||
void LoadAssocEntriesPath(const fs::FsPath& path);
|
||||
void LoadAssocEntries();
|
||||
auto FindFileAssocFor() -> std::vector<FileAssocEntry>;
|
||||
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<FileEntry> {
|
||||
if (!m_selected_count) {
|
||||
|
||||
@@ -9,8 +9,11 @@ namespace sphaira::ui::menu::homebrew {
|
||||
|
||||
enum SortType {
|
||||
SortType_Updated,
|
||||
SortType_Size,
|
||||
SortType_Alphabetical,
|
||||
SortType_Size,
|
||||
SortType_UpdatedStar,
|
||||
SortType_AlphabeticalStar,
|
||||
SortType_SizeStar,
|
||||
};
|
||||
|
||||
enum OrderType {
|
||||
@@ -36,6 +39,10 @@ struct Menu final : MenuBase {
|
||||
return m_entries;
|
||||
}
|
||||
|
||||
auto IsStarEnabled() -> bool {
|
||||
return m_sort.Get() >= SortType_UpdatedStar;
|
||||
}
|
||||
|
||||
static Result InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector<u8>& icon);
|
||||
static Result InstallHomebrewFromPath(const fs::FsPath& path);
|
||||
|
||||
@@ -46,9 +53,9 @@ private:
|
||||
std::size_t m_start{};
|
||||
std::size_t m_index{}; // where i am in the array
|
||||
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_Updated};
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_AlphabeticalStar};
|
||||
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Decending};
|
||||
option::OptionBool m_hide_sphaira{INI_SECTION, "hide_sphaira", false};}
|
||||
;
|
||||
option::OptionBool m_hide_sphaira{INI_SECTION, "hide_sphaira", false};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::homebrew
|
||||
|
||||
@@ -7,6 +7,17 @@
|
||||
|
||||
namespace sphaira::ui::menu::main {
|
||||
|
||||
enum class UpdateState {
|
||||
// still downloading json from github
|
||||
Pending,
|
||||
// no update available.
|
||||
None,
|
||||
// update available!
|
||||
Update,
|
||||
// there was an error whilst checking for updates.
|
||||
Error,
|
||||
};
|
||||
|
||||
// this holds 2 menus and allows for switching between them
|
||||
struct MainMenu final : Widget {
|
||||
MainMenu();
|
||||
@@ -31,7 +42,7 @@ private:
|
||||
std::string m_update_url{};
|
||||
std::string m_update_version{};
|
||||
std::string m_update_description{};
|
||||
bool m_update_avaliable{};
|
||||
UpdateState m_update_state{UpdateState::Pending};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::main
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
#include "fs.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "i18n.hpp"
|
||||
#include "ftpsrv_helper.hpp"
|
||||
|
||||
#include <nanovg_dk.h>
|
||||
#include <minIni.h>
|
||||
#include <pulsar.h>
|
||||
#include <haze.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
@@ -107,12 +109,12 @@ void on_applet_operation_mode(App* app) {
|
||||
switch (appletGetOperationMode()) {
|
||||
case AppletOperationMode_Handheld:
|
||||
log_write("[APPLET] AppletOperationMode_Handheld\n");
|
||||
App::Notify("Switch-Handheld!");
|
||||
App::Notify("Switch-Handheld!"_i18n);
|
||||
break;
|
||||
|
||||
case AppletOperationMode_Console:
|
||||
log_write("[APPLET] AppletOperationMode_Console\n");
|
||||
App::Notify("Switch-Docked!");
|
||||
App::Notify("Switch-Docked!"_i18n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -200,7 +202,13 @@ auto GetNroIcon(const std::vector<u8>& nro_icon) -> std::vector<u8> {
|
||||
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,22 +255,25 @@ void App::Loop() {
|
||||
} else if constexpr(std::is_same_v<T, evman::ExitEventData>) {
|
||||
log_write("[ExitEventData] got event\n");
|
||||
m_quit = true;
|
||||
} else if constexpr(std::is_same_v<T, HazeCallbackData>) {
|
||||
// log_write("[ExitEventData] got event\n");
|
||||
// m_quit = true;
|
||||
} else if constexpr(std::is_same_v<T, NxlinkCallbackData>) {
|
||||
switch (arg.type) {
|
||||
case NxlinkCallbackType_Connected:
|
||||
log_write("[NxlinkCallbackType_Connected]\n");
|
||||
App::Notify("Nxlink Connected");
|
||||
App::Notify("Nxlink Connected"_i18n);
|
||||
break;
|
||||
case NxlinkCallbackType_WriteBegin:
|
||||
log_write("[NxlinkCallbackType_WriteBegin] %s\n", arg.file.filename);
|
||||
App::Notify("Nxlink Upload");
|
||||
App::Notify("Nxlink Upload"_i18n);
|
||||
break;
|
||||
case NxlinkCallbackType_WriteProgress:
|
||||
// log_write("[NxlinkCallbackType_WriteProgress]\n");
|
||||
break;
|
||||
case NxlinkCallbackType_WriteEnd:
|
||||
log_write("[NxlinkCallbackType_WriteEnd] %s\n", arg.file.filename);
|
||||
App::Notify("Nxlink Finished");
|
||||
App::Notify("Nxlink Finished"_i18n);
|
||||
break;
|
||||
}
|
||||
} else if constexpr(std::is_same_v<T, DownloadEventData>) {
|
||||
@@ -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<ThemeMeta> {
|
||||
return g_app->m_theme_meta_entries;
|
||||
}
|
||||
@@ -363,10 +396,18 @@ auto App::GetReplaceHbmenuEnable() -> bool {
|
||||
return g_app->m_replace_hbmenu.Get();
|
||||
}
|
||||
|
||||
auto App::GetInstallEnable() -> bool {
|
||||
return g_app->m_install.Get();
|
||||
}
|
||||
|
||||
auto App::GetInstallSdEnable() -> bool {
|
||||
return g_app->m_install_sd.Get();
|
||||
}
|
||||
|
||||
auto App::GetInstallPrompt() -> bool {
|
||||
return g_app->m_install_prompt.Get();
|
||||
}
|
||||
|
||||
auto App::GetThemeShuffleEnable() -> bool {
|
||||
return g_app->m_theme_shuffle.Get();
|
||||
}
|
||||
@@ -375,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();
|
||||
}
|
||||
@@ -405,10 +454,18 @@ void App::SetReplaceHbmenuEnable(bool enable) {
|
||||
g_app->m_replace_hbmenu.Set(enable);
|
||||
}
|
||||
|
||||
void App::SetInstallEnable(bool enable) {
|
||||
g_app->m_install.Set(enable);
|
||||
}
|
||||
|
||||
void App::SetInstallSdEnable(bool enable) {
|
||||
g_app->m_install_sd.Set(enable);
|
||||
}
|
||||
|
||||
void App::SetInstallPrompt(bool enable) {
|
||||
g_app->m_install_prompt.Set(enable);
|
||||
}
|
||||
|
||||
void App::SetThemeShuffleEnable(bool enable) {
|
||||
g_app->m_theme_shuffle.Set(enable);
|
||||
}
|
||||
@@ -418,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);
|
||||
@@ -446,10 +525,10 @@ auto App::Install(OwoConfig& config) -> Result {
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
App::PlaySoundEffect(SoundEffect_Error);
|
||||
App::Push(std::make_shared<ui::ErrorBox>(rc, "Failed to install forwarder"));
|
||||
App::Push(std::make_shared<ui::ErrorBox>(rc, "Failed to install forwarder"_i18n));
|
||||
} else {
|
||||
App::PlaySoundEffect(SoundEffect_Install);
|
||||
App::Notify("Installed!");
|
||||
App::Notify("Installed!"_i18n);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -476,10 +555,10 @@ auto App::Install(ui::ProgressBox* pbox, OwoConfig& config) -> Result {
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
App::PlaySoundEffect(SoundEffect_Error);
|
||||
App::Push(std::make_shared<ui::ErrorBox>(rc, "Failed to install forwarder"));
|
||||
App::Push(std::make_shared<ui::ErrorBox>(rc, "Failed to install forwarder"_i18n));
|
||||
} else {
|
||||
App::PlaySoundEffect(SoundEffect_Install);
|
||||
App::Notify("Installed!");
|
||||
App::Notify("Installed!"_i18n);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -489,6 +568,11 @@ void App::Exit() {
|
||||
g_app->m_quit = true;
|
||||
}
|
||||
|
||||
void App::ExitRestart() {
|
||||
nro_launch(GetExePath());
|
||||
Exit();
|
||||
}
|
||||
|
||||
void App::Poll() {
|
||||
m_controller.Reset();
|
||||
|
||||
@@ -839,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);
|
||||
}
|
||||
@@ -1032,9 +1124,9 @@ App::~App() {
|
||||
NacpStruct nacp;
|
||||
fs::FsNativeSd fs;
|
||||
if (R_SUCCEEDED(nro_get_nacp("/hbmenu.nro", nacp)) && std::strcmp(nacp.lang[0].name, "sphaira")) {
|
||||
log_write("backing up hbmenu\n");
|
||||
log_write("backing up hbmenu.nro\n");
|
||||
if (R_FAILED(fs.copy_entire_file("/switch/hbmenu.nro", "/hbmenu.nro", true))) {
|
||||
log_write("failed to copy sphaire.nro to hbmenu.nro\n");
|
||||
log_write("failed to backup hbmenu.nro\n");
|
||||
}
|
||||
} else {
|
||||
log_write("not backing up\n");
|
||||
@@ -1046,6 +1138,39 @@ App::~App() {
|
||||
} else {
|
||||
log_write("success with copying over root file!\n");
|
||||
}
|
||||
} else if (IsHbmenu()) {
|
||||
// check we have a version that's newer than current.
|
||||
fs::FsNativeSd fs;
|
||||
NacpStruct sphaira_nacp;
|
||||
fs::FsPath sphaira_path = "/switch/sphaira/sphaira.nro";
|
||||
Result rc;
|
||||
|
||||
rc = nro_get_nacp(sphaira_path, sphaira_nacp);
|
||||
if (R_FAILED(rc) || std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
|
||||
sphaira_path = "/switch/sphaira.nro";
|
||||
rc = nro_get_nacp(sphaira_path, sphaira_nacp);
|
||||
}
|
||||
|
||||
// found sphaira, now lets get compare version
|
||||
if (R_SUCCEEDED(rc) && !std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
|
||||
if (std::strcmp(APP_VERSION, sphaira_nacp.display_version) < 0) {
|
||||
if (R_FAILED(rc = fs.copy_entire_file(GetExePath(), sphaira_path, true))) {
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", sphaira_path, rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
} else {
|
||||
log_write("success with updating hbmenu!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (App::GetMtpEnable()) {
|
||||
log_write("closing mtp\n");
|
||||
hazeExit();
|
||||
}
|
||||
|
||||
if (App::GetFtpEnable()) {
|
||||
log_write("closing ftp\n");
|
||||
ftpsrv::Exit();
|
||||
}
|
||||
|
||||
if (App::GetNxlinkEnable()) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
339
sphaira/source/ftpsrv_helper.cpp
Normal file
339
sphaira/source/ftpsrv_helper.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
#include "ftpsrv_helper.hpp"
|
||||
#include <ftpsrv.h>
|
||||
#include <ftpsrv_vfs.h>
|
||||
#include "app.hpp"
|
||||
#include "fs.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-arith"
|
||||
buf += sz;
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
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"
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "log.hpp"
|
||||
#include <yyjson.h>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sphaira::i18n {
|
||||
namespace {
|
||||
@@ -10,37 +11,52 @@ namespace {
|
||||
std::vector<u8> g_i18n_data;
|
||||
yyjson_doc* json;
|
||||
yyjson_val* root;
|
||||
std::unordered_map<std::string, std::string> g_tr_cache;
|
||||
|
||||
std::string get_internal(const char* str, size_t len) {
|
||||
const std::string kkey = {str, len};
|
||||
|
||||
if (auto it = g_tr_cache.find(kkey); it != g_tr_cache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// add default entry
|
||||
g_tr_cache.emplace(kkey, kkey);
|
||||
|
||||
std::string get(const char* str, size_t len) {
|
||||
if (!json || !root) {
|
||||
log_write("no json or root\n");
|
||||
return str;
|
||||
return kkey;
|
||||
}
|
||||
|
||||
auto key = yyjson_obj_getn(root, str, len);
|
||||
if (!key) {
|
||||
log_write("\tfailed to find key: [%.*s]\n", len, str);
|
||||
return str;
|
||||
log_write("\tfailed to find key: [%s]\n", kkey.c_str());
|
||||
return kkey;
|
||||
}
|
||||
|
||||
auto val = yyjson_get_str(key);
|
||||
auto val_len = yyjson_get_len(key);
|
||||
if (!val || !val_len) {
|
||||
log_write("\tfailed to get value: [%.*s]\n", len, str);
|
||||
return str;
|
||||
log_write("\tfailed to get value: [%s]\n", kkey.c_str());
|
||||
return kkey;
|
||||
}
|
||||
|
||||
return {val, val_len};
|
||||
// update entry in cache
|
||||
const std::string ret = {val, val_len};
|
||||
g_tr_cache.insert_or_assign(kkey, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool init(long index) {
|
||||
g_tr_cache.clear();
|
||||
R_TRY_RESULT(romfsInit(), false);
|
||||
ON_SCOPE_EXIT( romfsExit() );
|
||||
|
||||
u64 languageCode;
|
||||
SetLanguage setLanguage = SetLanguage_ENGB;
|
||||
std::string lang_name = "en";
|
||||
|
||||
switch (index) {
|
||||
case 0: // auto
|
||||
@@ -60,9 +76,9 @@ bool init(long index) {
|
||||
case 9: setLanguage = SetLanguage_NL; break; // "Dutch"
|
||||
case 10: setLanguage = SetLanguage_PT; break; // "Portuguese"
|
||||
case 11: setLanguage = SetLanguage_RU; break; // "Russian"
|
||||
case 12: lang_name = "se"; break; // "Swedish"
|
||||
}
|
||||
|
||||
std::string lang_name;
|
||||
switch (setLanguage) {
|
||||
case SetLanguage_JA: lang_name = "ja"; break;
|
||||
case SetLanguage_FR: lang_name = "fr"; break;
|
||||
@@ -75,7 +91,6 @@ bool init(long index) {
|
||||
case SetLanguage_PT: lang_name = "pt"; break;
|
||||
case SetLanguage_RU: lang_name = "ru"; break;
|
||||
case SetLanguage_ZHTW: lang_name = "zh"; break;
|
||||
default: lang_name = "en"; break;
|
||||
}
|
||||
|
||||
const fs::FsPath sdmc_path = "/config/sphaira/i18n/" + lang_name + ".json";
|
||||
@@ -117,12 +132,16 @@ void exit() {
|
||||
g_i18n_data.clear();
|
||||
}
|
||||
|
||||
std::string get(const char* str) {
|
||||
return get_internal(str, std::strlen(str));
|
||||
}
|
||||
|
||||
} // namespace sphaira::i18n
|
||||
|
||||
namespace literals {
|
||||
|
||||
std::string operator"" _i18n(const char* str, size_t len) {
|
||||
return sphaira::i18n::get(str, len);
|
||||
return sphaira::i18n::get_internal(str, len);
|
||||
}
|
||||
|
||||
} // namespace literals
|
||||
|
||||
@@ -59,6 +59,10 @@ void userAppInit(void) {
|
||||
diagAbortWithResult(rc);
|
||||
if (R_FAILED(rc = accountInitialize(is_application ? AccountServiceType_Application : AccountServiceType_System)))
|
||||
diagAbortWithResult(rc);
|
||||
if (R_FAILED(rc = setInitialize()))
|
||||
diagAbortWithResult(rc);
|
||||
if (R_FAILED(rc = hidsysInitialize()))
|
||||
diagAbortWithResult(rc);
|
||||
|
||||
log_nxlink_init();
|
||||
}
|
||||
@@ -66,6 +70,8 @@ void userAppInit(void) {
|
||||
void userAppExit(void) {
|
||||
log_nxlink_exit();
|
||||
|
||||
hidsysExit();
|
||||
setExit();
|
||||
accountExit();
|
||||
nifmExit();
|
||||
psmExit();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <poll.h>
|
||||
|
||||
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<u8> {
|
||||
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{
|
||||
@@ -224,7 +203,7 @@ void loop(void* args) {
|
||||
};
|
||||
|
||||
while (!g_quit) {
|
||||
svcSleepThread(1000000);
|
||||
svcSleepThread(1e+8);
|
||||
|
||||
if (poll_network_change()) {
|
||||
continue;
|
||||
@@ -266,21 +245,36 @@ void loop(void* args) {
|
||||
|
||||
sockaddr_in sa_remote{};
|
||||
|
||||
while (!g_quit) {
|
||||
svcSleepThread(10000);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "defines.hpp"
|
||||
#include "app.hpp"
|
||||
#include "ui/progress_box.hpp"
|
||||
#include "i18n.hpp"
|
||||
|
||||
namespace sphaira {
|
||||
namespace {
|
||||
@@ -1027,7 +1028,7 @@ auto install_forwader_internal(ui::ProgressBox* pbox, OwoConfig& config, NcmStor
|
||||
R_UNLESS(!config.main.empty(), OwoError_BadArgs);
|
||||
R_UNLESS(!config.npdm.empty(), OwoError_BadArgs);
|
||||
|
||||
pbox->NewTransfer("Creating Program").UpdateTransfer(0, 8);
|
||||
pbox->NewTransfer("Creating Program"_i18n).UpdateTransfer(0, 8);
|
||||
FileEntries exefs;
|
||||
add_file_entry(exefs, "main", config.main);
|
||||
add_file_entry(exefs, "main.npdm", config.npdm);
|
||||
@@ -1059,7 +1060,7 @@ auto install_forwader_internal(ui::ProgressBox* pbox, OwoConfig& config, NcmStor
|
||||
|
||||
// create control
|
||||
{
|
||||
pbox->NewTransfer("Creating Control").UpdateTransfer(1, 8);
|
||||
pbox->NewTransfer("Creating Control"_i18n).UpdateTransfer(1, 8);
|
||||
// patch nacp
|
||||
NcapPatch nacp_patch{};
|
||||
nacp_patch.tid = tid;
|
||||
@@ -1082,7 +1083,7 @@ auto install_forwader_internal(ui::ProgressBox* pbox, OwoConfig& config, NcmStor
|
||||
NcmContentStorageRecord content_storage_record;
|
||||
NcmContentMetaData content_meta_data;
|
||||
{
|
||||
pbox->NewTransfer("Creating Meta").UpdateTransfer(2, 8);
|
||||
pbox->NewTransfer("Creating Meta"_i18n).UpdateTransfer(2, 8);
|
||||
const auto meta_entry = create_meta_nca(tid, key, storage_id, nca_entries);
|
||||
|
||||
nca_entries.emplace_back(meta_entry.nca_entry);
|
||||
@@ -1099,7 +1100,7 @@ auto install_forwader_internal(ui::ProgressBox* pbox, OwoConfig& config, NcmStor
|
||||
ON_SCOPE_EXIT(ncmContentStorageClose(&cs));
|
||||
|
||||
for (const auto& nca : nca_entries) {
|
||||
pbox->NewTransfer("Writing Nca").UpdateTransfer(3, 8);
|
||||
pbox->NewTransfer("Writing Nca"_i18n).UpdateTransfer(3, 8);
|
||||
NcmContentId content_id;
|
||||
NcmPlaceHolderId placeholder_id;
|
||||
std::memcpy(&content_id, nca.hash, sizeof(content_id));
|
||||
@@ -1114,7 +1115,7 @@ auto install_forwader_internal(ui::ProgressBox* pbox, OwoConfig& config, NcmStor
|
||||
|
||||
// setup database
|
||||
{
|
||||
pbox->NewTransfer("Updating ncm databse").UpdateTransfer(4, 8);
|
||||
pbox->NewTransfer("Updating ncm databse"_i18n).UpdateTransfer(4, 8);
|
||||
NcmContentMetaDatabase db;
|
||||
R_TRY(ncmOpenContentMetaDatabase(&db, storage_id));
|
||||
ON_SCOPE_EXIT(ncmContentMetaDatabaseClose(&db));
|
||||
@@ -1125,7 +1126,7 @@ auto install_forwader_internal(ui::ProgressBox* pbox, OwoConfig& config, NcmStor
|
||||
|
||||
// push record
|
||||
{
|
||||
pbox->NewTransfer("Pushing application record").UpdateTransfer(5, 8);
|
||||
pbox->NewTransfer("Pushing application record"_i18n).UpdateTransfer(5, 8);
|
||||
Service srv{}, *srv_ptr = &srv;
|
||||
bool already_installed{};
|
||||
|
||||
@@ -1166,7 +1167,7 @@ auto install_forwarder(ui::ProgressBox* pbox, OwoConfig& config, NcmStorageId st
|
||||
}
|
||||
|
||||
auto install_forwarder(OwoConfig& config, NcmStorageId storage_id) -> Result {
|
||||
App::Push(std::make_shared<ui::ProgressBox>("Installing Forwarder", [config, storage_id](auto pbox) mutable -> bool {
|
||||
App::Push(std::make_shared<ui::ProgressBox>("Installing Forwarder"_i18n, [config, storage_id](auto pbox) mutable -> bool {
|
||||
return R_SUCCEEDED(install_forwarder(pbox, config, storage_id));
|
||||
}));
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ui/error_box.hpp"
|
||||
#include "ui/nvg_util.hpp"
|
||||
#include "app.hpp"
|
||||
#include "i18n.hpp"
|
||||
|
||||
namespace sphaira::ui {
|
||||
namespace {
|
||||
@@ -1147,10 +1148,10 @@ auto ErrorBox::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
gfx::drawTextArgs(vg, center_x, 180, 63, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, gfx::Colour::RED, "\uE140");
|
||||
gfx::drawTextArgs(vg, center_x, 270, 25, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "Error code: 0x%X Module: %s Description: %s", m_code, m_module_str.c_str(), m_description_str.c_str());
|
||||
gfx::drawTextArgs(vg, center_x, 325, 23, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "%s", m_message.c_str());
|
||||
gfx::drawTextArgs(vg, center_x, 380, 20, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, gfx::Colour::SILVER, "If this message appears repeatedly, please open an issue.");
|
||||
gfx::drawTextArgs(vg, center_x, 380, 20, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, gfx::Colour::SILVER, "If this message appears repeatedly, please open an issue."_i18n.c_str());
|
||||
gfx::drawTextArgs(vg, center_x, 415, 20, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, gfx::Colour::SILVER, "https://github.com/ITotalJustice/sphaira/issues");
|
||||
gfx::drawRectOutline(vg, 4.f, theme->elements[ThemeEntryID_SELECTED_OVERLAY].colour, box, theme->elements[ThemeEntryID_SELECTED].colour);
|
||||
gfx::drawTextArgs(vg, center_x, box.y + box.h/2, 23, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::WHITE, "OK");
|
||||
gfx::drawTextArgs(vg, center_x, box.y + box.h/2, 23, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::WHITE, "OK"_i18n.c_str());
|
||||
}
|
||||
|
||||
} // namespace sphaira::ui
|
||||
|
||||
@@ -370,7 +370,7 @@ auto UninstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
|
||||
// remove directory, this will also delete manifest and info
|
||||
const auto dir = BuildPackageCachePath(entry);
|
||||
pbox->NewTransfer("Removing " + dir);
|
||||
pbox->NewTransfer("Removing "_i18n + dir);
|
||||
if (R_FAILED(fs.DeleteDirectoryRecursively(dir))) {
|
||||
log_write("failed to delete folder: %s\n", dir);
|
||||
} else {
|
||||
@@ -394,7 +394,7 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
|
||||
// 1. download the zip
|
||||
if (!pbox->ShouldExit()) {
|
||||
pbox->NewTransfer("Downloading " + entry.title);
|
||||
pbox->NewTransfer("Downloading "_i18n + entry.title);
|
||||
log_write("starting download\n");
|
||||
|
||||
const auto url = BuildZipUrl(entry);
|
||||
@@ -416,7 +416,7 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
|
||||
// 2. md5 check the zip
|
||||
if (!pbox->ShouldExit()) {
|
||||
pbox->NewTransfer("Checking MD5");
|
||||
pbox->NewTransfer("Checking MD5"_i18n);
|
||||
log_write("starting md5 check\n");
|
||||
|
||||
FsFile f;
|
||||
@@ -472,7 +472,7 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
}
|
||||
|
||||
if (strncasecmp(md5_str, entry.md5.data(), entry.md5.length())) {
|
||||
log_write("bad md5: %.*s vs %.*s\n", 32, md5_str, 32, entry.md5);
|
||||
log_write("bad md5: %.*s vs %.*s\n", 32, md5_str, 32, entry.md5.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -792,11 +792,11 @@ void EntryMenu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
|
||||
// gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "author: %s", m_entry.author.c_str());
|
||||
// text_start_y += text_inc_y;
|
||||
gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "version: %s", m_entry.version.c_str());
|
||||
gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "version: %s"_i18n.c_str(), m_entry.version.c_str());
|
||||
text_start_y += text_inc_y;
|
||||
gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "updated: %s", m_entry.updated.c_str());
|
||||
gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "updated: %s"_i18n.c_str(), m_entry.updated.c_str());
|
||||
text_start_y += text_inc_y;
|
||||
gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "category: %s", m_entry.category.c_str());
|
||||
gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "category: %s"_i18n.c_str(), m_entry.category.c_str());
|
||||
text_start_y += text_inc_y;
|
||||
// gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "license: %s", m_entry.license.c_str());
|
||||
// text_start_y += text_inc_y;
|
||||
@@ -804,9 +804,9 @@ void EntryMenu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
// text_start_y += text_inc_y;
|
||||
// gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "filesize: %.2f MiB", (double)m_entry.filesize / 1024.0);
|
||||
// text_start_y += text_inc_y;
|
||||
gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "extracted: %.2f MiB", (double)m_entry.extracted / 1024.0);
|
||||
gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "extracted: %.2f MiB"_i18n.c_str(), (double)m_entry.extracted / 1024.0);
|
||||
text_start_y += text_inc_y;
|
||||
gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "app_dls: %s", AppDlToStr(m_entry.app_dls).c_str());
|
||||
gfx::drawTextArgs(vg, text_start_x, text_start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, theme->elements[ThemeEntryID_TEXT].colour, "app_dls: %s"_i18n.c_str(), AppDlToStr(m_entry.app_dls).c_str());
|
||||
text_start_y += text_inc_y;
|
||||
|
||||
// for (const auto& option : m_options) {
|
||||
@@ -935,7 +935,7 @@ auto toLower(const std::string& str) -> std::string {
|
||||
return lower;
|
||||
}
|
||||
|
||||
Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"AppStore"}, m_nro_entries{nro_entries} {
|
||||
Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"AppStore"_i18n}, m_nro_entries{nro_entries} {
|
||||
fs::FsNativeSd fs;
|
||||
fs.CreateDirectoryRecursively("/switch/sphaira/cache/appstore/icons");
|
||||
fs.CreateDirectoryRecursively("/switch/sphaira/cache/appstore/banners");
|
||||
@@ -1084,24 +1084,17 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"AppStore"}, m_n
|
||||
if (!time_stamp.is_valid) {
|
||||
download_file = true;
|
||||
} else {
|
||||
// check the date, if older than 1day, then fetch new file
|
||||
// check the date, if older than 1hour, then fetch new file
|
||||
// this relaxes the spam to their server, don't want to fetch repo
|
||||
// every time the user opens the app!
|
||||
const auto time_file = (time_t)time_stamp.created;
|
||||
const auto time_cur = (time_t)current_time;
|
||||
const auto tm_file = *gmtime(&time_file);
|
||||
const auto tm_cur = *gmtime(&time_cur);
|
||||
if (tm_cur.tm_yday > tm_file.tm_yday || tm_cur.tm_year > tm_file.tm_year) {
|
||||
log_write("repo.json expired, downloading new! cur_yday: %d file_yday: %d | cur_year: %d file_year: %d\n", tm_cur.tm_yday, tm_file.tm_yday, tm_cur.tm_year, tm_file.tm_year);
|
||||
const auto time_file = time_stamp.created;
|
||||
const auto time_cur = current_time;
|
||||
const auto day = 60 * 60;
|
||||
if (time_file > time_cur || time_cur - time_file >= day) {
|
||||
log_write("repo.json expired, downloading new! time_file: %zu time_cur: %zu\n", time_file, time_cur);
|
||||
download_file = true;
|
||||
} else {
|
||||
log_write("repo.json not expired! cur_yday: %d file_yday: %d | cur_year: %d file_year: %d\n", tm_cur.tm_yday, tm_file.tm_yday, tm_cur.tm_year, tm_file.tm_year);
|
||||
// time_file = (time_t)time_stamp.modified;
|
||||
// tm_file = *gmtime(&time_file);
|
||||
// log_write("repo.json not expired! cur_yday: %d file_yday: %d | cur_year: %d file_year: %d\n", tm_cur.tm_yday, tm_file.tm_yday, tm_cur.tm_year, tm_file.tm_year);
|
||||
// time_file = (time_t)time_stamp.accessed;
|
||||
// tm_file = *gmtime(&time_file);
|
||||
// log_write("repo.json not expired! cur_yday: %d file_yday: %d | cur_year: %d file_year: %d\n", tm_cur.tm_yday, tm_file.tm_yday, tm_cur.tm_year, tm_file.tm_year);
|
||||
log_write("repo.json not expired! time_file: %zu time_cur: %zu\n", time_file, time_cur);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1143,12 +1136,12 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
MenuBase::Draw(vg, theme);
|
||||
|
||||
if (m_entries.empty()) {
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Loading...");
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Loading..."_i18n.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_entries_current.empty()) {
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Empty!");
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Empty!"_i18n.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1438,7 +1431,7 @@ void Menu::Sort() {
|
||||
|
||||
|
||||
char subheader[128]{};
|
||||
std::snprintf(subheader, sizeof(subheader), "Sort: %s | Filter: %s | Order: %s", SORT_STR[m_sort], FILTER_STR[m_filter], ORDER_STR[m_order]);
|
||||
std::snprintf(subheader, sizeof(subheader), "Filter: %s | Sort: %s | Order: %s"_i18n.c_str(), i18n::get(FILTER_STR[m_filter]).c_str(), i18n::get(SORT_STR[m_sort]).c_str(), i18n::get(ORDER_STR[m_order]).c_str());
|
||||
SetTitleSubHeading(subheader);
|
||||
|
||||
std::sort(m_entries_current.begin(), m_entries_current.end(), sorter);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ui/menus/file_viewer.hpp"
|
||||
#include "i18n.hpp"
|
||||
|
||||
namespace sphaira::ui::menu::fileview {
|
||||
namespace {
|
||||
@@ -6,7 +7,7 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
Menu::Menu(const fs::FsPath& path) : MenuBase{path}, m_path{path} {
|
||||
SetAction(Button::B, Action{"Back", [this](){
|
||||
SetAction(Button::B, Action{"Back"_i18n, [this](){
|
||||
SetPop();
|
||||
}});
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <ranges>
|
||||
#include <stack>
|
||||
// #include <stack>
|
||||
#include <expected>
|
||||
|
||||
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<std::string_view, std::string_view>;
|
||||
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<std::string_view, std::string_view>;
|
||||
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 == ' ') {
|
||||
@@ -215,7 +223,7 @@ auto GetRomIcon(ProgressBox* pbox, std::string filename, std::string extension,
|
||||
log_write("starting image convert on: %s\n", ra_thumbnail_path.c_str());
|
||||
// try and find icon locally
|
||||
if (!pbox->ShouldExit()) {
|
||||
pbox->NewTransfer("Trying to load " + ra_thumbnail_path);
|
||||
pbox->NewTransfer("Trying to load "_i18n + ra_thumbnail_path);
|
||||
std::vector<u8> image_file;
|
||||
if (R_SUCCEEDED(fs::FsNativeSd().read_entire_file(ra_thumbnail_path.c_str(), image_file))) {
|
||||
return image_file;
|
||||
@@ -469,17 +477,17 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Show Hidden"_i18n, m_show_hidden.Get(), [this](bool& v_out){
|
||||
m_show_hidden.Set(v_out);
|
||||
SortAndFindLastFile();
|
||||
}, "Yes"_i18n, "No"));
|
||||
}, "Yes"_i18n, "No"_i18n));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Folders First"_i18n, m_folders_first.Get(), [this](bool& v_out){
|
||||
m_folders_first.Set(v_out);
|
||||
SortAndFindLastFile();
|
||||
}, "Yes"_i18n, "No"));
|
||||
}, "Yes"_i18n, "No"_i18n));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Hidden Last"_i18n, m_hidden_last.Get(), [this](bool& v_out){
|
||||
m_hidden_last.Set(v_out);
|
||||
SortAndFindLastFile();
|
||||
}, "Yes"_i18n, "No"));
|
||||
}, "Yes"_i18n, "No"_i18n));
|
||||
}));
|
||||
|
||||
if (m_entries_current.size()) {
|
||||
@@ -519,7 +527,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
|
||||
if (!m_selected_files.empty() && (m_selected_type == SelectedType::Cut || m_selected_type == SelectedType::Copy)) {
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Paste"_i18n, [this](){
|
||||
const std::string buf = "Paste " + std::to_string(m_selected_files.size()) + " file(s)?";
|
||||
const std::string buf = "Paste "_i18n + std::to_string(m_selected_files.size()) + " file(s)?"_i18n;
|
||||
App::Push(std::make_shared<OptionBox>(
|
||||
buf, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
@@ -535,7 +543,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
std::string out;
|
||||
const auto& entry = GetEntry();
|
||||
const auto name = entry.GetName();
|
||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set New File Name", name.c_str())) && !out.empty() && out != name) {
|
||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set New File Name"_i18n.c_str(), name.c_str())) && !out.empty() && out != name) {
|
||||
const auto src_path = GetNewPath(entry);
|
||||
const auto dst_path = GetNewPath(m_path, out);
|
||||
|
||||
@@ -563,7 +571,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Create File"_i18n, [this](){
|
||||
std::string out;
|
||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set File Name")) && !out.empty()) {
|
||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set File Name"_i18n.c_str())) && !out.empty()) {
|
||||
fs::FsPath full_path;
|
||||
if (out[0] == '/') {
|
||||
full_path = out;
|
||||
@@ -584,7 +592,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Create Folder"_i18n, [this](){
|
||||
std::string out;
|
||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set Folder Name")) && !out.empty()) {
|
||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set Folder Name"_i18n.c_str())) && !out.empty()) {
|
||||
fs::FsPath full_path;
|
||||
if (out[0] == '/') {
|
||||
full_path = out;
|
||||
@@ -601,67 +609,28 @@ Menu::Menu(const std::vector<NroEntry>& 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<SidebarEntryCallback>("View as text (unfinished)"_i18n, [this](){
|
||||
App::Push(std::make_shared<fileview::Menu>(GetNewPathCurrent()));
|
||||
}, true));
|
||||
}
|
||||
|
||||
if (m_entries_current.size()) {
|
||||
if (HasTypeInSelectedEntries(FsDirEntryType_File) && !m_selected_count && (GetEntry().GetExtension() == "nro" || !FindFileAssocFor().empty())) {
|
||||
if (App::GetInstallEnable() && HasTypeInSelectedEntries(FsDirEntryType_File) && !m_selected_count && (GetEntry().GetExtension() == "nro" || !FindFileAssocFor().empty())) {
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){;
|
||||
App::Push(std::make_shared<OptionBox>(
|
||||
"WARNING: Installing forwarders will lead to a ban!"_i18n,
|
||||
"Back"_i18n, "Install"_i18n, 0, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
if (GetEntry().GetExtension() == "nro") {
|
||||
if (R_FAILED(homebrew::Menu::InstallHomebrewFromPath(GetNewPathCurrent()))) {
|
||||
log_write("failed to create forwarder\n");
|
||||
}
|
||||
} else {
|
||||
if (App::GetInstallPrompt()) {
|
||||
App::Push(std::make_shared<OptionBox>(
|
||||
"WARNING: Installing forwarders will lead to a ban!"_i18n,
|
||||
"Back"_i18n, "Install"_i18n, 0, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
InstallForwarder();
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
}));
|
||||
}
|
||||
|
||||
if (HasTypeInSelectedEntries(FsDirEntryType_Dir)) {
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Set Archive Bit"_i18n, [this](){
|
||||
if (!m_selected_count) {
|
||||
AddCurrentFileToSelection(SelectedType::None);
|
||||
));
|
||||
} else {
|
||||
AddSelectedEntries(SelectedType::None);
|
||||
InstallForwarder();
|
||||
}
|
||||
|
||||
App::Push(std::make_shared<OptionBox>(
|
||||
"Warning! Setting the archive bit cannot be undone!"_i18n,
|
||||
"No"_i18n, "Yes"_i18n, 1, [this](auto op_index) {
|
||||
if (op_index && *op_index == 1) {
|
||||
bool re_sort{};
|
||||
for (const auto&p : m_selected_files) {
|
||||
const auto file_path = GetNewPath(m_selected_path, p.name);
|
||||
if (p.IsDir()) {
|
||||
fs::FsNativeSd fs;
|
||||
if (R_SUCCEEDED(fsFsSetConcatenationFileAttribute(&fs.m_fs, file_path))) {
|
||||
log_write("updated timestamp\n");
|
||||
re_sort |= true;
|
||||
} else {
|
||||
log_write("failed to set concatenation file attribute\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (re_sort) {
|
||||
Scan(m_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
ResetSelection();
|
||||
}, true));
|
||||
}));
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -687,7 +656,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const auto& text_col = theme->elements[ThemeEntryID_TEXT].colour;
|
||||
|
||||
if (m_entries_current.empty()) {
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, text_col, "Empty...");
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, text_col, "Empty..."_i18n.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -779,8 +748,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
nvgRestore(vg);
|
||||
|
||||
if (e.IsDir()) {
|
||||
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->elements[text_id].colour, "%zd files", e.file_count);
|
||||
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->elements[text_id].colour, "%zd dirs", e.dir_count);
|
||||
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->elements[text_id].colour, "%zd files"_i18n.c_str(), e.file_count);
|
||||
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->elements[text_id].colour, "%zd dirs"_i18n.c_str(), e.dir_count);
|
||||
} else {
|
||||
if (!e.time_stamp.is_valid) {
|
||||
fs::FsNativeSd fs;
|
||||
@@ -791,8 +760,9 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
}
|
||||
}
|
||||
const auto t = (time_t)(e.time_stamp.modified);
|
||||
const auto tm = gmtime(&t);
|
||||
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->elements[text_id].colour, "%02u/%02u/%u", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
|
||||
struct tm tm{};
|
||||
localtime_r(&t, &tm);
|
||||
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->elements[text_id].colour, "%02u/%02u/%u", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900);
|
||||
if ((double)e.file_size / 1024.0 / 1024.0 <= 0.009) {
|
||||
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->elements[text_id].colour, "%.2f KiB", (double)e.file_size / 1024.0);
|
||||
} else {
|
||||
@@ -859,6 +829,13 @@ void Menu::SetIndex(std::size_t index) {
|
||||
}
|
||||
|
||||
void Menu::InstallForwarder() {
|
||||
if (GetEntry().GetExtension() == "nro") {
|
||||
if (R_FAILED(homebrew::Menu::InstallHomebrewFromPath(GetNewPathCurrent()))) {
|
||||
log_write("failed to create forwarder\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const auto assoc_list = FindFileAssocFor();
|
||||
if (assoc_list.empty()) {
|
||||
log_write("failed to find assoc for: %s ext: %s\n", GetEntry().name, GetEntry().extension.c_str());
|
||||
@@ -870,13 +847,13 @@ void Menu::InstallForwarder() {
|
||||
items.emplace_back(p.name);
|
||||
}
|
||||
|
||||
const auto title = std::string{"Select launcher for: "} + GetEntry().name;
|
||||
const auto title = std::string{"Select launcher for: "_i18n} + GetEntry().name;
|
||||
App::Push(std::make_shared<PopupList>(
|
||||
title, items, [this, assoc_list](auto op_index){
|
||||
if (op_index) {
|
||||
const auto assoc = assoc_list[*op_index];
|
||||
log_write("pushing it\n");
|
||||
App::Push(std::make_shared<ProgressBox>("Installing Forwarder", [assoc, this](auto pbox) -> bool {
|
||||
App::Push(std::make_shared<ProgressBox>("Installing Forwarder"_i18n, [assoc, this](auto pbox) -> bool {
|
||||
log_write("inside callback\n");
|
||||
|
||||
NroEntry nro{};
|
||||
@@ -895,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();
|
||||
@@ -903,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));
|
||||
}));
|
||||
@@ -997,24 +974,24 @@ auto Menu::Scan(const fs::FsPath& new_path, bool is_walk_up) -> Result {
|
||||
|
||||
auto Menu::FindFileAssocFor() -> std::vector<FileAssocEntry> {
|
||||
// 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<FileAssocEntry> 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());
|
||||
@@ -1052,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<FileAssocEntry*>(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);
|
||||
@@ -1134,7 +1095,6 @@ void Menu::LoadAssocEntriesPath(const fs::FsPath& path) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// after all of that, the file doesn't exist :(
|
||||
@@ -1166,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();
|
||||
@@ -1324,7 +1258,7 @@ void Menu::OnDeleteCallback() {
|
||||
Scan(m_path);
|
||||
log_write("did delete\n");
|
||||
} else {
|
||||
App::Push(std::make_shared<ProgressBox>("Deleting", [this](auto pbox){
|
||||
App::Push(std::make_shared<ProgressBox>("Deleting"_i18n, [this](auto pbox){
|
||||
fs::FsNativeSd fs;
|
||||
FsDirCollections collections;
|
||||
|
||||
@@ -1337,7 +1271,7 @@ void Menu::OnDeleteCallback() {
|
||||
|
||||
const auto full_path = GetNewPath(m_selected_path, p.name);
|
||||
if (p.IsDir()) {
|
||||
pbox->NewTransfer("Scanning " + full_path);
|
||||
pbox->NewTransfer("Scanning "_i18n + full_path);
|
||||
if (R_FAILED(get_collections(full_path, p.name, collections))) {
|
||||
log_write("failed to get dir collection: %s\n", full_path);
|
||||
return false;
|
||||
@@ -1355,7 +1289,7 @@ void Menu::OnDeleteCallback() {
|
||||
}
|
||||
|
||||
const auto full_path = GetNewPath(c.path, p.name);
|
||||
pbox->NewTransfer("Deleting " + full_path);
|
||||
pbox->NewTransfer("Deleting "_i18n + full_path);
|
||||
if (p.type == FsDirEntryType_Dir) {
|
||||
log_write("deleting dir: %s\n", full_path);
|
||||
fs.DeleteDirectory(full_path);
|
||||
@@ -1382,7 +1316,7 @@ void Menu::OnDeleteCallback() {
|
||||
}
|
||||
|
||||
const auto full_path = GetNewPath(m_selected_path, p.name);
|
||||
pbox->NewTransfer("Deleting " + full_path);
|
||||
pbox->NewTransfer("Deleting "_i18n + full_path);
|
||||
|
||||
if (p.IsDir()) {
|
||||
log_write("deleting dir: %s\n", full_path);
|
||||
@@ -1421,7 +1355,7 @@ void Menu::OnPasteCallback() {
|
||||
Scan(m_path);
|
||||
log_write("did paste\n");
|
||||
} else {
|
||||
App::Push(std::make_shared<ProgressBox>("Pasting", [this](auto pbox){
|
||||
App::Push(std::make_shared<ProgressBox>("Pasting"_i18n, [this](auto pbox){
|
||||
fs::FsNativeSd fs;
|
||||
|
||||
if (m_selected_type == SelectedType::Cut) {
|
||||
@@ -1434,7 +1368,7 @@ void Menu::OnPasteCallback() {
|
||||
const auto src_path = GetNewPath(m_selected_path, p.name);
|
||||
const auto dst_path = GetNewPath(m_path, p.name);
|
||||
|
||||
pbox->NewTransfer("Pasting " + src_path);
|
||||
pbox->NewTransfer("Pasting "_i18n + src_path);
|
||||
|
||||
if (p.IsDir()) {
|
||||
fs.RenameDirectory(src_path, dst_path);
|
||||
@@ -1454,7 +1388,7 @@ void Menu::OnPasteCallback() {
|
||||
|
||||
const auto full_path = GetNewPath(m_selected_path, p.name);
|
||||
if (p.IsDir()) {
|
||||
pbox->NewTransfer("Scanning " + full_path);
|
||||
pbox->NewTransfer("Scanning "_i18n + full_path);
|
||||
if (R_FAILED(get_collections(full_path, p.name, collections))) {
|
||||
log_write("failed to get dir collection: %s\n", full_path);
|
||||
return false;
|
||||
@@ -1472,10 +1406,10 @@ void Menu::OnPasteCallback() {
|
||||
const auto dst_path = GetNewPath(p);
|
||||
|
||||
if (p.IsDir()) {
|
||||
pbox->NewTransfer("Creating " + dst_path);
|
||||
pbox->NewTransfer("Creating "_i18n + dst_path);
|
||||
fs.CreateDirectory(dst_path);
|
||||
} else {
|
||||
pbox->NewTransfer("Copying " + src_path);
|
||||
pbox->NewTransfer("Copying "_i18n + src_path);
|
||||
R_TRY_RESULT(pbox->CopyFile(src_path, dst_path), false);
|
||||
}
|
||||
}
|
||||
@@ -1494,7 +1428,7 @@ void Menu::OnPasteCallback() {
|
||||
const auto dst_path = GetNewPath(base_dst_path, p.name);
|
||||
|
||||
log_write("creating: %s to %s\n", src_path, dst_path);
|
||||
pbox->NewTransfer("Creating " + dst_path);
|
||||
pbox->NewTransfer("Creating "_i18n + dst_path);
|
||||
fs.CreateDirectory(dst_path);
|
||||
}
|
||||
|
||||
@@ -1507,7 +1441,7 @@ void Menu::OnPasteCallback() {
|
||||
const auto src_path = GetNewPath(c.path, p.name);
|
||||
const auto dst_path = GetNewPath(base_dst_path, p.name);
|
||||
|
||||
pbox->NewTransfer("Copying " + src_path);
|
||||
pbox->NewTransfer("Copying "_i18n + src_path);
|
||||
log_write("copying: %s to %s\n", src_path, dst_path);
|
||||
R_TRY_RESULT(pbox->CopyFile(src_path, dst_path), false);
|
||||
}
|
||||
|
||||
@@ -17,37 +17,11 @@
|
||||
namespace sphaira::ui::menu::homebrew {
|
||||
namespace {
|
||||
|
||||
constexpr const char* SORT_STR[] = {
|
||||
"Updated",
|
||||
"Size",
|
||||
"Alphabetical",
|
||||
};
|
||||
|
||||
constexpr const char* ORDER_STR[] = {
|
||||
"Desc",
|
||||
"Asc",
|
||||
};
|
||||
|
||||
// returns seconds as: hh:mm:ss
|
||||
auto TimeFormat(u64 sec) -> std::string {
|
||||
char buf[9];
|
||||
|
||||
const auto s = sec % 60;
|
||||
const auto h = sec / 60 % 60;
|
||||
const auto d = sec / 60 / 60 % 24;
|
||||
|
||||
if (sec < 60) {
|
||||
if (!sec) {
|
||||
return "00:00:00";
|
||||
}
|
||||
std::snprintf(buf, sizeof(buf), "00:00:%02lu", s);
|
||||
} else if (sec < 3600) {
|
||||
std::snprintf(buf, sizeof(buf), "00:%02lu:%02lu", h, s);
|
||||
} else {
|
||||
std::snprintf(buf, sizeof(buf), "%02lu:%02lu:%02lu", d, h, s);
|
||||
}
|
||||
|
||||
return std::string{buf};
|
||||
auto GenerateStarPath(const fs::FsPath& nro_path) -> fs::FsPath {
|
||||
fs::FsPath out{};
|
||||
const auto dilem = std::strrchr(nro_path.s, '/');
|
||||
std::snprintf(out, sizeof(out), "%.*s.%s.star", dilem - nro_path.s + 1, nro_path.s, dilem + 1);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -106,8 +80,11 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
|
||||
|
||||
SidebarEntryArray::Items sort_items;
|
||||
sort_items.push_back("Updated"_i18n);
|
||||
sort_items.push_back("Size"_i18n);
|
||||
sort_items.push_back("Alphabetical"_i18n);
|
||||
sort_items.push_back("Size"_i18n);
|
||||
sort_items.push_back("Updated (Star)"_i18n);
|
||||
sort_items.push_back("Alphabetical (Star)"_i18n);
|
||||
sort_items.push_back("Size (Star)"_i18n);
|
||||
|
||||
SidebarEntryArray::Items order_items;
|
||||
order_items.push_back("Decending"_i18n);
|
||||
@@ -122,6 +99,10 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
|
||||
m_order.Set(index_out);
|
||||
SortAndFindLastFile();
|
||||
}, m_order.Get()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Hide Sphaira"_i18n, m_hide_sphaira.Get(), [this](bool& enable){
|
||||
m_hide_sphaira.Set(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
}));
|
||||
|
||||
#if 0
|
||||
@@ -145,20 +126,22 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
|
||||
));
|
||||
}, true));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Hide Sphaira"_i18n, m_hide_sphaira.Get(), [this](bool& enable){
|
||||
m_hide_sphaira.Set(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){
|
||||
App::Push(std::make_shared<OptionBox>(
|
||||
"WARNING: Installing forwarders will lead to a ban!"_i18n,
|
||||
"Back"_i18n, "Install"_i18n, 0, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
InstallHomebrew();
|
||||
}
|
||||
if (App::GetInstallEnable()) {
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){
|
||||
if (App::GetInstallPrompt()) {
|
||||
App::Push(std::make_shared<OptionBox>(
|
||||
"WARNING: Installing forwarders will lead to a ban!"_i18n,
|
||||
"Back"_i18n, "Install"_i18n, 0, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
InstallHomebrew();
|
||||
}
|
||||
}
|
||||
));
|
||||
} else {
|
||||
InstallHomebrew();
|
||||
}
|
||||
));
|
||||
}, true));
|
||||
}, true));
|
||||
}
|
||||
}
|
||||
}})
|
||||
);
|
||||
@@ -183,6 +166,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const u64 max_entry_display = 9;
|
||||
const u64 nro_total = m_entries.size();
|
||||
const u64 cursor_pos = m_index;
|
||||
fs::FsNativeSd fs;
|
||||
|
||||
// only draw scrollbar if needed
|
||||
if (nro_total > max_entry_display) {
|
||||
@@ -219,18 +203,18 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
nvgSave(vg);
|
||||
nvgScissor(vg, x, y, w - 30.f, h); // clip
|
||||
{
|
||||
bool has_star = false;
|
||||
if (IsStarEnabled()) {
|
||||
if (!e.has_star.has_value()) {
|
||||
e.has_star = fs.FileExists(GenerateStarPath(e.path));
|
||||
}
|
||||
has_star = e.has_star.value();
|
||||
}
|
||||
|
||||
const float font_size = 18;
|
||||
const float diff = 32;
|
||||
#if 1
|
||||
gfx::drawTextArgs(vg, x + 148, y + 45, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, "%s", e.GetName());
|
||||
gfx::drawTextArgs(vg, x + 148, y + 45, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, "%s%s", has_star ? "\u2605 " : "", e.GetName());
|
||||
gfx::drawTextArgs(vg, x + 148, y + 80, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, e.GetAuthor());
|
||||
gfx::drawTextArgs(vg, x + 148, y + 115, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, e.GetDisplayVersion());
|
||||
#else
|
||||
gfx::drawTextArgs(vg, x + 148, y + 35, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, "%s", e.GetName());
|
||||
gfx::drawTextArgs(vg, x + 148, y + 35 + (diff * 1), font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, e.GetAuthor());
|
||||
gfx::drawTextArgs(vg, x + 148, y + 35 + (diff * 2), font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, e.GetDisplayVersion());
|
||||
// gfx::drawTextArgs(vg, x + 148, y + 110, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, "PlayCount: %u", e.hbini.launch_count);
|
||||
#endif
|
||||
}
|
||||
nvgRestore(vg);
|
||||
}
|
||||
@@ -251,6 +235,26 @@ void Menu::SetIndex(std::size_t index) {
|
||||
}
|
||||
|
||||
const auto& e = m_entries[m_index];
|
||||
|
||||
if (IsStarEnabled()) {
|
||||
const auto star_path = GenerateStarPath(m_entries[m_index].path);
|
||||
if (fs::FsNativeSd().FileExists(star_path)) {
|
||||
SetAction(Button::R3, Action{"Unstar"_i18n, [this](){
|
||||
fs::FsNativeSd().DeleteFile(GenerateStarPath(m_entries[m_index].path));
|
||||
App::Notify("Unstarred "_i18n + m_entries[m_index].GetName());
|
||||
SortAndFindLastFile();
|
||||
}});
|
||||
} else {
|
||||
SetAction(Button::R3, Action{"Star"_i18n, [this](){
|
||||
fs::FsNativeSd().CreateFile(GenerateStarPath(m_entries[m_index].path));
|
||||
App::Notify("Starred "_i18n + m_entries[m_index].GetName());
|
||||
SortAndFindLastFile();
|
||||
}});
|
||||
}
|
||||
} else {
|
||||
RemoveAction(Button::R3);
|
||||
}
|
||||
|
||||
// TimeCalendarTime caltime;
|
||||
// timeToCalendarTimeWithMyRule()
|
||||
// todo: fix GetFileTimeStampRaw being different to timeGetCurrentTime
|
||||
@@ -308,12 +312,31 @@ void Menu::ScanHomebrew() {
|
||||
}
|
||||
|
||||
void Menu::Sort() {
|
||||
if (IsStarEnabled()) {
|
||||
fs::FsNativeSd fs;
|
||||
fs::FsPath star_path;
|
||||
for (auto& p : m_entries) {
|
||||
p.has_star = fs.FileExists(GenerateStarPath(p.path));
|
||||
if (p.has_star == true) {
|
||||
log_write("found star: %s\n", p.path.s);
|
||||
} else {
|
||||
log_write("no star: %s\n", p.path.s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if lhs should be before rhs
|
||||
const auto sort = m_sort.Get();
|
||||
const auto order = m_order.Get();
|
||||
|
||||
const auto sorter = [this, sort, order](const NroEntry& lhs, const NroEntry& rhs) -> bool {
|
||||
switch (sort) {
|
||||
case SortType_UpdatedStar:
|
||||
if (lhs.has_star.value() && !rhs.has_star.value()) {
|
||||
return true;
|
||||
} else if (!lhs.has_star.value() && rhs.has_star.value()) {
|
||||
return false;
|
||||
}
|
||||
case SortType_Updated: {
|
||||
auto lhs_timestamp = lhs.hbini.timestamp;
|
||||
auto rhs_timestamp = rhs.hbini.timestamp;
|
||||
@@ -332,6 +355,13 @@ void Menu::Sort() {
|
||||
return lhs_timestamp < rhs_timestamp;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SortType_SizeStar:
|
||||
if (lhs.has_star.value() && !rhs.has_star.value()) {
|
||||
return true;
|
||||
} else if (!lhs.has_star.value() && rhs.has_star.value()) {
|
||||
return false;
|
||||
}
|
||||
case SortType_Size: {
|
||||
if (lhs.size == rhs.size) {
|
||||
return strcasecmp(lhs.GetName(), rhs.GetName()) < 0;
|
||||
@@ -341,6 +371,13 @@ void Menu::Sort() {
|
||||
return lhs.size < rhs.size;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SortType_AlphabeticalStar:
|
||||
if (lhs.has_star.value() && !rhs.has_star.value()) {
|
||||
return true;
|
||||
} else if (!lhs.has_star.value() && rhs.has_star.value()) {
|
||||
return false;
|
||||
}
|
||||
case SortType_Alphabetical: {
|
||||
if (order == OrderType_Decending) {
|
||||
return strcasecmp(lhs.GetName(), rhs.GetName()) < 0;
|
||||
@@ -357,7 +394,27 @@ void Menu::Sort() {
|
||||
}
|
||||
|
||||
void Menu::SortAndFindLastFile() {
|
||||
const auto path = m_entries[m_index].path;
|
||||
Sort();
|
||||
SetIndex(0);
|
||||
|
||||
s64 index = -1;
|
||||
for (u64 i = 0; i < m_entries.size(); i++) {
|
||||
if (path == m_entries[i].path) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
// guesstimate where the position is
|
||||
if (index >= 9) {
|
||||
m_start = (index - 9) / 3 * 3 + 3;
|
||||
} else {
|
||||
m_start = 0;
|
||||
}
|
||||
SetIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
Result Menu::InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector<u8>& icon) {
|
||||
|
||||
@@ -242,7 +242,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const auto rc = irsGetImageTransferProcessorState(m_entries[m_index].m_handle, m_irs_buffer.data(), m_irs_buffer.size(), &state);
|
||||
if (R_SUCCEEDED(rc) && state.sampling_number != m_prev_state.sampling_number) {
|
||||
m_prev_state = state;
|
||||
SetSubHeading("Ambient Noise Level: " + std::to_string(m_prev_state.ambient_noise_level));
|
||||
SetSubHeading("Ambient Noise Level: "_i18n + std::to_string(m_prev_state.ambient_noise_level));
|
||||
updateColourArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,76 +1,200 @@
|
||||
#include "ui/menus/main_menu.hpp"
|
||||
#include "ui/menus/irs_menu.hpp"
|
||||
#include "ui/menus/themezer.hpp"
|
||||
|
||||
#include "ui/sidebar.hpp"
|
||||
#include "ui/popup_list.hpp"
|
||||
#include "ui/option_box.hpp"
|
||||
#include "ui/progress_box.hpp"
|
||||
#include "ui/error_box.hpp"
|
||||
|
||||
#include "app.hpp"
|
||||
#include "log.hpp"
|
||||
#include "download.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "ui/menus/irs_menu.hpp"
|
||||
#include "ui/menus/themezer.hpp"
|
||||
#include "web.hpp"
|
||||
#include "i18n.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <minizip/unzip.h>
|
||||
#include <yyjson.h>
|
||||
|
||||
namespace sphaira::ui::menu::main {
|
||||
namespace {
|
||||
|
||||
#if 0
|
||||
bool parseSearch(const char *parse_string, const char *filter, char* new_string) {
|
||||
char c;
|
||||
u32 offset = 0;
|
||||
const u32 filter_len = std::strlen(filter) - 1;
|
||||
auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string version) -> bool {
|
||||
static fs::FsPath zip_out{"/switch/sphaira/cache/update.zip"};
|
||||
constexpr auto chunk_size = 1024 * 512; // 512KiB
|
||||
|
||||
while ((c = parse_string[offset++]) != '\0') {
|
||||
if (c == *filter) {
|
||||
for (u32 i = 0; c == filter[i]; i++) {
|
||||
c = parse_string[offset++];
|
||||
if (i == filter_len) {
|
||||
for (u32 j = 0; c != '\"'; j++) {
|
||||
new_string[j] = c;
|
||||
new_string[j+1] = '\0';
|
||||
c = parse_string[offset++];
|
||||
fs::FsNativeSd fs;
|
||||
R_TRY_RESULT(fs.GetFsOpenResult(), false);
|
||||
|
||||
// 1. download the zip
|
||||
if (!pbox->ShouldExit()) {
|
||||
pbox->NewTransfer("Downloading "_i18n + version);
|
||||
log_write("starting download: %s\n", url.c_str());
|
||||
|
||||
DownloadClearCache(url);
|
||||
if (!DownloadFile(url, zip_out, "", [pbox](u32 dltotal, u32 dlnow, u32 ultotal, u32 ulnow){
|
||||
if (pbox->ShouldExit()) {
|
||||
return false;
|
||||
}
|
||||
pbox->UpdateTransfer(dlnow, dltotal);
|
||||
return true;
|
||||
})) {
|
||||
log_write("error with download\n");
|
||||
// push popup error box
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ON_SCOPE_EXIT(fs.DeleteFile(zip_out));
|
||||
|
||||
// 2. extract the zip
|
||||
if (!pbox->ShouldExit()) {
|
||||
auto zfile = unzOpen64(zip_out);
|
||||
if (!zfile) {
|
||||
log_write("failed to open zip: %s\n", zip_out);
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT(unzClose(zfile));
|
||||
|
||||
unz_global_info64 pglobal_info;
|
||||
if (UNZ_OK != unzGetGlobalInfo64(zfile, &pglobal_info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pglobal_info.number_entry; i++) {
|
||||
if (i > 0) {
|
||||
if (UNZ_OK != unzGoToNextFile(zfile)) {
|
||||
log_write("failed to unzGoToNextFile\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (UNZ_OK != unzOpenCurrentFile(zfile)) {
|
||||
log_write("failed to open current file\n");
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT(unzCloseCurrentFile(zfile));
|
||||
|
||||
unz_file_info64 info;
|
||||
fs::FsPath file_path;
|
||||
if (UNZ_OK != unzGetCurrentFileInfo64(zfile, &info, file_path, sizeof(file_path), 0, 0, 0, 0)) {
|
||||
log_write("failed to get current info\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_path[0] != '/') {
|
||||
file_path = fs::AppendPath("/", file_path);
|
||||
}
|
||||
|
||||
Result rc;
|
||||
if (file_path[strlen(file_path) -1] == '/') {
|
||||
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_PathAlreadyExists) {
|
||||
log_write("failed to create file: %s 0x%04X\n", file_path, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
FsFile f;
|
||||
if (R_FAILED(rc = fs.OpenFile(file_path, FsOpenMode_Write, &f))) {
|
||||
log_write("failed to open file: %s 0x%04X\n", file_path, rc);
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT(fsFileClose(&f));
|
||||
|
||||
if (R_FAILED(rc = fsFileSetSize(&f, info.uncompressed_size))) {
|
||||
log_write("failed to set file size: %s 0x%04X\n", file_path, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char> buf(chunk_size);
|
||||
u64 offset{};
|
||||
while (offset < info.uncompressed_size) {
|
||||
const auto bytes_read = unzReadCurrentFile(zfile, buf.data(), buf.size());
|
||||
if (bytes_read <= 0) {
|
||||
// log_write("failed to read zip file: %s\n", inzip.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
if (R_FAILED(rc = fsFileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None))) {
|
||||
log_write("failed to write file: %s 0x%04X\n", file_path, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
pbox->UpdateTransfer(offset, info.uncompressed_size);
|
||||
offset += bytes_read;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
log_write("finished update :)\n");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
MainMenu::MainMenu() {
|
||||
#if 0
|
||||
DownloadMemoryAsync("https://api.github.com/repos/ITotalJustice/sys-patch/releases/latest", [this](std::vector<u8>& data, bool success){
|
||||
data.push_back('\0');
|
||||
auto raw_str = (const char*)data.data();
|
||||
char out_str[0x301];
|
||||
DownloadMemoryAsync("https://api.github.com/repos/ITotalJustice/sphaira/releases/latest", "", [this](std::vector<u8>& data, bool success){
|
||||
m_update_state = UpdateState::Error;
|
||||
ON_SCOPE_EXIT( log_write("update status: %u\n", (u8)m_update_state) );
|
||||
|
||||
if (parseSearch(raw_str, "tag_name\":\"", out_str)) {
|
||||
m_update_version = out_str;
|
||||
if (strcasecmp("v1.5.0", m_update_version.c_str())) {
|
||||
m_update_avaliable = true;
|
||||
}
|
||||
log_write("FOUND IT : %s\n", out_str);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parseSearch(raw_str, "browser_download_url\":\"", out_str)) {
|
||||
m_update_url = out_str;
|
||||
log_write("FOUND IT : %s\n", out_str);
|
||||
auto json = yyjson_read((const char*)data.data(), data.size(), 0);
|
||||
R_UNLESS(json, false);
|
||||
ON_SCOPE_EXIT(yyjson_doc_free(json));
|
||||
|
||||
auto root = yyjson_doc_get_root(json);
|
||||
R_UNLESS(root, false);
|
||||
|
||||
auto tag_key = yyjson_obj_get(root, "tag_name");
|
||||
R_UNLESS(tag_key, false);
|
||||
|
||||
const auto version = yyjson_get_str(tag_key);
|
||||
R_UNLESS(version, false);
|
||||
if (std::strcmp(APP_VERSION, version) >= 0) {
|
||||
m_update_state = UpdateState::None;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parseSearch(raw_str, "body\":\"", out_str)) {
|
||||
m_update_description = out_str;
|
||||
// m_update_description.replace("\r\n\r\n", "\n");
|
||||
log_write("FOUND IT : %s\n", out_str);
|
||||
}
|
||||
auto body_key = yyjson_obj_get(root, "body");
|
||||
R_UNLESS(body_key, false);
|
||||
|
||||
const auto body = yyjson_get_str(body_key);
|
||||
R_UNLESS(body, false);
|
||||
|
||||
auto assets = yyjson_obj_get(root, "assets");
|
||||
R_UNLESS(assets, false);
|
||||
|
||||
auto idx0 = yyjson_arr_get(assets, 0);
|
||||
R_UNLESS(idx0, false);
|
||||
|
||||
auto url_key = yyjson_obj_get(idx0, "browser_download_url");
|
||||
R_UNLESS(url_key, false);
|
||||
|
||||
const auto url = yyjson_get_str(url_key);
|
||||
R_UNLESS(url, false);
|
||||
|
||||
m_update_version = version;
|
||||
m_update_url = url;
|
||||
m_update_description = body;
|
||||
m_update_state = UpdateState::Update;
|
||||
log_write("found url: %s\n", url);
|
||||
log_write("found body: %s\n", body);
|
||||
App::Notify("Update avaliable: "_i18n + m_update_version);
|
||||
|
||||
return true;
|
||||
});
|
||||
#endif
|
||||
|
||||
AddOnLPress();
|
||||
AddOnRPress();
|
||||
@@ -81,20 +205,20 @@ MainMenu::MainMenu() {
|
||||
auto options = std::make_shared<Sidebar>("Menu Options"_i18n, "v" APP_VERSION_HASH, Sidebar::Side::LEFT);
|
||||
ON_SCOPE_EXIT(App::Push(options));
|
||||
|
||||
|
||||
SidebarEntryArray::Items language_items;
|
||||
language_items.push_back("Auto"_i18n);
|
||||
language_items.push_back("English");
|
||||
language_items.push_back("Japanese");
|
||||
language_items.push_back("French");
|
||||
language_items.push_back("German");
|
||||
language_items.push_back("Italian");
|
||||
language_items.push_back("Spanish");
|
||||
language_items.push_back("Chinese");
|
||||
language_items.push_back("Korean");
|
||||
language_items.push_back("Dutch");
|
||||
language_items.push_back("Portuguese");
|
||||
language_items.push_back("Russian");
|
||||
language_items.push_back("English"_i18n);
|
||||
language_items.push_back("Japanese"_i18n);
|
||||
language_items.push_back("French"_i18n);
|
||||
language_items.push_back("German"_i18n);
|
||||
language_items.push_back("Italian"_i18n);
|
||||
language_items.push_back("Spanish"_i18n);
|
||||
language_items.push_back("Chinese"_i18n);
|
||||
language_items.push_back("Korean"_i18n);
|
||||
language_items.push_back("Dutch"_i18n);
|
||||
language_items.push_back("Portuguese"_i18n);
|
||||
language_items.push_back("Russian"_i18n);
|
||||
language_items.push_back("Swedish"_i18n);
|
||||
|
||||
options->AddHeader("Header"_i18n);
|
||||
options->AddSpacer();
|
||||
@@ -125,32 +249,46 @@ MainMenu::MainMenu() {
|
||||
auto options = std::make_shared<Sidebar>("Network Options"_i18n, Sidebar::Side::LEFT);
|
||||
ON_SCOPE_EXIT(App::Push(options));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Ftp"_i18n, App::GetFtpEnable(), [this](bool& enable){
|
||||
App::SetFtpEnable(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Mtp"_i18n, App::GetMtpEnable(), [this](bool& enable){
|
||||
App::SetMtpEnable(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Nxlink"_i18n, App::GetNxlinkEnable(), [this](bool& enable){
|
||||
App::SetNxlinkEnable(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Check for update"_i18n, [this](){
|
||||
App::Notify("Not Implemented"_i18n);
|
||||
}));
|
||||
|
||||
if (m_update_state == UpdateState::Update) {
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Download update: "_i18n + m_update_version, [this](){
|
||||
App::Push(std::make_shared<ProgressBox>("Downloading "_i18n + m_update_version, [this](auto pbox){
|
||||
return InstallUpdate(pbox, m_update_url, m_update_version);
|
||||
}, [this](bool success){
|
||||
if (success) {
|
||||
m_update_state = UpdateState::None;
|
||||
App::Notify("Updated to "_i18n + m_update_version);
|
||||
App::Push(std::make_shared<OptionBox>(
|
||||
"Restart Sphaira?"_i18n,
|
||||
"Back"_i18n, "Restart"_i18n, 1, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
App::ExitRestart();
|
||||
}
|
||||
}
|
||||
));
|
||||
} else {
|
||||
App::Push(std::make_shared<ui::ErrorBox>(MAKERESULT(351, 1), "Failed to download update"_i18n));
|
||||
}
|
||||
}, 2));
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Language"_i18n, language_items, [this, language_items](std::size_t& index_out){
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Language"_i18n, language_items, [this](std::size_t& index_out){
|
||||
App::SetLanguage(index_out);
|
||||
}, (std::size_t)App::GetLanguage()));
|
||||
|
||||
if (m_update_avaliable) {
|
||||
std::string str = "Update avaliable: "_i18n + m_update_version;
|
||||
options->Add(std::make_shared<SidebarEntryCallback>(str, [this](){
|
||||
App::Notify("Not Implemented"_i18n);
|
||||
}));
|
||||
}
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Logging"_i18n, App::GetLogEnable(), [this](bool& enable){
|
||||
App::SetLogEnable(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Replace hbmenu on exit"_i18n, App::GetReplaceHbmenuEnable(), [this](bool& enable){
|
||||
App::SetReplaceHbmenuEnable(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Misc"_i18n, [this](){
|
||||
auto options = std::make_shared<Sidebar>("Misc Options"_i18n, Sidebar::Side::LEFT);
|
||||
ON_SCOPE_EXIT(App::Push(options));
|
||||
@@ -158,12 +296,45 @@ MainMenu::MainMenu() {
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Themezer"_i18n, [](){
|
||||
App::Push(std::make_shared<menu::themezer::Menu>());
|
||||
}));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Irs"_i18n, [](){
|
||||
App::Push(std::make_shared<menu::irs::Menu>());
|
||||
}));
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Web"_i18n, [](){
|
||||
WebShow("https://lite.duckduckgo.com/lite");
|
||||
}));
|
||||
|
||||
if (App::IsApplication()) {
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Web"_i18n, [](){
|
||||
WebShow("https://lite.duckduckgo.com/lite");
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Advanced"_i18n, [this](){
|
||||
auto options = std::make_shared<Sidebar>("Advanced Options"_i18n, Sidebar::Side::LEFT);
|
||||
ON_SCOPE_EXIT(App::Push(options));
|
||||
|
||||
SidebarEntryArray::Items install_items;
|
||||
install_items.push_back("System memory"_i18n);
|
||||
install_items.push_back("microSD card"_i18n);
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Logging"_i18n, App::GetLogEnable(), [this](bool& enable){
|
||||
App::SetLogEnable(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Replace hbmenu on exit"_i18n, App::GetReplaceHbmenuEnable(), [this](bool& enable){
|
||||
App::SetReplaceHbmenuEnable(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Install forwarders"_i18n, App::GetInstallEnable(), [this](bool& enable){
|
||||
App::SetInstallEnable(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Install location"_i18n, install_items, [this](std::size_t& index_out){
|
||||
App::SetInstallSdEnable(index_out);
|
||||
}, (std::size_t)App::GetInstallSdEnable()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Show install warning"_i18n, App::GetInstallPrompt(), [this](bool& enable){
|
||||
App::SetInstallPrompt(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
}));
|
||||
}})
|
||||
);
|
||||
@@ -205,8 +376,15 @@ void MainMenu::OnLRPress(std::shared_ptr<MenuBase> menu, Button b) {
|
||||
if (m_current_menu == m_homebrew_menu) {
|
||||
m_current_menu = menu;
|
||||
RemoveAction(b);
|
||||
if (b == Button::L) {
|
||||
AddOnRPress();
|
||||
} else {
|
||||
AddOnLPress();
|
||||
}
|
||||
} else {
|
||||
m_current_menu = m_homebrew_menu;
|
||||
AddOnRPress();
|
||||
AddOnLPress();
|
||||
}
|
||||
|
||||
m_current_menu->OnFocusGained();
|
||||
@@ -214,22 +392,18 @@ void MainMenu::OnLRPress(std::shared_ptr<MenuBase> menu, Button b) {
|
||||
for (auto [button, action] : m_actions) {
|
||||
m_current_menu->SetAction(button, action);
|
||||
}
|
||||
|
||||
if (b == Button::L) {
|
||||
AddOnRPress();
|
||||
} else {
|
||||
AddOnLPress();
|
||||
}
|
||||
}
|
||||
|
||||
void MainMenu::AddOnLPress() {
|
||||
SetAction(Button::L, Action{"Fs"_i18n, [this]{
|
||||
const auto label = m_current_menu == m_homebrew_menu ? "Files" : "Apps";
|
||||
SetAction(Button::L, Action{i18n::get(label), [this]{
|
||||
OnLRPress(m_filebrowser_menu, Button::L);
|
||||
}});
|
||||
}
|
||||
|
||||
void MainMenu::AddOnRPress() {
|
||||
SetAction(Button::R, Action{"App"_i18n, [this]{
|
||||
const auto label = m_current_menu == m_homebrew_menu ? "Store" : "Apps";
|
||||
SetAction(Button::R, Action{i18n::get(label), [this]{
|
||||
OnLRPress(m_app_store_menu, Button::R);
|
||||
}});
|
||||
}
|
||||
|
||||
@@ -32,12 +32,9 @@ void MenuBase::Draw(NVGcontext* vg, Theme* theme) {
|
||||
u32 strength{};
|
||||
u32 ip{};
|
||||
|
||||
const auto _time = time(NULL);
|
||||
const auto t = time(NULL);
|
||||
struct tm tm{};
|
||||
const auto gmt = gmtime(&_time);
|
||||
if (gmt) {
|
||||
tm = *gmt;
|
||||
}
|
||||
localtime_r(&t, &tm);
|
||||
|
||||
// todo: app thread poll every 1s and this query the result
|
||||
psmGetBatteryChargePercentage(&battery_percetange);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -508,7 +508,7 @@ Menu::Menu() : MenuBase{"Themezer"_i18n} {
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Page"_i18n, [this](){
|
||||
s64 out;
|
||||
if (R_SUCCEEDED(swkbd::ShowNumPad(out, "Enter Page Number", nullptr, -1, 3))) {
|
||||
if (R_SUCCEEDED(swkbd::ShowNumPad(out, "Enter Page Number"_i18n.c_str(), nullptr, -1, 3))) {
|
||||
if (out < m_page_index_max) {
|
||||
m_page_index = out;
|
||||
PackListDownload();
|
||||
@@ -569,7 +569,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
MenuBase::Draw(vg, theme);
|
||||
|
||||
if (m_pages.empty()) {
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Empty!");
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Empty!"_i18n.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -577,15 +577,15 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
|
||||
switch (page.m_ready) {
|
||||
case PageLoadState::None:
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Not Ready...");
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Not Ready..."_i18n.c_str());
|
||||
return;
|
||||
case PageLoadState::Loading:
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Loading");
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Loading"_i18n.c_str());
|
||||
return;
|
||||
case PageLoadState::Done:
|
||||
break;
|
||||
case PageLoadState::Error:
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Error loading page!");
|
||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, gfx::Colour::YELLOW, "Error loading page!"_i18n.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -687,7 +687,7 @@ void Menu::InvalidateAllPages() {
|
||||
void Menu::PackListDownload() {
|
||||
const auto page_index = m_page_index + 1;
|
||||
char subheading[128];
|
||||
std::snprintf(subheading, sizeof(subheading), "Page %zu / %zu", m_page_index+1, m_page_index_max);
|
||||
std::snprintf(subheading, sizeof(subheading), "Page %zu / %zu"_i18n.c_str(), m_page_index+1, m_page_index_max);
|
||||
SetSubHeading(subheading);
|
||||
|
||||
m_index = 0;
|
||||
@@ -733,7 +733,7 @@ void Menu::PackListDownload() {
|
||||
m_page_index_max = a.pagination.page_count;
|
||||
|
||||
char subheading[128];
|
||||
std::snprintf(subheading, sizeof(subheading), "Page %zu / %zu", m_page_index+1, m_page_index_max);
|
||||
std::snprintf(subheading, sizeof(subheading), "Page %zu / %zu"_i18n.c_str(), m_page_index+1, m_page_index_max);
|
||||
SetSubHeading(subheading);
|
||||
|
||||
log_write("a.pagination.page: %u\n", a.pagination.page);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ OptionBox::OptionBox(const std::string& message, const Option& a, const Option&
|
||||
|
||||
m_pos.w = 770.f;
|
||||
m_pos.h = 295.f;
|
||||
m_pos.x = (1280.f / 2.f) - (m_pos.w / 2.f);
|
||||
m_pos.y = (720.f / 2.f) - (m_pos.h / 2.f);
|
||||
m_pos.x = (SCREEN_WIDTH / 2.f) - (m_pos.w / 2.f);
|
||||
m_pos.y = (SCREEN_HEIGHT / 2.f) - (m_pos.h / 2.f);
|
||||
|
||||
auto box = m_pos;
|
||||
box.w /= 2.f;
|
||||
@@ -102,9 +102,15 @@ auto OptionBox::OnLayoutChange() -> void {
|
||||
}
|
||||
|
||||
auto OptionBox::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
const float padding = 15;
|
||||
gfx::dimBackground(vg);
|
||||
gfx::drawRect(vg, m_pos, theme->elements[ThemeEntryID_SELECTED].colour);
|
||||
gfx::drawText(vg, {m_pos.x + (m_pos.w / 2.f), m_pos.y + 110.f}, 26.f, theme->elements[ThemeEntryID_TEXT].colour, m_message.c_str(), NVG_ALIGN_CENTER | NVG_ALIGN_TOP);
|
||||
|
||||
nvgSave(vg);
|
||||
nvgTextLineHeight(vg, 1.5);
|
||||
gfx::drawTextBox(vg, m_pos.x + padding, m_pos.y + 110.f, 26.f, m_pos.w - padding*2, theme->elements[ThemeEntryID_TEXT].colour, m_message.c_str(), NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
|
||||
nvgRestore(vg);
|
||||
|
||||
gfx::drawRect(vg, m_spacer_line, theme->elements[ThemeEntryID_TEXT].colour);
|
||||
|
||||
for (auto&p: m_entries) {
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
#include "ui/option_list.hpp"
|
||||
#include "app.hpp"
|
||||
#include "ui/nvg_util.hpp"
|
||||
#include "i18n.hpp"
|
||||
|
||||
namespace sphaira::ui {
|
||||
|
||||
OptionList::OptionList(Options options)
|
||||
: m_options{std::move(options)} {
|
||||
SetAction(Button::A, Action{"Select", [this](){
|
||||
SetAction(Button::A, Action{"Select"_i18n, [this](){
|
||||
const auto& [_, func] = m_options[m_index];
|
||||
func();
|
||||
SetPop();
|
||||
}});
|
||||
|
||||
SetAction(Button::B, Action{"Back", [this](){
|
||||
SetAction(Button::B, Action{"Back"_i18n, [this](){
|
||||
SetPop();
|
||||
}});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ui/popup_list.hpp"
|
||||
#include "ui/nvg_util.hpp"
|
||||
#include "app.hpp"
|
||||
#include "i18n.hpp"
|
||||
|
||||
namespace sphaira::ui {
|
||||
|
||||
@@ -68,13 +69,13 @@ PopupList::PopupList(std::string title, Items items, Callback cb, std::size_t in
|
||||
m_scrollbar.Setup(Vec4{1220.f, m_line_top, 1.f, m_line_bottom - m_line_top}, m_block.h, m_items.size());
|
||||
|
||||
SetActions(
|
||||
std::make_pair(Button::A, Action{"Select", [this](){
|
||||
std::make_pair(Button::A, Action{"Select"_i18n, [this](){
|
||||
if (m_callback) {
|
||||
m_callback(m_index);
|
||||
}
|
||||
SetPop();
|
||||
}}),
|
||||
std::make_pair(Button::B, Action{"Back", [this](){
|
||||
std::make_pair(Button::B, Action{"Back"_i18n, [this](){
|
||||
if (m_callback) {
|
||||
m_callback(std::nullopt);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "app.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "log.hpp"
|
||||
#include "i18n.hpp"
|
||||
|
||||
namespace sphaira::ui {
|
||||
namespace {
|
||||
@@ -17,8 +18,8 @@ void threadFunc(void* arg) {
|
||||
} // namespace
|
||||
|
||||
ProgressBox::ProgressBox(const std::string& title, ProgressBoxCallback callback, ProgressBoxDoneCallback done, int cpuid, int prio, int stack_size) {
|
||||
SetAction(Button::B, Action{"Back", [this](){
|
||||
App::Push(std::make_shared<OptionBox>("Are you sure you wish to cancel?", "No", "Yes", 1, [this](auto op_index){
|
||||
SetAction(Button::B, Action{"Back"_i18n, [this](){
|
||||
App::Push(std::make_shared<OptionBox>("Are you sure you wish to cancel?"_i18n, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
RequestExit();
|
||||
SetPop();
|
||||
@@ -30,6 +31,12 @@ ProgressBox::ProgressBox(const std::string& title, ProgressBoxCallback callback,
|
||||
m_pos.h = 430.f;
|
||||
m_pos.x = 255;
|
||||
m_pos.y = 145;
|
||||
145 + 430; // 575, 200, 420
|
||||
|
||||
m_pos.w = 770.f;
|
||||
m_pos.h = 295.f;
|
||||
m_pos.x = (SCREEN_WIDTH / 2.f) - (m_pos.w / 2.f);
|
||||
m_pos.y = (SCREEN_HEIGHT / 2.f) - (m_pos.h / 2.f);
|
||||
|
||||
m_done = done;
|
||||
m_title = title;
|
||||
@@ -80,8 +87,9 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
|
||||
// The pop up shape.
|
||||
// const Vec4 box = { 255, 145, 770, 430 };
|
||||
const Vec4 prog_bar = { 400, 470, 480, 12 };
|
||||
const auto center_x = m_pos.x + m_pos.w/2;
|
||||
const auto end_y = m_pos.y + m_pos.h;
|
||||
const Vec4 prog_bar = { 400, end_y - 80, 480, 12 };
|
||||
|
||||
// shapes.
|
||||
if (offset && size) {
|
||||
@@ -92,12 +100,12 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
gfx::drawTextArgs(vg, prog_bar.x + prog_bar.w + 10, prog_bar.y, 20, NVG_ALIGN_LEFT | NVG_ALIGN_TOP, gfx::Colour::WHITE, "%u%%", percentage);
|
||||
}
|
||||
|
||||
gfx::drawTextArgs(vg, center_x, 200, 25, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, gfx::Colour::WHITE, title.c_str());
|
||||
gfx::drawTextArgs(vg, center_x, m_pos.y + 60, 25, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, gfx::Colour::WHITE, title.c_str());
|
||||
// gfx::drawTextArgs(vg, center_x, 260, 20, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, gfx::Colour::SILVER, "Please do not remove the gamecard or");
|
||||
// gfx::drawTextArgs(vg, center_x, 295, 20, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, gfx::Colour::SILVER, "power off the system whilst installing.");
|
||||
// gfx::drawTextArgs(vg, center_x, 360, 20, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, gfx::Colour::WHITE, "%.2f MiB/s", 24.0);
|
||||
if (!transfer.empty()) {
|
||||
gfx::drawTextArgs(vg, center_x, 420, 20, NVG_ALIGN_CENTER, gfx::Colour::WHITE, "%s", transfer.c_str());
|
||||
gfx::drawTextArgs(vg, center_x, prog_bar.y - 15 - 20 * 1.5, 20, NVG_ALIGN_CENTER, gfx::Colour::WHITE, "%s", transfer.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "app.hpp"
|
||||
#include "ui/popup_list.hpp"
|
||||
#include "ui/nvg_util.hpp"
|
||||
#include "i18n.hpp"
|
||||
|
||||
namespace sphaira::ui {
|
||||
namespace {
|
||||
@@ -42,7 +43,7 @@ SidebarEntryBool::SidebarEntryBool(std::string title, bool option, Callback cb,
|
||||
, m_true_str{std::move(true_str)}
|
||||
, m_false_str{std::move(false_str)} {
|
||||
|
||||
SetAction(Button::A, Action{"OK", [this](){
|
||||
SetAction(Button::A, Action{"OK"_i18n, [this](){
|
||||
m_option ^= 1;
|
||||
m_callback(m_option);
|
||||
}
|
||||
@@ -77,7 +78,7 @@ SidebarEntryCallback::SidebarEntryCallback(std::string title, Callback cb, bool
|
||||
: SidebarEntryBase{std::move(title)}
|
||||
, m_callback{cb}
|
||||
, m_pop_on_click{pop_on_click} {
|
||||
SetAction(Button::A, Action{"OK", [this](){
|
||||
SetAction(Button::A, Action{"OK"_i18n, [this](){
|
||||
m_callback();
|
||||
if (m_pop_on_click) {
|
||||
SetPop();
|
||||
@@ -143,7 +144,7 @@ SidebarEntryArray::SidebarEntryArray(std::string title, Items items, Callback cb
|
||||
));
|
||||
};
|
||||
|
||||
SetAction(Button::A, Action{"OK", [this](){
|
||||
SetAction(Button::A, Action{"OK"_i18n, [this](){
|
||||
// m_callback(m_index);
|
||||
m_list_callback();
|
||||
}
|
||||
@@ -191,7 +192,7 @@ Sidebar::Sidebar(std::string title, std::string sub, Side side, Items&& items)
|
||||
m_base_pos = Vec4{GetX() + 30.f, GetY() + 170.f, m_pos.w - (30.f * 2.f), 70.f};
|
||||
|
||||
// each item has it's own Action, but we take over B
|
||||
SetAction(Button::B, Action{"Back", [this](){
|
||||
SetAction(Button::B, Action{"Back"_i18n, [this](){
|
||||
SetPop();
|
||||
}});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user