Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b4e81c935 | ||
|
|
e243d5b64e | ||
|
|
252cd0cee6 | ||
|
|
14abcc50b5 | ||
|
|
134aadad5a | ||
|
|
a56bc9e4fa | ||
|
|
5bd466a9b6 | ||
|
|
16c58512ec | ||
|
|
b1b0b13f2a | ||
|
|
03e77faf06 | ||
|
|
7e381924ab | ||
|
|
5763610e54 | ||
|
|
49956a3f84 | ||
|
|
b2915a8142 | ||
|
|
e002aa9ec2 | ||
|
|
0aaf460dbf | ||
|
|
76c8b806d0 | ||
|
|
61783bc530 | ||
|
|
a3a2a04991 | ||
|
|
b6304fca75 | ||
|
|
5612ae5691 | ||
|
|
657c160599 | ||
|
|
f66494aeb5 | ||
|
|
650e7812e5 | ||
|
|
cca54340a2 | ||
|
|
8161b52e7b | ||
|
|
9390bd3865 | ||
|
|
483be133a5 | ||
|
|
e2022eac4c | ||
|
|
977331c3b2 | ||
|
|
64a40ae672 |
44
README.md
@@ -1,10 +1,12 @@
|
||||
# sphaira
|
||||
# Sphaira
|
||||
|
||||
A homebrew menu for the switch.
|
||||
A homebrew menu for the Nintendo Switch.
|
||||
|
||||
[See the gbatemp thread for more details / discussion](https://gbatemp.net/threads/sphaira-hbmenu-replacement.664523/).
|
||||
[See the GBATemp thread for more details / discussion](https://gbatemp.net/threads/sphaira-hbmenu-replacement.664523/).
|
||||
|
||||
## showcase
|
||||
[We have now have a Discord server!](https://discord.gg/8vZBsrprEc). Please use the issues tab to report bugs, as it is much easier for me to track.
|
||||
|
||||
## Showcase
|
||||
|
||||
| | |
|
||||
:-------------------------:|:-------------------------:
|
||||
@@ -13,29 +15,29 @@ A homebrew menu for the switch.
|
||||
 | 
|
||||
 | 
|
||||
|
||||
## bug reports
|
||||
## Bug reports
|
||||
|
||||
for any bug reports, please use the issues tab and explain in as much detail as possible!
|
||||
For any bug reports, please use the issues tab and explain in as much detail as possible!
|
||||
|
||||
please include:
|
||||
Please include:
|
||||
|
||||
- CFW type (i assume Atmosphere, but someone out there is still using Rajnx)
|
||||
- CFW version
|
||||
- FW version
|
||||
- The bug itself and how to reproduce it
|
||||
- CFW type (i assume Atmosphere, but someone out there is still using Rajnx);
|
||||
- CFW version;
|
||||
- FW version;
|
||||
- The bug itself and how to reproduce it.
|
||||
|
||||
## ftp
|
||||
## FTP
|
||||
|
||||
ftp can be enabled via the network menu. It uses the same config as ftpsrv `/config/ftpsrv/config.ini`. [See here for the full list
|
||||
FTP can be enabled via the network menu. It uses the same config as ftpsrv `/config/ftpsrv/config.ini`. [See here for the full list
|
||||
of all configs available](https://github.com/ITotalJustice/ftpsrv/blob/master/assets/config.ini.template).
|
||||
|
||||
## mtp
|
||||
## MTP
|
||||
|
||||
mtp can be enabled via the network menu.
|
||||
MTP can be enabled via the Network menu.
|
||||
|
||||
## file assoc
|
||||
## File association
|
||||
|
||||
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.
|
||||
Sphaira has file association support. Let's say your app supports loading .png files, then you could write an association 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.
|
||||
|
||||
```ini
|
||||
[config]
|
||||
@@ -43,9 +45,9 @@ path=/switch/your_app.nro
|
||||
supported_extensions=jpg|png|mp4|mp3
|
||||
```
|
||||
|
||||
the `path` field is optional. if left out, it will use the name of the ini to find the nro. For example, if the ini is called mgba.ini, it will try to find the nro in /switch/mgba.nro and /switch/folder/mgba.nro.
|
||||
The `path` field is optional. If left out, it will use the name of the ini to find the nro. For example, if the ini is called mgba.ini, it will try to find the nro in /switch/mgba.nro and /switch/folder/mgba.nro.
|
||||
|
||||
see `assets/romfs/assoc/` for more examples of file assoc entries
|
||||
See `assets/romfs/assoc/` for more examples of file assoc entries.
|
||||
|
||||
## Credits
|
||||
|
||||
@@ -57,7 +59,7 @@ see `assets/romfs/assoc/` for more examples of file assoc entries
|
||||
- deko3d-nanovg
|
||||
- libpulsar
|
||||
- minIni
|
||||
- gbatemp
|
||||
- GBATemp
|
||||
- hb-appstore
|
||||
- haze
|
||||
- everyone who has contributed to this project!
|
||||
- Everyone who has contributed to this project!
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"Details": "Details",
|
||||
"Update": "Update",
|
||||
"Remove": "Entfernen",
|
||||
"Restore": "",
|
||||
"Restore": "Wiederherstellen",
|
||||
"Download": "Download",
|
||||
"Next Page": "Nächste Seite",
|
||||
"Prev Page": "Vorherige Seite",
|
||||
@@ -27,9 +27,12 @@
|
||||
"Star": "Favorit",
|
||||
"System memory": "System-Speicher",
|
||||
"microSD card": "microSD-Karte",
|
||||
"Sd": "",
|
||||
"Image System memory": "",
|
||||
"Image microSD card": "",
|
||||
"Sd": "SD",
|
||||
"Image System memory": "System-Speicher Bild",
|
||||
"Image microSD card": "microSD-Karten Bild",
|
||||
"Slow": "Langsam",
|
||||
"Normal": "Normal",
|
||||
"Fast": "Schnell",
|
||||
"Yes": "Ja",
|
||||
"No": "Nein",
|
||||
"Enabled": "Aktiviert",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "Alphabetisch (Favoriten)",
|
||||
"Likes": "Likes",
|
||||
"ID": "ID",
|
||||
"Decending": "Absteigend",
|
||||
"Descending": "Absteigend",
|
||||
"Descending (down)": "Absteigend",
|
||||
"Desc": "Abst.",
|
||||
"Ascending": "Aufsteigend",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "Theme auswählen",
|
||||
"Shuffle": "Zufällig",
|
||||
"Music": "Musik",
|
||||
"12 Hour Time": "",
|
||||
"Network": "Netzwerk",
|
||||
"Network Options": "Netzwerk-Optionen",
|
||||
"Ftp": "FTP",
|
||||
@@ -87,6 +91,7 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "Logging",
|
||||
"Replace hbmenu on exit": "hbmenu beim Beenden ersetzen",
|
||||
"Misc": "Sonstiges",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Install forwarders": "Forwarder installieren",
|
||||
"Install location": "Installationsort",
|
||||
"Show install warning": "Installationswarnung anzeigen",
|
||||
"Text scroll speed": "Textlaufgeschwindigkeit",
|
||||
|
||||
"FileBrowser": "Datei-Browser",
|
||||
"%zd files": "%zd Dateien",
|
||||
@@ -117,8 +123,8 @@
|
||||
"Create Folder": "Ordner erstellen",
|
||||
"Set Folder Name": "Ordnernamen eingeben",
|
||||
"View as text (unfinished)": "Als Text anzeigen (Beta)",
|
||||
"Ignore read only": "",
|
||||
"Mount": "",
|
||||
"Ignore read only": "Schreibschutz ignorieren",
|
||||
"Mount": "Einbinden",
|
||||
"Empty...": "Leer...",
|
||||
"Open with DayBreak?": "Mit DayBreak öffnen?",
|
||||
"Launch ": "Starten ",
|
||||
@@ -206,9 +212,9 @@
|
||||
"Bad Page": "Ungültige Seite",
|
||||
"Download theme?": "Theme herunterladen?",
|
||||
|
||||
"GitHub": "",
|
||||
"Downloading json": "",
|
||||
"Select asset to download for ": "",
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Lade JSON herunter",
|
||||
"Select asset to download for ": "Wähle Asset zum Download für ",
|
||||
|
||||
"Installing ": "Installiere ",
|
||||
"Uninstalling ": "Deinstalliere ",
|
||||
@@ -222,8 +228,8 @@
|
||||
"Copying ": "Kopiere ",
|
||||
"Trying to load ": "Lade ",
|
||||
"Downloading ": "Lade herunter ",
|
||||
"Downloaded ": "",
|
||||
"Removed ": "",
|
||||
"Downloaded ": "Heruntergeladen ",
|
||||
"Removed ": "Entfernt ",
|
||||
"Checking MD5": "Prüfe MD5",
|
||||
"Loading...": "Lade...",
|
||||
"Loading": "Lade",
|
||||
@@ -233,17 +239,19 @@
|
||||
"Update avaliable: ": "Update verfügbar: ",
|
||||
"Download update: ": "Update herunterladen: ",
|
||||
"Updated to ": "Aktualisiert auf ",
|
||||
"Press OK to restart Sphaira": "OK drücken um Sphaira neuzustarten",
|
||||
"Restart Sphaira?": "Sphaira neustarten?",
|
||||
"Failed to download update": "Update-Download fehlgeschlagen",
|
||||
"Restore hbmenu?": "",
|
||||
"Failed to find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "",
|
||||
"Failed to restore hbmenu, using sphaira instead": "",
|
||||
"Restored hbmenu, closing sphaira": "",
|
||||
"Restored hbmenu": "",
|
||||
"Restore hbmenu?": "hbmenu wiederherstellen?",
|
||||
"Failed to find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "Konnte /switch/hbmenu.nro nicht finden\nBitte hbmenu über den AppStore neu installieren",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "Wiederherstellung fehlgeschlagen, bitte hbmenu neu herunterladen",
|
||||
"Failed to restore hbmenu, using sphaira instead": "Wiederherstellung fehlgeschlagen, verwende stattdessen Sphaira",
|
||||
"Restored hbmenu, closing sphaira": "hbmenu wiederhergestellt, Sphaira wird beendet",
|
||||
"Restored hbmenu": "hbmenu wiederhergestellt",
|
||||
"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?",
|
||||
"Audio disabled due to suspended game": "",
|
||||
"If this message appears repeatedly, please open an issue.": "Bei wiederholtem Auftreten bitte Issue erstellen."
|
||||
}
|
||||
@@ -30,6 +30,9 @@
|
||||
"Sd": "Sd",
|
||||
"Image System memory": "Image System memory",
|
||||
"Image microSD card": "Image microSD card",
|
||||
"Slow": "Slow",
|
||||
"Normal": "Normal",
|
||||
"Fast": "Fast",
|
||||
"Yes": "Yes",
|
||||
"No": "No",
|
||||
"Enabled": "Enabled",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "Alphabetical (Star)",
|
||||
"Likes": "Likes",
|
||||
"ID": "ID",
|
||||
"Decending": "Decending",
|
||||
"Descending": "Descending",
|
||||
"Descending (down)": "Descending (down)",
|
||||
"Desc": "Desc",
|
||||
"Ascending": "Ascending",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "Select Theme",
|
||||
"Shuffle": "Shuffle",
|
||||
"Music": "Music",
|
||||
"12 Hour Time": "12 Hour Time",
|
||||
"Network": "Network",
|
||||
"Network Options": "Network Options",
|
||||
"Ftp": "FTP",
|
||||
@@ -87,6 +91,7 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "Logging",
|
||||
"Replace hbmenu on exit": "Replace hbmenu on exit",
|
||||
"Misc": "Misc",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Install forwarders": "Install forwarders",
|
||||
"Install location": "Install location",
|
||||
"Show install warning": "Show install warning",
|
||||
"Text scroll speed": "Text scroll speed",
|
||||
|
||||
"FileBrowser": "FileBrowser",
|
||||
"%zd files": "%zd files",
|
||||
@@ -233,6 +239,7 @@
|
||||
"Update avaliable: ": "Update avaliable: ",
|
||||
"Download update: ": "Download update: ",
|
||||
"Updated to ": "Updated to ",
|
||||
"Press OK to restart Sphaira": "Press OK to restart Sphaira",
|
||||
"Restart Sphaira?": "Restart Sphaira?",
|
||||
"Failed to download update": "Failed to download update",
|
||||
"Restore hbmenu?": "Restore hbmenu?",
|
||||
@@ -245,5 +252,6 @@
|
||||
"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?",
|
||||
"Audio disabled due to suspended game": "Audio disabled due to suspended game",
|
||||
"If this message appears repeatedly, please open an issue.": "If this message appears repeatedly, please open an issue."
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
"Details": "Detalles",
|
||||
"Update": "Actualizar",
|
||||
"Remove": "Borrar",
|
||||
"Restore": "",
|
||||
"Restore": "Restaurar",
|
||||
"Download": "Descargar",
|
||||
"Next Page": "Página siguiente",
|
||||
"Prev Page": "Página anterior",
|
||||
@@ -27,9 +27,12 @@
|
||||
"Star": "Favorito",
|
||||
"System memory": "Memoria de sistema",
|
||||
"microSD card": "microSD",
|
||||
"Sd": "",
|
||||
"Image System memory": "",
|
||||
"Image microSD card": "",
|
||||
"Sd": "SD",
|
||||
"Image System memory": "Imagen memoria interna",
|
||||
"Image microSD card": "Imagen tarjeta microSD",
|
||||
"Slow": "Lento",
|
||||
"Normal": "Normal",
|
||||
"Fast": "Rápido",
|
||||
"Yes": "Sí",
|
||||
"No": "No",
|
||||
"Enabled": "Activado",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "Alfabético (favorito)",
|
||||
"Likes": "Me Gusta",
|
||||
"ID": "ID",
|
||||
"Decending": "Descendente",
|
||||
"Descending": "Descendente",
|
||||
"Descending (down)": "Descendente (abajo)",
|
||||
"Desc": "Descendente",
|
||||
"Ascending": "Ascendente",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "Seleccionar tema",
|
||||
"Shuffle": "Barajar",
|
||||
"Music": "Música",
|
||||
"12 Hour Time": "",
|
||||
"Network": "Red",
|
||||
"Network Options": "Opciones de red",
|
||||
"Ftp": "FTP",
|
||||
@@ -87,18 +91,20 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Logging": "Registros",
|
||||
"Replace hbmenu on exit": "Reemplazar hbmenu al salir",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "Registro",
|
||||
"Replace hbmenu on exit": "Reemplazar hbmenu",
|
||||
"Misc": "Varios",
|
||||
"Misc Options": "Opciones varias",
|
||||
"Web": "Web",
|
||||
"Install forwarders": "Instalar forwarders",
|
||||
"Install location": "Ruta de instalación ",
|
||||
"Show install warning": "Mostrar precaución de instalación",
|
||||
"Install location": "Dispositivo de instalación",
|
||||
"Show install warning": "Precaución de instalación",
|
||||
"Text scroll speed": "Velocidad de scroll",
|
||||
|
||||
"FileBrowser": "Explorador de archivos",
|
||||
"%zd files": "%zd files",
|
||||
"%zd dirs": "%zd dirs",
|
||||
"%zd files": "%zd archivos",
|
||||
"%zd dirs": "%zd carpetas",
|
||||
"File Options": "Opciones de archivo",
|
||||
"Show Hidden": "Mostrar archivos ocultos",
|
||||
"Folders First": "Carpetas primero",
|
||||
@@ -117,15 +123,15 @@
|
||||
"Create Folder": "Crear carpeta",
|
||||
"Set Folder Name": "Establecer nombre de carpeta",
|
||||
"View as text (unfinished)": "Ver como texto (sin terminar)",
|
||||
"Ignore read only": "",
|
||||
"Mount": "",
|
||||
"Ignore read only": "Ignorar sólo lectura",
|
||||
"Mount": "Montar",
|
||||
"Empty...": "Vacío...",
|
||||
"Open with DayBreak?": "¿Abrir con DayBreak?",
|
||||
"Launch ": "Abrir ",
|
||||
"Launch option for: ": "Opción de abrir con: ",
|
||||
"Select launcher for: ": "Seleccionar abrir con: ",
|
||||
|
||||
"Homebrew": "Honebrew",
|
||||
"Homebrew": "Homebrew",
|
||||
"Homebrew Options": "Opciones de Homebrew",
|
||||
"Hide Sphaira": "Ocultar Sphaira",
|
||||
"Install Forwarder": "Instalar Forwarder",
|
||||
@@ -162,16 +168,16 @@
|
||||
"Irs": "IRS",
|
||||
"Ambient Noise Level: ": "Nivel de Ruido Ambiente",
|
||||
"Controller": "Control",
|
||||
"Pad ": "Almohadilla ",
|
||||
"Pad ": "GamePad ",
|
||||
" (Available)": " (Disponible)",
|
||||
" (Unsupported)": "(No Compatible)",
|
||||
" (Unconnected)": " (Desconectado)",
|
||||
"HandHeld": "Portátil",
|
||||
"Rotation": "Rotación",
|
||||
"0 (Sideways)": "0 (De lado)",
|
||||
"90 (Flat)": "90 (Plano)",
|
||||
"180 (-Sideways)": "180 (-De lado)",
|
||||
"270 (Upside down)": "270 (Al revés)",
|
||||
"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": "Paleta térmica",
|
||||
@@ -206,9 +212,9 @@
|
||||
"Bad Page": "Página Errónea",
|
||||
"Download theme?": "¿Descargar Tema?",
|
||||
|
||||
"GitHub": "",
|
||||
"Downloading json": "",
|
||||
"Select asset to download for ": "",
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Descargando json",
|
||||
"Select asset to download for ": "Seleccionar recurso a descargar para ",
|
||||
|
||||
"Installing ": "Instalando ",
|
||||
"Uninstalling ": "Desinstalando ",
|
||||
@@ -220,10 +226,10 @@
|
||||
"Scanning ": "Escaneando ",
|
||||
"Creating ": "Creando ",
|
||||
"Copying ": "Copiando ",
|
||||
"Trying to load ": "Intentando cargar",
|
||||
"Trying to load ": "Intentando cargar ",
|
||||
"Downloading ": "Descargando ",
|
||||
"Downloaded ": "",
|
||||
"Removed ": "",
|
||||
"Downloaded ": "Descargado ",
|
||||
"Removed ": "Removido ",
|
||||
"Checking MD5": "Chequeando MD5",
|
||||
"Loading...": "Cargando...",
|
||||
"Loading": "Cargando",
|
||||
@@ -233,17 +239,19 @@
|
||||
"Update avaliable: ": "Actualización disponible: ",
|
||||
"Download update: ": "Descargar actualización: ",
|
||||
"Updated to ": "Actualizado a ",
|
||||
"Restart Sphaira?": "¿Reiniciar Sphaira?",
|
||||
"Press OK to restart Sphaira": "Presiona OK para reiniciar sphaira",
|
||||
"Restart Sphaira?": "¿Reiniciar sphaira?",
|
||||
"Failed to download update": "Fallo al descargar actualización",
|
||||
"Restore hbmenu?": "",
|
||||
"Failed to find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "",
|
||||
"Failed to restore hbmenu, using sphaira instead": "",
|
||||
"Restored hbmenu, closing sphaira": "",
|
||||
"Restored hbmenu": "",
|
||||
"Restore hbmenu?": "¿Restaurar hbmenu?",
|
||||
"Failed to find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "Fallo al encontrar /switch/hbmenu.nro\nUsar la Tienda para reinstalar hbmenu",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "Fallo al restaurar hbmenu, por favor volver a descargar hbmenu",
|
||||
"Failed to restore hbmenu, using sphaira instead": "Fallo al restaurar hbmenu, se usará sphaira",
|
||||
"Restored hbmenu, closing sphaira": "hbmenu restaurado, cerrando sphaira",
|
||||
"Restored hbmenu": "hbmenu restaurado",
|
||||
"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?",
|
||||
"Audio disabled due to suspended game": "",
|
||||
"If this message appears repeatedly, please open an issue.": "Si este mensaje aparece repetidamente, por favor abrir un 'issue'."
|
||||
}
|
||||
@@ -30,6 +30,9 @@
|
||||
"Sd": "Sd",
|
||||
"Image System memory": "Image de la mémoire System",
|
||||
"Image microSD card": "Image de la Carte microSD",
|
||||
"Slow": "Lent",
|
||||
"Normal": "Normal",
|
||||
"Fast": "Rapide",
|
||||
"Yes": "Oui",
|
||||
"No": "Non",
|
||||
"Enabled": "Activé(e)",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "Alphabétique (Favories)",
|
||||
"Likes": "Likes",
|
||||
"ID": "ID",
|
||||
"Decending": "Décroissant",
|
||||
"Descending": "Décroissant",
|
||||
"Descending (down)": "Décroissant",
|
||||
"Desc": "Décroissant",
|
||||
"Ascending": "Croissant",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "Choisir un Thème",
|
||||
"Shuffle": "Aléatoire",
|
||||
"Music": "Musique",
|
||||
"12 Hour Time": "Temps sur 12 heures",
|
||||
"Network": "Réseau",
|
||||
"Network Options": "Options Réseau",
|
||||
"Ftp": "FTP",
|
||||
@@ -87,6 +91,7 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "Journalisation",
|
||||
"Replace hbmenu on exit": "Remplacer hbmenu quand quitté",
|
||||
"Misc": "Divers",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Install forwarders": "Installer les Forwarders",
|
||||
"Install location": "Emplacement d'installation",
|
||||
"Show install warning": "Afficher l'avertissement d'installation",
|
||||
"Text scroll speed": "Vitesse de défilement du texte",
|
||||
|
||||
"FileBrowser": "Explorateur de Fichiers",
|
||||
"%zd files": "%zd fichiers",
|
||||
@@ -233,6 +239,7 @@
|
||||
"Update avaliable: ": "Mise à jour disponible: ",
|
||||
"Download update: ": "Télécharger la mise à jour: ",
|
||||
"Updated to ": "Mis à jour vers ",
|
||||
"Press OK to restart Sphaira": "Appuyez sur OK pour redémarrer Sphaira",
|
||||
"Restart Sphaira?": "Redémarrer Sphaira?",
|
||||
"Failed to download update": "Echec du téléchargement de la mise à jour",
|
||||
"Restore hbmenu?": "Restaurer hbmenu?",
|
||||
@@ -245,5 +252,6 @@
|
||||
"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?",
|
||||
"Audio disabled due to suspended game": "Audio désactivé à cause d'un jeu suspendu",
|
||||
"If this message appears repeatedly, please open an issue.": "Si ce message apparait en boucle veuillez ouvrir une issue."
|
||||
}
|
||||
@@ -30,6 +30,9 @@
|
||||
"Sd": "SD",
|
||||
"Image System memory": "Immagine memoria di sistema",
|
||||
"Image microSD card": "Immagine scheda microSD",
|
||||
"Slow": "",
|
||||
"Normal": "",
|
||||
"Fast": "",
|
||||
"Yes": "Sì",
|
||||
"No": "No",
|
||||
"Enabled": "Abilitato",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "Alfabetico (Preferiti)",
|
||||
"Likes": "Mi Piace",
|
||||
"ID": "ID",
|
||||
"Decending": "Decrescente",
|
||||
"Descending": "Decrescente",
|
||||
"Descending (down)": "Decrescente",
|
||||
"Desc": "Decrescente",
|
||||
"Ascending": "Crescente",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "Seleziona tema",
|
||||
"Shuffle": "Mescola",
|
||||
"Music": "Musica",
|
||||
"12 Hour Time": "",
|
||||
"Network": "Rete",
|
||||
"Network Options": "Opzioni di rete",
|
||||
"Ftp": "FTP",
|
||||
@@ -87,6 +91,7 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "Logging",
|
||||
"Replace hbmenu on exit": "Sostituisci hbmenu all'uscita",
|
||||
"Misc": "Varie",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Install forwarders": "Installa forwarder",
|
||||
"Install location": "Installa posizione",
|
||||
"Show install warning": "Mostra avvertimento installazione",
|
||||
"Text scroll speed": "",
|
||||
|
||||
"FileBrowser": "FileBrowser",
|
||||
"%zd files": "%zd files",
|
||||
@@ -198,52 +204,54 @@
|
||||
"Load Default": "Carica predefinito",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "",
|
||||
"Nsfw": "",
|
||||
"Page": "",
|
||||
"Page %zu / %zu": "Page %zu / %zu",
|
||||
"Enter Page Number": "",
|
||||
"Bad Page": "",
|
||||
"Download theme?": "",
|
||||
"Themezer Options": "Impostazioni Themezer",
|
||||
"Nsfw": "NSFW",
|
||||
"Page": "Pagina",
|
||||
"Page %zu / %zu": "Pagina %zu / %zu",
|
||||
"Enter Page Number": "Inserisci il numero della pagina",
|
||||
"Bad Page": "Pagina invalida",
|
||||
"Download theme?": "Vuoi scaricare il tema?",
|
||||
|
||||
"GitHub": "",
|
||||
"Downloading json": "",
|
||||
"Select asset to download for ": "",
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Scaricamento json",
|
||||
"Select asset to download for": "Scegli l'asset da scaricare per",
|
||||
|
||||
"Installing ": "",
|
||||
"Uninstalling ": "",
|
||||
"Deleting ": "",
|
||||
"Deleting": "",
|
||||
"Pasting ": "",
|
||||
"Pasting": "",
|
||||
"Removing ": "",
|
||||
"Scanning ": "",
|
||||
"Creating ": "",
|
||||
"Copying ": "",
|
||||
"Trying to load ": "",
|
||||
"Downloading ": "",
|
||||
"Downloaded ": "",
|
||||
"Removed ": "",
|
||||
"Checking MD5": "",
|
||||
"Loading...": "",
|
||||
"Loading": "",
|
||||
"Empty!": "",
|
||||
"Not Ready...": "",
|
||||
"Error loading page!": "",
|
||||
"Update avaliable: ": "",
|
||||
"Download update: ": "",
|
||||
"Updated to ": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Failed to download update": "",
|
||||
"Restore hbmenu?": "",
|
||||
"Failed to find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "",
|
||||
"Failed to restore hbmenu, using sphaira instead": "",
|
||||
"Restored hbmenu, closing sphaira": "",
|
||||
"Restored hbmenu": "",
|
||||
"Delete Selected files?": "",
|
||||
"Completely remove ": "",
|
||||
"Installing ": "Installazione",
|
||||
"Uninstalling ": "Disinstallazione",
|
||||
"Deleting ": "Eliminazione",
|
||||
"Deleting": "Eliminazione",
|
||||
"Pasting ": "Incollo",
|
||||
"Pasting": "Incollo",
|
||||
"Removing ": "Rimozione",
|
||||
"Scanning ": "Scan",
|
||||
"Creating ": "Creazione",
|
||||
"Copying ": "Copio",
|
||||
"Trying to load ": "Cercando di caricare",
|
||||
"Downloading ": "Scaricando",
|
||||
"Downloaded ": "Scaricato",
|
||||
"Removed ": ""Rimosso,
|
||||
"Checking MD5": "Controllo MD5",
|
||||
"Loading...": "Caricamento...",
|
||||
"Loading": "Caricamento",
|
||||
"Empty!": "Vuoto!",
|
||||
"Not Ready...": "Non pronto...",
|
||||
"Error loading page!": "Errore nel caricare la pagina!",
|
||||
"Update avaliable: ": "Aggiornamento disponibile",
|
||||
"Download update: ": "Scarica aggiornamento",
|
||||
"Updated to ": "Aggiornato a",
|
||||
"Press OK to restart Sphaira": "Premi OK per riavviare Sphaira",
|
||||
"Restart Sphaira?": "Vuoi riavviare Sphaira?",
|
||||
"Failed to download update": "Download aggiornamento fallito",
|
||||
"Restore hbmenu?": "Vuoi ripristinare hbmenu?",
|
||||
"Failed to find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "Impossibile trovare /switch/hbmenu.nro\nUsa l'Appstore per reinstallare hbmenu",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "Impossibile ripristinare hbmenu, per favore riscaricalo",
|
||||
"Failed to restore hbmenu, using sphaira instead": "Impossibile ripristinare hbmenu, uso Sphaira invece",
|
||||
"Restored hbmenu, closing sphaira": "hbmenu ripristinato, chiudo Sphaira",
|
||||
"Restored hbmenu": "hbmenu ripristinato",
|
||||
"Delete Selected files?": "Vuoi rimuovere i file selezionati?",
|
||||
"Completely remove ": "Elimina definitivamente",
|
||||
"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.": ""
|
||||
"Are you sure you wish to cancel?": "Sei sicuro di voler annullare?",
|
||||
"Audio disabled due to suspended game": "Audio disabilitato poichè un app è in pausa",
|
||||
"If this message appears repeatedly, please open an issue.": "Se questo messaggio appare frequentemente, segnala il bug."
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
"Sd": "SDメモリーカード",
|
||||
"Image System memory": "システムメモリイメージ",
|
||||
"Image microSD card": "SDイメージ",
|
||||
"Slow": "遅い",
|
||||
"Normal": "普通",
|
||||
"Fast": "速い",
|
||||
"Yes": "はい",
|
||||
"No": "いいえ",
|
||||
"Enabled": "",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "アルファベット順(お気に入り)",
|
||||
"Likes": "いいね順",
|
||||
"ID": "デベロッパー順",
|
||||
"Decending": "降順",
|
||||
"Descending": "降順",
|
||||
"Descending (down)": "降順",
|
||||
"Desc": "降順",
|
||||
"Ascending": "上昇",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "テーマを選ぶ",
|
||||
"Shuffle": "シャッフル",
|
||||
"Music": "BGM",
|
||||
"12 Hour Time": "",
|
||||
"Network": "ネットワーク",
|
||||
"Network Options": "ネットワーク設定",
|
||||
"Ftp": "FTP",
|
||||
@@ -87,6 +91,7 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "ログの取得",
|
||||
"Replace hbmenu on exit": "終了時に hbmenu を置き換える",
|
||||
"Misc": "その他",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Install forwarders": "Forwarderのインストール機能",
|
||||
"Install location": "インストール経路",
|
||||
"Show install warning": "警告文を示す",
|
||||
"Text scroll speed": "流れる文字の速さ",
|
||||
|
||||
"FileBrowser": "ファイルブラウザ",
|
||||
"%zd files": "%zd個のファイル",
|
||||
@@ -233,6 +239,7 @@
|
||||
"Update avaliable: ": "アップデート可能: ",
|
||||
"Download update: ": "アップデートをダウンロード: ",
|
||||
"Updated to ": "アップデート: ",
|
||||
"Press OK to restart Sphaira": "確認ボタンを押してSphairaを再起動",
|
||||
"Restart Sphaira?": "Sphairaを再起動しますか?",
|
||||
"Failed to download update": "アップデートのダウンロード失敗",
|
||||
"Restore hbmenu?": "hbmenuに戻しますか?",
|
||||
@@ -245,5 +252,6 @@
|
||||
"Completely remove ": "除去しますか ",
|
||||
"Are you sure you want to delete ": "消去してもよろしいですか ",
|
||||
"Are you sure you wish to cancel?": "本当に取り消しますか?",
|
||||
"Audio disabled due to suspended game": "ゲームが一時停止状態の場合、オーディオは無効になります",
|
||||
"If this message appears repeatedly, please open an issue.": "このメッセージが繰り返し表示される場合は、問題を開いてください。"
|
||||
}
|
||||
@@ -30,6 +30,9 @@
|
||||
"Sd": "SD 카드",
|
||||
"Image System memory": "낸드 이미지",
|
||||
"Image microSD card": "SD 이미지",
|
||||
"Slow": "느림",
|
||||
"Normal": "보통",
|
||||
"Fast": "빠름",
|
||||
"Yes": "예",
|
||||
"No": "아니요",
|
||||
"Enabled": "",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "알파벳순 (즐겨찾기)",
|
||||
"Likes": "좋아요순",
|
||||
"ID": "ID순",
|
||||
"Decending": "내림차순",
|
||||
"Descending": "내림차순",
|
||||
"Descending (down)": "내림차순",
|
||||
"Desc": "내림차순",
|
||||
"Ascending": "오름차순",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "테마 선택",
|
||||
"Shuffle": "셔플",
|
||||
"Music": "BGM",
|
||||
"12 Hour Time": "",
|
||||
"Network": "네트워크",
|
||||
"Network Options": "네트워크 옵션",
|
||||
"Ftp": "FTP (무선)",
|
||||
@@ -87,6 +91,7 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "로깅",
|
||||
"Replace hbmenu on exit": "종료 시 hbmenu 교체",
|
||||
"Misc": "기타",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Install forwarders": "바로가기 설치",
|
||||
"Install location": "설치 위치",
|
||||
"Show install warning": "경고 메시지",
|
||||
"Text scroll speed": "흐르는 텍스트 속도",
|
||||
|
||||
"FileBrowser": "파일 탐색기",
|
||||
"%zd files": "%zd 개 파일",
|
||||
@@ -233,6 +239,7 @@
|
||||
"Update avaliable: ": "업데이트 가능: ",
|
||||
"Download update: ": "업데이트 다운로드: ",
|
||||
"Updated to ": "업데이트: ",
|
||||
"Press OK to restart Sphaira": "확인 버튼 입력하여 Sphaira 재시작",
|
||||
"Restart Sphaira?": "Sphaira를 재시작할까요?",
|
||||
"Failed to download update": "업데이트 다운로드 실패함",
|
||||
"Restore hbmenu?": "hbmenu로 교체할까요?",
|
||||
@@ -245,5 +252,6 @@
|
||||
"Completely remove ": "정말 삭제할까요 ",
|
||||
"Are you sure you want to delete ": "정말 삭제할까요 ",
|
||||
"Are you sure you wish to cancel?": "정말 취소할까요?",
|
||||
"Audio disabled due to suspended game": "게임 실행 중에는 BGM이 비활성화 됩니다",
|
||||
"If this message appears repeatedly, please open an issue.": "해당 메시지가 반복해서 나타나는 경우, 이슈를 등록하세요."
|
||||
}
|
||||
@@ -30,6 +30,9 @@
|
||||
"Sd": "",
|
||||
"Image System memory": "",
|
||||
"Image microSD card": "",
|
||||
"Slow": "",
|
||||
"Normal": "",
|
||||
"Fast": "",
|
||||
"Yes": "Ja",
|
||||
"No": "Nee",
|
||||
"Enabled": "Ingeschakeld",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "",
|
||||
"Likes": "",
|
||||
"ID": "",
|
||||
"Decending": "Aflopend",
|
||||
"Descending": "Aflopend",
|
||||
"Descending (down)": "Aflopend",
|
||||
"Desc": "Aflopend",
|
||||
"Ascending": "Oplopend",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "Selecteer Thema",
|
||||
"Shuffle": "Schudden",
|
||||
"Music": "Muziek",
|
||||
"12 Hour Time": "",
|
||||
"Network": "Netwerk",
|
||||
"Network Options": "Netwerkopties",
|
||||
"Ftp": "FTP",
|
||||
@@ -87,6 +91,7 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "Loggen",
|
||||
"Replace hbmenu on exit": "Vervang hbmenu bij afsluiten",
|
||||
"Misc": "Diversen",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Install forwarders": "",
|
||||
"Install location": "",
|
||||
"Show install warning": "",
|
||||
"Text scroll speed": "",
|
||||
|
||||
"FileBrowser": "Bestandsbrowser",
|
||||
"%zd files": "%zd files",
|
||||
@@ -233,6 +239,7 @@
|
||||
"Update avaliable: ": "",
|
||||
"Download update: ": "",
|
||||
"Updated to ": "",
|
||||
"Press OK to restart Sphaira": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Failed to download update": "",
|
||||
"Restore hbmenu?": "",
|
||||
@@ -245,5 +252,6 @@
|
||||
"Completely remove ": "",
|
||||
"Are you sure you want to delete ": "Weet u zeker dat u wilt verwijderen ",
|
||||
"Are you sure you wish to cancel?": "",
|
||||
"Audio disabled due to suspended game": "",
|
||||
"If this message appears repeatedly, please open an issue.": ""
|
||||
}
|
||||
@@ -28,45 +28,49 @@
|
||||
"System memory": "Memória do console",
|
||||
"microSD card": "Cartão microSD",
|
||||
"Sd": "SD",
|
||||
"Image System memory": "Imagem (Memória do console)",
|
||||
"Image microSD card": "Imagem (Cartão microSD)",
|
||||
"Image System memory": "Imagem (memória do console)",
|
||||
"Image microSD card": "Imagem (cartão microSD)",
|
||||
"Slow": "Lenta",
|
||||
"Normal": "Normal",
|
||||
"Fast": "Rápida",
|
||||
"Yes": "Sim",
|
||||
"No": "Não",
|
||||
"Enabled": "Habilitado",
|
||||
"Disabled": "Desabilitado",
|
||||
"Enabled": "Sim",
|
||||
"Disabled": "Não",
|
||||
|
||||
"Sort By": "Ordenar por",
|
||||
"Sort Options": "Opções de classificação",
|
||||
"Sort By": "Ordernar/Organizar",
|
||||
"Sort Options": "Ordernar/Organizar",
|
||||
"Filter": "Filtro",
|
||||
"Sort": "Organizar",
|
||||
"Sort": "Organizar por",
|
||||
"Order": "Ordem",
|
||||
"Search": "Procurar",
|
||||
"Search": "Buscar",
|
||||
"Updated": "Atualizado",
|
||||
"Updated (Star)": "Atualizado (Favoritos)",
|
||||
"Downloads": "Downloads",
|
||||
"Updated (Star)": "Atualizado (favoritos)",
|
||||
"Downloads": "Nº de downloads",
|
||||
"Size": "Tamanho",
|
||||
"Size (Star)": "Tamanho (Favoritos)",
|
||||
"Alphabetical": "Alfabético",
|
||||
"Alphabetical (Star)": "Alfabético (Favoritos)",
|
||||
"Likes": "Curtidas",
|
||||
"Size (Star)": "Tamanho (favoritos)",
|
||||
"Alphabetical": "Ordem alfabética",
|
||||
"Alphabetical (Star)": "Ordem alfabética (favoritos)",
|
||||
"Likes": "Nº de curtidas",
|
||||
"ID": "ID",
|
||||
"Decending": "Decrescente",
|
||||
"Descending (down)": "Decrescente (Baixo)",
|
||||
"Descending": "Decrescente",
|
||||
"Descending (down)": "Decrescente (baixo)",
|
||||
"Desc": "Decr.",
|
||||
"Ascending": "Ascendente",
|
||||
"Ascending (Up)": "Ascendente (Cima)",
|
||||
"Ascending (Up)": "Ascendente (cima)",
|
||||
"Asc": "Asc.",
|
||||
|
||||
"Menu Options": "Opções do menu",
|
||||
"Theme": "Tema",
|
||||
"Theme Options": "Opções de tema",
|
||||
"Select Theme": "Selecionar tema",
|
||||
"Shuffle": "Embaralhar",
|
||||
"Select Theme": "Tema atual",
|
||||
"Shuffle": "Embaralhar temas",
|
||||
"Music": "Música",
|
||||
"12 Hour Time": "",
|
||||
"Network": "Rede",
|
||||
"Network Options": "Opções de rede",
|
||||
"Ftp": "FTP",
|
||||
"Mtp": "MTP",
|
||||
"Ftp": "Servidor FTP",
|
||||
"Mtp": "Escuta MTP",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "Nxlink conectado",
|
||||
"Nxlink Upload": "Envio Nxlink",
|
||||
@@ -87,49 +91,51 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "Registro de depuração",
|
||||
"Replace hbmenu on exit": "Substituir hbmenu ao sair",
|
||||
"Misc": "Diversos",
|
||||
"Misc Options": "Opções diversas",
|
||||
"Web": "Navegador web",
|
||||
"Install forwarders": "Instalar forwarder",
|
||||
"Web": "Navegador de internet",
|
||||
"Install forwarders": "Instalar atalhos (forwarders)",
|
||||
"Install location": "Local de instalação",
|
||||
"Show install warning": "Mostrar aviso de instalação",
|
||||
"Text scroll speed": "Rolagem do texto",
|
||||
|
||||
"FileBrowser": "Navegador de arquivos",
|
||||
"FileBrowser": "Arquivos",
|
||||
"%zd files": "%zd arquivo(s)",
|
||||
"%zd dirs": "%zd diretório(s)",
|
||||
"File Options": "Opções de arquivo",
|
||||
"Show Hidden": "Mostrar ocultos",
|
||||
"Folders First": "Pastas primeiro",
|
||||
"Hidden Last": "Ocultos por último",
|
||||
"Cut": "Cortar",
|
||||
"Cut": "Recortar",
|
||||
"Copy": "Copiar",
|
||||
"Paste": "Colar",
|
||||
"Paste ": "Colar",
|
||||
"Paste ": "Colar ",
|
||||
" file(s)?": " arquivo(s)?",
|
||||
"Rename": "Renomear",
|
||||
"Set New File Name": "Definir novo nome do arquivo",
|
||||
"Set New File Name": "Defina o nome do novo arquivo",
|
||||
"Advanced": "Avançado",
|
||||
"Advanced Options": "Opções avançadas",
|
||||
"Create File": "Criar arquivo",
|
||||
"Set File Name": "Definir nome do arquivo",
|
||||
"Set File Name": "Defina o nome do arquivo",
|
||||
"Create Folder": "Criar pasta",
|
||||
"Set Folder Name": "Definir novo nome da pasta",
|
||||
"Set Folder Name": "Defina o nome da pasta",
|
||||
"View as text (unfinished)": "Ver como texto (inacabado)",
|
||||
"Ignore read only": "Ignorar somente leitura",
|
||||
"Mount": "Montar",
|
||||
"Empty...": "Vazio...",
|
||||
"Open with DayBreak?": "Abrir com DayBreak?",
|
||||
"Launch ": "Iniciar",
|
||||
"Launch ": "Iniciar ",
|
||||
"Launch option for: ": "Opções de inicialização para: ",
|
||||
"Select launcher for: ": "Selecionar launcher para: ",
|
||||
|
||||
"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: Instalar forwarders pode\nresultar em um banimento!",
|
||||
"Homebrew": "Aplicativos",
|
||||
"Homebrew Options": "Opções do aplicativo",
|
||||
"Hide Sphaira": "Esconder sphaira",
|
||||
"Install Forwarder": "Instalar atalho (forwarder)",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "AVISO: Instalar atalhos pode\nresultar em um banimento!",
|
||||
"Installing Forwarder": "Instalando forwarder",
|
||||
"Creating Program": "Criando Program",
|
||||
"Creating Control": "Criando Control",
|
||||
@@ -142,9 +148,9 @@
|
||||
"Unstarred ": "Desfavoritado ",
|
||||
"Starred ": "Favoritado ",
|
||||
|
||||
"AppStore": "AppStore",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filtro: %s | Organizar: %s | Ordem: %s",
|
||||
"AppStore Options": "Opções da AppStore",
|
||||
"AppStore": "Loja",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filtro: %s | Por: %s | Ordem: %s",
|
||||
"AppStore Options": "Opções da loja",
|
||||
"All": "Todos",
|
||||
"Games": "Jogos",
|
||||
"Emulators": "Emuladores",
|
||||
@@ -156,29 +162,29 @@
|
||||
"category: %s": "categoria: %s",
|
||||
"extracted: %.2f MiB": "tam. extraído: %.2f MiB",
|
||||
"app_dls: %s": "downloads: %s",
|
||||
"More by Author": "Mais do autor",
|
||||
"More by Author": "Mais deste autor",
|
||||
"Leave Feedback": "Deixar um feedback",
|
||||
|
||||
"Irs": "Irs",
|
||||
"Ambient Noise Level: ": "Nível de ruído ambiente",
|
||||
"Irs": "Sensor infravermelho",
|
||||
"Ambient Noise Level: ": "Nível de ruído ambiente: ",
|
||||
"Controller": "Controle",
|
||||
"Pad ": "Pad ",
|
||||
" (Available)": " (Disponível)",
|
||||
" (Unsupported)": "(Não suportado)",
|
||||
" (Unconnected)": " (Desconectado)",
|
||||
" (Available)": " (disponível)",
|
||||
" (Unsupported)": "(não suportado)",
|
||||
" (Unconnected)": " (desconectado)",
|
||||
"HandHeld": "Portátil",
|
||||
"Rotation": "Rotação",
|
||||
"0 (Sideways)": "0 (Lateralmente)",
|
||||
"0 (Sideways)": "0 (lateralmente)",
|
||||
"90 (Flat)": "90 (plano)",
|
||||
"180 (-Sideways)": "180 (-Lateralmente)",
|
||||
"270 (Upside down)": "270 (De cabeça para baixo)",
|
||||
"180 (-Sideways)": "180 (-lateralmente)",
|
||||
"270 (Upside down)": "270 (de cabeça para baixo)",
|
||||
"Colour": "Cor",
|
||||
"Grey": "Cinza",
|
||||
"Ironbow": "Arco de ferro",
|
||||
"Ironbow": "Ferro",
|
||||
"Green": "Verde",
|
||||
"Red": "Vermelho",
|
||||
"Blue": "Azul",
|
||||
"Light Target": "Alvo leve",
|
||||
"Light Target": "Alvo de luz",
|
||||
"All leds": "Todos os LEDs",
|
||||
"Bright group": "Grupo claro",
|
||||
"Dim group": "Grupo escuro",
|
||||
@@ -193,27 +199,27 @@
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"Trimming Format": "Formato de corte",
|
||||
"Trimming Format": "Formato do recorte",
|
||||
"External Light Filter": "Filtro de luz externa",
|
||||
"Load Default": "Carregar padrão",
|
||||
"Load Default": "Restaurar padrão",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Opções do Themezer",
|
||||
"Nsfw": "NSFW",
|
||||
"Page": "Página",
|
||||
"Page %zu / %zu": "Page %zu / %zu",
|
||||
"Enter Page Number": "Digite o número da página",
|
||||
"Nsfw": "Temas 18+ (NSFW)",
|
||||
"Page": "Ir para página",
|
||||
"Page %zu / %zu": "Página %zu / %zu",
|
||||
"Enter Page Number": "Número da página",
|
||||
"Bad Page": "Página inválida",
|
||||
"Download theme?": "Baixar tema?",
|
||||
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Baixando JSON",
|
||||
"Select asset to download for ": "Selecione o recurso para baixar em ",
|
||||
"Select asset to download for ": "Selecione o recurso para baixar de ",
|
||||
|
||||
"Installing ": "Instalando ",
|
||||
"Uninstalling ": "Desinstalando ",
|
||||
"Deleting ": "Deletando ",
|
||||
"Deleting": "Deletando ",
|
||||
"Deleting ": "Excluindo ",
|
||||
"Deleting": "Excluindo",
|
||||
"Pasting ": "Colando ",
|
||||
"Pasting": "Colando ",
|
||||
"Removing ": "Removendo ",
|
||||
@@ -222,18 +228,19 @@
|
||||
"Copying ": "Copiando ",
|
||||
"Trying to load ": "Tentando carregar ",
|
||||
"Downloading ": "Baixando ",
|
||||
"Downloaded ": "Baixado",
|
||||
"Removed ": "Removido",
|
||||
"Downloaded ": "Baixado ",
|
||||
"Removed ": "Removido ",
|
||||
"Checking MD5": "Checando MD5",
|
||||
"Loading...": "Carregando...",
|
||||
"Loading": "Carregando",
|
||||
"Empty!": "Vazio!",
|
||||
"Empty!": "Vazio",
|
||||
"Not Ready...": "Não está pronto...",
|
||||
"Error loading page!": "Erro ao carregar página!",
|
||||
"Update avaliable: ": "Atualização disponível: ",
|
||||
"Download update: ": "Baixar autalização: ",
|
||||
"Updated to ": "Atualizado para ",
|
||||
"Restart Sphaira?": "Reiniciar Sphaira?",
|
||||
"Press OK to restart Sphaira": "Selecione OK para reiniciar o sphaira",
|
||||
"Restart Sphaira?": "Reiniciar sphaira?",
|
||||
"Failed to download update": "Falha ao baixar a atualização",
|
||||
"Restore hbmenu?": "Restaurar hbmenu?",
|
||||
"Failed to find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "Falha ao buscar /switch/hbmenu.nro\nUse a AppStore para reinstalar o hbmenu",
|
||||
@@ -241,9 +248,10 @@
|
||||
"Failed to restore hbmenu, using sphaira instead": "Falha ao restaurar hbmenu, usando sphaira",
|
||||
"Restored hbmenu, closing sphaira": "hbmenu restaurado, fechando sphaira",
|
||||
"Restored hbmenu": "hbmenu restaurado",
|
||||
"Delete Selected files?": "Deletar arquivos selecionados?",
|
||||
"Delete Selected files?": "Excluir os arquivos selecionados?",
|
||||
"Completely remove ": "Remover completamente ",
|
||||
"Are you sure you want to delete ": "Você tem certeza que quer deletar ",
|
||||
"Are you sure you want to delete ": "Você tem certeza que quer excluir ",
|
||||
"Are you sure you wish to cancel?": "Você tem certeza que quer cancelar?",
|
||||
"Audio disabled due to suspended game": "Áudio desativado devido ao software suspenso",
|
||||
"If this message appears repeatedly, please open an issue.": "Se esta mensagem aparecer repetidamente, abra um issue."
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
"Sd": "",
|
||||
"Image System memory": "",
|
||||
"Image microSD card": "",
|
||||
"Slow": "",
|
||||
"Normal": "",
|
||||
"Fast": "",
|
||||
"Yes": "Да",
|
||||
"No": "Нет",
|
||||
"Enabled": "Включено",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "",
|
||||
"Likes": "",
|
||||
"ID": "",
|
||||
"Decending": "По убыванию",
|
||||
"Descending": "По убыванию",
|
||||
"Descending (down)": "По убыванию",
|
||||
"Desc": "По убыванию",
|
||||
"Ascending": "По возрастанию",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "Выберите тему",
|
||||
"Shuffle": "Перетасовать",
|
||||
"Music": "Музыка",
|
||||
"12 Hour Time": "",
|
||||
"Network": "Сеть",
|
||||
"Network Options": "Параметры сети",
|
||||
"Ftp": "FTP",
|
||||
@@ -87,6 +91,7 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "Журналирование",
|
||||
"Replace hbmenu on exit": "Заменить hbmenu при выходе",
|
||||
"Misc": "Прочее",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Install forwarders": "",
|
||||
"Install location": "",
|
||||
"Show install warning": "",
|
||||
"Text scroll speed": "",
|
||||
|
||||
"FileBrowser": "Файловый менеджер",
|
||||
"%zd files": "%zd files",
|
||||
@@ -233,6 +239,7 @@
|
||||
"Update avaliable: ": "",
|
||||
"Download update: ": "",
|
||||
"Updated to ": "",
|
||||
"Press OK to restart Sphaira": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Failed to download update": "",
|
||||
"Restore hbmenu?": "",
|
||||
@@ -245,5 +252,6 @@
|
||||
"Completely remove ": "",
|
||||
"Are you sure you want to delete ": "Вы уверены, что хотите удалить ",
|
||||
"Are you sure you wish to cancel?": "",
|
||||
"Audio disabled due to suspended game": "",
|
||||
"If this message appears repeatedly, please open an issue.": ""
|
||||
}
|
||||
@@ -30,6 +30,9 @@
|
||||
"Sd": "Sd",
|
||||
"Image System memory": "Avbild Systemminne",
|
||||
"Image microSD card": "Avbild microSD-kort",
|
||||
"Slow": "",
|
||||
"Normal": "",
|
||||
"Fast": "",
|
||||
"Yes": "Ja",
|
||||
"No": "Nej",
|
||||
"Enabled": "Aktiverad",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "Alfabetisk (Stjärna)",
|
||||
"Likes": "Gillar",
|
||||
"ID": "ID",
|
||||
"Decending": "Fallande",
|
||||
"Descending": "Fallande",
|
||||
"Descending (down)": "Fallande (nedåt)",
|
||||
"Desc": "Fall",
|
||||
"Ascending": "Stigande",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "Välj tema",
|
||||
"Shuffle": "Blanda",
|
||||
"Music": "Musik",
|
||||
"12 Hour Time": "",
|
||||
"Network": "Nätverk",
|
||||
"Network Options": "Nätverksalternativ",
|
||||
"Ftp": "FTP",
|
||||
@@ -87,6 +91,7 @@
|
||||
"Portuguese": "Portugisiska",
|
||||
"Russian": "Ryska",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "Loggning",
|
||||
"Replace hbmenu on exit": "Ersätt hbmenu vid avslut",
|
||||
"Misc": "Övrigt",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Install forwarders": "Installera genvägar",
|
||||
"Install location": "Installationsplats",
|
||||
"Show install warning": "Visa installationsvarning",
|
||||
"Text scroll speed": "",
|
||||
|
||||
"FileBrowser": "Filbläddrare",
|
||||
"%zd files": "%zd filer",
|
||||
@@ -233,6 +239,7 @@
|
||||
"Update avaliable: ": "Uppdatering tillgänglig: ",
|
||||
"Download update: ": "Ladda ner uppdatering: ",
|
||||
"Updated to ": "Uppdaterad till ",
|
||||
"Press OK to restart Sphaira": "",
|
||||
"Restart Sphaira?": "Starta om Sphaira?",
|
||||
"Failed to download update": "Misslyckades att ladda ner uppdatering",
|
||||
"Restore hbmenu?": "Återställ hbmenu?",
|
||||
@@ -245,5 +252,6 @@
|
||||
"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?",
|
||||
"Audio disabled due to suspended game": "Ljud är avstängt på grund av bakgrundsprogram",
|
||||
"If this message appears repeatedly, please open an issue.": "Om detta meddelande visas upprepade gånger, vänligen öppna en felanmälan.",
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,9 @@
|
||||
"Sd": "Sd",
|
||||
"Image System memory": "Bộ nhớ hệ thống hình ảnh",
|
||||
"Image microSD card": "Thẻ nhớ hệ thống hình ảnh",
|
||||
"Slow": "",
|
||||
"Normal": "",
|
||||
"Fast": "",
|
||||
"Yes": "Có",
|
||||
"No": "Không",
|
||||
"Enabled": "Bật",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "A-Z (Yêu thích)",
|
||||
"Likes": "Thích",
|
||||
"ID": "ID",
|
||||
"Decending": "Giảm dần",
|
||||
"Descending": "Giảm dần",
|
||||
"Descending (down)": "Giảm dần (xuống)",
|
||||
"Desc": "Giảm",
|
||||
"Ascending": "Tăng dần",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "Chọn Theme",
|
||||
"Shuffle": "Trộn",
|
||||
"Music": "Âm nhạc",
|
||||
"12 Hour Time": "",
|
||||
"Network": "Mạng",
|
||||
"Network Options": "Tuỳ chọn mạng",
|
||||
"Ftp": "FTP",
|
||||
@@ -76,7 +80,6 @@
|
||||
"Language": "Ngôn ngữ",
|
||||
"Auto": "Tự động",
|
||||
"English": "English",
|
||||
"Vietnamese": "Việt Nam",
|
||||
"Japanese": "日本語",
|
||||
"French": "Français",
|
||||
"German": "Deutsch",
|
||||
@@ -88,6 +91,7 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Việt Nam",
|
||||
"Logging": "Logging",
|
||||
"Replace hbmenu on exit": "Thay thế hbmenu khi thoát",
|
||||
"Misc": "Tiện ích",
|
||||
@@ -96,6 +100,7 @@
|
||||
"Install forwarders": "Cài ra màn hình",
|
||||
"Install location": "Vị trí cài đặt",
|
||||
"Show install warning": "Hiển thị cảnh báo cài đặt",
|
||||
"Text scroll speed": "",
|
||||
|
||||
"FileBrowser": "Duyệt tập tin",
|
||||
"%zd files": "%zd tập tin",
|
||||
@@ -234,6 +239,7 @@
|
||||
"Update avaliable: ": "Cập nhậc có sẵn: ",
|
||||
"Download update: ": "Tải cập nhật: ",
|
||||
"Updated to ": "Đã cập nhật ",
|
||||
"Press OK to restart Sphaira": "",
|
||||
"Restart Sphaira?": "Khởi động lại Sphaira?",
|
||||
"Failed to download update": "Cập nhật thất bại",
|
||||
"Restore hbmenu?": "Khôi phục hbmenu?",
|
||||
@@ -246,5 +252,6 @@
|
||||
"Completely remove ": "Đã gỡ thành công ",
|
||||
"Are you sure you want to delete ": "Bạn có muốn xoá ",
|
||||
"Are you sure you wish to cancel?": "Bạn có chắn muốn huỷ không?",
|
||||
"Audio disabled due to suspended game": "",
|
||||
"If this message appears repeatedly, please open an issue.": "Nếu thấy tin nhắn này, hãy báo lỗi."
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,9 @@
|
||||
"Sd": "SD卡",
|
||||
"Image System memory": "主机内存图像",
|
||||
"Image microSD card": "SD卡图像",
|
||||
"Slow": "慢",
|
||||
"Normal": "正常",
|
||||
"Fast": "快",
|
||||
"Yes": "是",
|
||||
"No": "否",
|
||||
"Enabled": "启用",
|
||||
@@ -50,7 +53,7 @@
|
||||
"Alphabetical (Star)": "按字母顺序(星标优先)",
|
||||
"Likes": "点赞量",
|
||||
"ID": "ID",
|
||||
"Decending": "降序",
|
||||
"Descending": "降序",
|
||||
"Descending (down)": "降序",
|
||||
"Desc": "降序",
|
||||
"Ascending": "升序",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Select Theme": "选择主题",
|
||||
"Shuffle": "随机播放",
|
||||
"Music": "音乐",
|
||||
"12 Hour Time": "",
|
||||
"Network": "网络",
|
||||
"Network Options": "网络选项",
|
||||
"Ftp": "FTP",
|
||||
@@ -87,6 +91,7 @@
|
||||
"Portuguese": "Português",
|
||||
"Russian": "Русский",
|
||||
"Swedish": "Svenska",
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Logging": "日志",
|
||||
"Replace hbmenu on exit": "退出后用Sphaira替换hbmenu",
|
||||
"Misc": "拓展",
|
||||
@@ -95,6 +100,7 @@
|
||||
"Install forwarders": "允许安装前端应用",
|
||||
"Install location": "安装位置",
|
||||
"Show install warning": "显示安装警告",
|
||||
"Text scroll speed": "文本滚动速度",
|
||||
|
||||
"FileBrowser": "文件浏览",
|
||||
"%zd files": "%zd 个文件",
|
||||
@@ -233,6 +239,7 @@
|
||||
"Update avaliable: ": "有可用更新!",
|
||||
"Download update: ": "下载更新:",
|
||||
"Updated to ": "更新至 ",
|
||||
"Press OK to restart Sphaira": "",
|
||||
"Restart Sphaira?": "重启 Sphaira?",
|
||||
"Failed to download update": "更新下载失败",
|
||||
"Restore hbmenu?": "恢复 hbmenu?",
|
||||
@@ -245,5 +252,6 @@
|
||||
"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.": "如果此消息反复出现,请提交一个 issue。"
|
||||
"Audio disabled due to suspended game": "由于游戏暂停,音频已禁用",
|
||||
"If this message appears repeatedly, please open an issue.": "若此消息反复出现,请提交问题报告。"
|
||||
}
|
||||
|
||||
BIN
assets/romfs/theme/icons-sp/icon_SP_audio.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
assets/romfs/theme/icons-sp/icon_SP_file.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/romfs/theme/icons-sp/icon_SP_folder.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/romfs/theme/icons-sp/icon_SP_image.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/romfs/theme/icons-sp/icon_SP_nro.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
assets/romfs/theme/icons-sp/icon_SP_video.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
assets/romfs/theme/icons-sp/icon_SP_zip.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
15
assets/romfs/themes/black_theme_sp_icons.ini
Normal file
@@ -0,0 +1,15 @@
|
||||
[meta]
|
||||
name=Black alt-icons-SP
|
||||
author=spkatsi
|
||||
version=1.0.0
|
||||
inherit=romfs:/themes/base_black_theme.ini
|
||||
|
||||
[theme]
|
||||
icon_audio = romfs:/theme/icons-sp/icon_SP_audio.png
|
||||
icon_video = romfs:/theme/icons-sp/icon_SP_video.png
|
||||
icon_image = romfs:/theme/icons-sp/icon_SP_image.png
|
||||
icon_file = romfs:/theme/icons-sp/icon_SP_file.png
|
||||
icon_folder = romfs:/theme/icons-sp/icon_SP_folder.png
|
||||
icon_zip = romfs:/theme/icons-sp/icon_SP_zip.png
|
||||
icon_nro = romfs:/theme/icons-sp/icon_SP_nro.png
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
set(sphaira_VERSION 0.6.0)
|
||||
set(sphaira_VERSION 0.6.3)
|
||||
|
||||
project(sphaira
|
||||
VERSION ${sphaira_VERSION}
|
||||
@@ -81,12 +81,54 @@ target_compile_definitions(sphaira PRIVATE
|
||||
-DAPP_VERSION_HASH="${sphaira_VERSION_HASH}"
|
||||
)
|
||||
|
||||
target_compile_options(sphaira PRIVATE
|
||||
-Wall
|
||||
-Wextra
|
||||
|
||||
# unsure if it's a good idea to enable these by default as
|
||||
# it may cause breakage upon compiler updates.
|
||||
# -Werror
|
||||
# -Wfatal-errors
|
||||
|
||||
# disabled as nx uses s64 for size and offset, however stl uses size_t instead, thus
|
||||
# there being a lot of warnings.
|
||||
-Wno-sign-compare
|
||||
# disabled as many overriden methods don't use the params.
|
||||
-Wno-unused-parameter
|
||||
# pedantic warning, missing fields are set to 0.
|
||||
-Wno-missing-field-initializers
|
||||
# disabled as it warns for strcat 2 paths together, but it will never
|
||||
# overflow due to fs enforcing a max path len anyway.
|
||||
-Wno-format-truncation
|
||||
|
||||
# the below are taken from my gba emulator, they've served me well ;)
|
||||
-Wformat-overflow=2
|
||||
-Wundef
|
||||
-Wmissing-include-dirs
|
||||
-fstrict-aliasing
|
||||
-Wstrict-overflow=2
|
||||
-Walloca
|
||||
-Wduplicated-cond
|
||||
-Wwrite-strings
|
||||
-Wdate-time
|
||||
-Wlogical-op
|
||||
-Wpacked
|
||||
-Wcast-qual
|
||||
-Wcast-align
|
||||
-Wimplicit-fallthrough=5
|
||||
-Wsuggest-final-types
|
||||
-Wuninitialized
|
||||
-fimplicit-constexpr
|
||||
-Wmissing-requires
|
||||
)
|
||||
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_QUIET FALSE)
|
||||
|
||||
FetchContent_Declare(ftpsrv
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/ftpsrv.git
|
||||
GIT_TAG 1.2.1
|
||||
GIT_TAG 1.2.2
|
||||
SOURCE_SUBDIR NONE
|
||||
)
|
||||
|
||||
FetchContent_Declare(libhaze
|
||||
@@ -101,7 +143,7 @@ FetchContent_Declare(libpulsar
|
||||
|
||||
FetchContent_Declare(nanovg
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/nanovg-deko3d.git
|
||||
GIT_TAG 1902b38
|
||||
GIT_TAG 845c9fc
|
||||
)
|
||||
|
||||
FetchContent_Declare(stb
|
||||
@@ -136,8 +178,6 @@ set(NANOVG_NO_GIF ON)
|
||||
set(NANOVG_NO_HDR ON)
|
||||
set(NANOVG_NO_PIC ON)
|
||||
set(NANOVG_NO_PNM ON)
|
||||
set(NANOVG_STBI_STATIC OFF)
|
||||
set(NANOVG_STBTT_STATIC ON)
|
||||
|
||||
set(YYJSON_DISABLE_READER OFF)
|
||||
set(YYJSON_DISABLE_WRITER OFF)
|
||||
@@ -148,7 +188,7 @@ set(YYJSON_DISABLE_UTF8_VALIDATION ON)
|
||||
set(YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS OFF)
|
||||
|
||||
FetchContent_MakeAvailable(
|
||||
# ftpsrv
|
||||
ftpsrv
|
||||
libhaze
|
||||
libpulsar
|
||||
nanovg
|
||||
@@ -157,11 +197,6 @@ FetchContent_MakeAvailable(
|
||||
yyjson
|
||||
)
|
||||
|
||||
FetchContent_GetProperties(ftpsrv)
|
||||
if (NOT ftpsrv_POPULATED)
|
||||
FetchContent_Populate(ftpsrv)
|
||||
endif()
|
||||
|
||||
set(FTPSRV_LIB_BUILD TRUE)
|
||||
set(FTPSRV_LIB_SOCK_UNISTD TRUE)
|
||||
set(FTPSRV_LIB_VFS_CUSTOM ${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs_nx.h)
|
||||
@@ -178,6 +213,7 @@ set(FTPSRV_LIB_CUSTOM_DEFINES
|
||||
USE_VFS_SAVE=$<BOOL:TRUE>
|
||||
USE_VFS_STORAGE=$<BOOL:TRUE>
|
||||
USE_VFS_GC=$<BOOL:${USE_VFS_GC}>
|
||||
USE_VFS_USBHSFS=$<BOOL:FALSE>
|
||||
VFS_NX_BUFFER_IO=$<BOOL:TRUE>
|
||||
)
|
||||
|
||||
@@ -294,7 +330,7 @@ nx_generate_nacp(
|
||||
OUTPUT sphaira.nacp
|
||||
NAME ${CMAKE_PROJECT_NAME}
|
||||
AUTHOR TotalJustice
|
||||
VERSION ${CMAKE_PROJECT_VERSION}
|
||||
VERSION ${sphaira_VERSION}
|
||||
)
|
||||
|
||||
# create nro
|
||||
|
||||
@@ -79,6 +79,7 @@ public:
|
||||
static auto GetInstallPrompt() -> bool;
|
||||
static auto GetThemeShuffleEnable() -> bool;
|
||||
static auto GetThemeMusicEnable() -> bool;
|
||||
static auto Get12HourTimeEnable() -> bool;
|
||||
static auto GetLanguage() -> long;
|
||||
static auto GetTextScrollSpeed() -> long;
|
||||
|
||||
@@ -92,6 +93,7 @@ public:
|
||||
static void SetInstallPrompt(bool enable);
|
||||
static void SetThemeShuffleEnable(bool enable);
|
||||
static void SetThemeMusicEnable(bool enable);
|
||||
static void Set12HourTimeEnable(bool enable);
|
||||
static void SetLanguage(long index);
|
||||
static void SetTextScrollSpeed(long index);
|
||||
|
||||
@@ -119,6 +121,21 @@ public:
|
||||
return type == AppletType_Application || type == AppletType_SystemApplication;
|
||||
}
|
||||
|
||||
static auto IsApplet() -> bool {
|
||||
return !IsApplication();
|
||||
}
|
||||
|
||||
// returns true if launched in applet mode with a title suspended in the background.
|
||||
static auto IsAppletWithSuspendedApp() -> bool {
|
||||
R_UNLESS(IsApplet(), false);
|
||||
R_TRY_RESULT(pmdmntInitialize(), false);
|
||||
ON_SCOPE_EXIT(pmdmntExit());
|
||||
|
||||
u64 pid;
|
||||
return R_SUCCEEDED(pmdmntGetApplicationProcessId(&pid));
|
||||
}
|
||||
|
||||
|
||||
// private:
|
||||
static constexpr inline auto CONFIG_PATH = "/config/sphaira/config.ini";
|
||||
static constexpr inline auto PLAYLOG_PATH = "/config/sphaira/playlog.ini";
|
||||
@@ -162,11 +179,11 @@ public:
|
||||
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::OptionBool m_12hour_time{INI_SECTION, "12hour_time", false};
|
||||
option::OptionLong m_language{INI_SECTION, "language", 0}; // auto
|
||||
// todo: move this into it's own menu
|
||||
option::OptionLong m_text_scroll_speed{"accessibility", "text_scroll_speed", 1}; // normal
|
||||
|
||||
PLSR_BFSAR m_qlaunch_bfsar{};
|
||||
PLSR_PlayerSoundId m_sound_ids[SoundEffect_MAX]{};
|
||||
|
||||
private: // from nanovg decko3d example by adubbz
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <stop_token>
|
||||
#include <switch.h>
|
||||
|
||||
namespace sphaira::curl {
|
||||
@@ -29,6 +30,7 @@ struct ApiResult;
|
||||
using Path = fs::FsPath;
|
||||
using OnComplete = std::function<void(ApiResult& result)>;
|
||||
using OnProgress = std::function<bool(u32 dltotal, u32 dlnow, u32 ultotal, u32 ulnow)>;
|
||||
using StopToken = std::stop_token;
|
||||
|
||||
struct Url {
|
||||
Url() = default;
|
||||
@@ -71,6 +73,7 @@ struct ApiResult {
|
||||
struct DownloadEventData {
|
||||
OnComplete callback;
|
||||
ApiResult result;
|
||||
StopToken stoken;
|
||||
};
|
||||
|
||||
auto Init() -> bool;
|
||||
@@ -84,6 +87,9 @@ auto ToFile(const Api& e) -> ApiResult;
|
||||
auto ToMemoryAsync(const Api& e) -> bool;
|
||||
auto ToFileAsync(const Api& e) -> bool;
|
||||
|
||||
// uses curl to convert string to their %XX
|
||||
auto EscapeString(const std::string& str) -> std::string;
|
||||
|
||||
struct Api {
|
||||
Api() = default;
|
||||
|
||||
@@ -114,6 +120,7 @@ struct Api {
|
||||
auto ToMemory(Ts&&... ts) {
|
||||
static_assert(std::disjunction_v<std::is_same<Url, Ts>...>, "Url must be specified");
|
||||
static_assert(!std::disjunction_v<std::is_same<Path, Ts>...>, "Path must not valid for memory");
|
||||
static_assert(!std::disjunction_v<std::is_same<OnComplete, Ts>...>, "OnComplete must not be specified");
|
||||
Api::set_option(std::forward<Ts>(ts)...);
|
||||
return curl::ToMemory(*this);
|
||||
}
|
||||
@@ -122,6 +129,7 @@ struct Api {
|
||||
auto ToFile(Ts&&... ts) {
|
||||
static_assert(std::disjunction_v<std::is_same<Url, Ts>...>, "Url must be specified");
|
||||
static_assert(std::disjunction_v<std::is_same<Path, Ts>...>, "Path must be specified");
|
||||
static_assert(!std::disjunction_v<std::is_same<OnComplete, Ts>...>, "OnComplete must not be specified");
|
||||
Api::set_option(std::forward<Ts>(ts)...);
|
||||
return curl::ToFile(*this);
|
||||
}
|
||||
@@ -131,6 +139,7 @@ struct Api {
|
||||
static_assert(std::disjunction_v<std::is_same<Url, Ts>...>, "Url must be specified");
|
||||
static_assert(std::disjunction_v<std::is_same<OnComplete, Ts>...>, "OnComplete must be specified");
|
||||
static_assert(!std::disjunction_v<std::is_same<Path, Ts>...>, "Path must not valid for memory");
|
||||
static_assert(std::disjunction_v<std::is_same<StopToken, Ts>...>, "StopToken must be specified");
|
||||
Api::set_option(std::forward<Ts>(ts)...);
|
||||
return curl::ToMemoryAsync(*this);
|
||||
}
|
||||
@@ -140,18 +149,38 @@ struct Api {
|
||||
static_assert(std::disjunction_v<std::is_same<Url, Ts>...>, "Url must be specified");
|
||||
static_assert(std::disjunction_v<std::is_same<Path, Ts>...>, "Path must be specified");
|
||||
static_assert(std::disjunction_v<std::is_same<OnComplete, Ts>...>, "OnComplete must be specified");
|
||||
static_assert(std::disjunction_v<std::is_same<StopToken, Ts>...>, "StopToken must be specified");
|
||||
Api::set_option(std::forward<Ts>(ts)...);
|
||||
return curl::ToFileAsync(*this);
|
||||
}
|
||||
|
||||
Url m_url;
|
||||
Fields m_fields{};
|
||||
Header m_header{};
|
||||
Flags m_flags{};
|
||||
Path m_path{};
|
||||
OnComplete m_on_complete = nullptr;
|
||||
OnProgress m_on_progress = nullptr;
|
||||
Priority m_prio = Priority::High;
|
||||
auto& GetUrl() const {
|
||||
return m_url.m_str;
|
||||
}
|
||||
auto& GetFields() const {
|
||||
return m_fields.m_str;
|
||||
}
|
||||
auto& GetHeader() const {
|
||||
return m_header;
|
||||
}
|
||||
auto& GetFlags() const {
|
||||
return m_flags.m_flags;
|
||||
}
|
||||
auto& GetPath() const {
|
||||
return m_path;
|
||||
}
|
||||
auto& GetOnComplete() const {
|
||||
return m_on_complete;
|
||||
}
|
||||
auto& GetOnProgress() const {
|
||||
return m_on_progress;
|
||||
}
|
||||
auto& GetPriority() const {
|
||||
return m_prio;
|
||||
}
|
||||
auto& GetToken() const {
|
||||
return m_stoken;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetOption(Url&& v) {
|
||||
@@ -178,6 +207,9 @@ private:
|
||||
void SetOption(Priority&& v) {
|
||||
m_prio = v;
|
||||
}
|
||||
void SetOption(StopToken&& v) {
|
||||
m_stoken = v;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_option(T&& t) {
|
||||
@@ -189,6 +221,18 @@ private:
|
||||
set_option(std::forward<T>(t));
|
||||
set_option(std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Url m_url;
|
||||
Fields m_fields{};
|
||||
Header m_header{};
|
||||
Flags m_flags{};
|
||||
Path m_path{};
|
||||
OnComplete m_on_complete{nullptr};
|
||||
OnProgress m_on_progress{nullptr};
|
||||
Priority m_prio{Priority::High};
|
||||
std::stop_source m_stop_source{};
|
||||
StopToken m_stoken{m_stop_source.get_token()};
|
||||
};
|
||||
|
||||
} // namespace sphaira::curl
|
||||
|
||||
@@ -13,10 +13,10 @@ public:
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
|
||||
private:
|
||||
Result m_code;
|
||||
std::string m_message;
|
||||
std::string m_module_str;
|
||||
std::string m_description_str;
|
||||
Result m_code{};
|
||||
std::string m_message{};
|
||||
std::string m_module_str{};
|
||||
std::string m_description_str{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui
|
||||
|
||||
@@ -43,8 +43,8 @@ private:
|
||||
const s64 m_row;
|
||||
const s64 m_page;
|
||||
|
||||
Vec4 m_v;
|
||||
Vec2 m_pad;
|
||||
Vec4 m_v{};
|
||||
Vec2 m_pad{};
|
||||
|
||||
Vec4 m_scrollbar{};
|
||||
|
||||
|
||||
@@ -42,26 +42,26 @@ enum class EntryStatus {
|
||||
};
|
||||
|
||||
struct Entry {
|
||||
std::string category; // todo: lable
|
||||
std::string binary; // optional, only valid for .nro
|
||||
std::string updated; // date of update
|
||||
std::string name;
|
||||
std::string license; // optional
|
||||
std::string title; // same as name but with spaces
|
||||
std::string url; // url of repo (optional?)
|
||||
std::string description;
|
||||
std::string author;
|
||||
std::string changelog; // optional
|
||||
u64 screens; // number of screenshots
|
||||
u64 extracted; // extracted size in KiB
|
||||
std::string version;
|
||||
u64 filesize; // compressed size in KiB
|
||||
std::string details;
|
||||
u64 app_dls;
|
||||
std::string md5; // md5 of the zip
|
||||
std::string category{}; // todo: lable
|
||||
std::string binary{}; // optional, only valid for .nro
|
||||
std::string updated{}; // date of update
|
||||
std::string name{};
|
||||
std::string license{}; // optional
|
||||
std::string title{}; // same as name but with spaces
|
||||
std::string url{}; // url of repo (optional?)
|
||||
std::string description{};
|
||||
std::string author{};
|
||||
std::string changelog{}; // optional
|
||||
u64 screens{}; // number of screenshots
|
||||
u64 extracted{}; // extracted size in KiB
|
||||
std::string version{};
|
||||
u64 filesize{}; // compressed size in KiB
|
||||
std::string details{};
|
||||
u64 app_dls{};
|
||||
std::string md5{}; // md5 of the zip
|
||||
|
||||
LazyImage image;
|
||||
u32 updated_num;
|
||||
LazyImage image{};
|
||||
u32 updated_num{};
|
||||
EntryStatus status{EntryStatus::Get};
|
||||
};
|
||||
|
||||
@@ -99,13 +99,13 @@ private:
|
||||
Menu& m_menu;
|
||||
|
||||
s64 m_index{}; // where i am in the array
|
||||
std::vector<Option> m_options;
|
||||
LazyImage m_banner;
|
||||
std::unique_ptr<List> m_list;
|
||||
std::vector<Option> m_options{};
|
||||
LazyImage m_banner{};
|
||||
std::unique_ptr<List> m_list{};
|
||||
|
||||
std::shared_ptr<ScrollableText> m_details;
|
||||
std::shared_ptr<ScrollableText> m_changelog;
|
||||
std::shared_ptr<ScrollableText> m_detail_changelog;
|
||||
std::shared_ptr<ScrollableText> m_details{};
|
||||
std::shared_ptr<ScrollableText> m_changelog{};
|
||||
std::shared_ptr<ScrollableText> m_detail_changelog{};
|
||||
|
||||
bool m_show_changlog{};
|
||||
};
|
||||
@@ -130,38 +130,10 @@ enum SortType {
|
||||
};
|
||||
|
||||
enum OrderType {
|
||||
OrderType_Decending,
|
||||
OrderType_Descending,
|
||||
OrderType_Ascending,
|
||||
};
|
||||
|
||||
struct FeedbackEntry {
|
||||
u32 id;
|
||||
u64 time;
|
||||
std::string package; // name of package
|
||||
std::string content; // the feedback message that was sent
|
||||
std::string reply; // the reply, "" if no reply yet :)
|
||||
};
|
||||
|
||||
struct FeedbackMenu final : MenuBase {
|
||||
FeedbackMenu(const std::vector<Entry>& package_entries, LazyImage& default_image);
|
||||
~FeedbackMenu();
|
||||
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
void OnFocusGained() override;
|
||||
|
||||
void SetIndex(s64 index);
|
||||
void ScanHomebrew();
|
||||
void Sort();
|
||||
|
||||
private:
|
||||
const std::vector<Entry>& m_package_entries;
|
||||
LazyImage& m_default_image;
|
||||
std::vector<FeedbackEntry> m_entries;
|
||||
s64 m_index{}; // where i am in the array
|
||||
ImageDownloadState m_repo_download_state{ImageDownloadState::None};
|
||||
};
|
||||
|
||||
struct Menu final : MenuBase {
|
||||
Menu(const std::vector<NroEntry>& nro_entries);
|
||||
~Menu();
|
||||
@@ -191,27 +163,27 @@ struct Menu final : MenuBase {
|
||||
|
||||
private:
|
||||
const std::vector<NroEntry>& m_nro_entries;
|
||||
std::vector<Entry> m_entries;
|
||||
std::vector<EntryMini> m_entries_index[Filter_MAX];
|
||||
std::vector<EntryMini> m_entries_index_author;
|
||||
std::vector<EntryMini> m_entries_index_search;
|
||||
std::span<EntryMini> m_entries_current;
|
||||
std::vector<Entry> m_entries{};
|
||||
std::vector<EntryMini> m_entries_index[Filter_MAX]{};
|
||||
std::vector<EntryMini> m_entries_index_author{};
|
||||
std::vector<EntryMini> m_entries_index_search{};
|
||||
std::span<EntryMini> m_entries_current{};
|
||||
|
||||
Filter m_filter{Filter::Filter_All};
|
||||
SortType m_sort{SortType::SortType_Updated};
|
||||
OrderType m_order{OrderType::OrderType_Decending};
|
||||
OrderType m_order{OrderType::OrderType_Descending};
|
||||
|
||||
s64 m_index{}; // where i am in the array
|
||||
LazyImage m_default_image;
|
||||
LazyImage m_update;
|
||||
LazyImage m_get;
|
||||
LazyImage m_local;
|
||||
LazyImage m_installed;
|
||||
LazyImage m_default_image{};
|
||||
LazyImage m_update{};
|
||||
LazyImage m_get{};
|
||||
LazyImage m_local{};
|
||||
LazyImage m_installed{};
|
||||
ImageDownloadState m_repo_download_state{ImageDownloadState::None};
|
||||
std::unique_ptr<List> m_list;
|
||||
std::unique_ptr<List> m_list{};
|
||||
|
||||
std::string m_search_term;
|
||||
std::string m_author_term;
|
||||
std::string m_search_term{};
|
||||
std::string m_author_term{};
|
||||
s64 m_entry_search_jump_back{};
|
||||
s64 m_entry_author_jump_back{};
|
||||
bool m_is_search{};
|
||||
|
||||
@@ -16,12 +16,12 @@ struct Menu final : MenuBase {
|
||||
|
||||
private:
|
||||
const fs::FsPath m_path;
|
||||
fs::FsNativeSd m_fs;
|
||||
FsFile m_file;
|
||||
fs::FsNativeSd m_fs{};
|
||||
FsFile m_file{};
|
||||
s64 m_file_size{};
|
||||
s64 m_file_offset{};
|
||||
|
||||
std::unique_ptr<ScrollableText> m_scroll_text;
|
||||
std::unique_ptr<ScrollableText> m_scroll_text{};
|
||||
|
||||
s64 m_start{};
|
||||
s64 m_index{}; // where i am in the array
|
||||
|
||||
@@ -29,7 +29,7 @@ enum SortType {
|
||||
};
|
||||
|
||||
enum OrderType {
|
||||
OrderType_Decending,
|
||||
OrderType_Descending,
|
||||
OrderType_Ascending,
|
||||
};
|
||||
|
||||
@@ -86,23 +86,23 @@ struct FileEntry : FsDirectoryEntry {
|
||||
|
||||
struct FileAssocEntry {
|
||||
fs::FsPath path{}; // ini name
|
||||
std::string name; // ini name
|
||||
std::vector<std::string> ext; // list of ext
|
||||
std::vector<std::string> database; // list of systems
|
||||
std::string name{}; // ini name
|
||||
std::vector<std::string> ext{}; // list of ext
|
||||
std::vector<std::string> database{}; // list of systems
|
||||
};
|
||||
|
||||
struct LastFile {
|
||||
fs::FsPath name;
|
||||
s64 index;
|
||||
float offset;
|
||||
s64 entries_count;
|
||||
fs::FsPath name{};
|
||||
s64 index{};
|
||||
float offset{};
|
||||
s64 entries_count{};
|
||||
};
|
||||
|
||||
struct FsDirCollection {
|
||||
fs::FsPath path;
|
||||
fs::FsPath parent_name;
|
||||
std::vector<FsDirectoryEntry> files;
|
||||
std::vector<FsDirectoryEntry> dirs;
|
||||
fs::FsPath path{};
|
||||
fs::FsPath parent_name{};
|
||||
std::vector<FsDirectoryEntry> files{};
|
||||
std::vector<FsDirectoryEntry> dirs{};
|
||||
};
|
||||
|
||||
using FsDirCollections = std::vector<FsDirCollection>;
|
||||
@@ -231,38 +231,38 @@ private:
|
||||
static constexpr inline const char* INI_SECTION = "filebrowser";
|
||||
|
||||
const std::vector<NroEntry>& m_nro_entries;
|
||||
std::unique_ptr<fs::FsNative> m_fs;
|
||||
FsType m_fs_type;
|
||||
fs::FsPath m_path;
|
||||
std::vector<FileEntry> m_entries;
|
||||
std::vector<u32> m_entries_index; // files not including hidden
|
||||
std::vector<u32> m_entries_index_hidden; // includes hidden files
|
||||
std::vector<u32> m_entries_index_search; // files found via search
|
||||
std::span<u32> m_entries_current;
|
||||
std::unique_ptr<fs::FsNative> m_fs{};
|
||||
FsType m_fs_type{};
|
||||
fs::FsPath m_path{};
|
||||
std::vector<FileEntry> m_entries{};
|
||||
std::vector<u32> m_entries_index{}; // files not including hidden
|
||||
std::vector<u32> m_entries_index_hidden{}; // includes hidden files
|
||||
std::vector<u32> m_entries_index_search{}; // files found via search
|
||||
std::span<u32> m_entries_current{};
|
||||
|
||||
std::unique_ptr<List> m_list;
|
||||
std::optional<fs::FsPath> m_daybreak_path;
|
||||
std::unique_ptr<List> m_list{};
|
||||
std::optional<fs::FsPath> m_daybreak_path{};
|
||||
|
||||
// search options
|
||||
// show files [X]
|
||||
// show folders [X]
|
||||
// recursive (slow) [ ]
|
||||
|
||||
std::vector<FileAssocEntry> m_assoc_entries;
|
||||
std::vector<FileEntry> m_selected_files;
|
||||
std::vector<FileAssocEntry> m_assoc_entries{};
|
||||
std::vector<FileEntry> m_selected_files{};
|
||||
|
||||
// this keeps track of the highlighted file before opening a folder
|
||||
// if the user presses B to go back to the previous dir
|
||||
// this vector is popped, then, that entry is checked if it still exists
|
||||
// if it does, the index becomes that file.
|
||||
std::vector<LastFile> m_previous_highlighted_file;
|
||||
fs::FsPath m_selected_path;
|
||||
std::vector<LastFile> m_previous_highlighted_file{};
|
||||
fs::FsPath m_selected_path{};
|
||||
s64 m_index{};
|
||||
s64 m_selected_count{};
|
||||
SelectedType m_selected_type{SelectedType::None};
|
||||
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_Alphabetical};
|
||||
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Decending};
|
||||
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending};
|
||||
option::OptionBool m_show_hidden{INI_SECTION, "show_hidden", false};
|
||||
option::OptionBool m_folders_first{INI_SECTION, "folders_first", true};
|
||||
option::OptionBool m_hidden_last{INI_SECTION, "hidden_last", false};
|
||||
|
||||
@@ -10,35 +10,35 @@
|
||||
namespace sphaira::ui::menu::gh {
|
||||
|
||||
struct AssetEntry {
|
||||
std::string name;
|
||||
std::string path;
|
||||
std::string pre_install_message;
|
||||
std::string post_install_message;
|
||||
std::string name{};
|
||||
std::string path{};
|
||||
std::string pre_install_message{};
|
||||
std::string post_install_message{};
|
||||
};
|
||||
|
||||
struct Entry {
|
||||
fs::FsPath json_path;
|
||||
std::string url;
|
||||
std::string owner;
|
||||
std::string repo;
|
||||
std::string tag;
|
||||
std::string pre_install_message;
|
||||
std::string post_install_message;
|
||||
std::vector<AssetEntry> assets;
|
||||
fs::FsPath json_path{};
|
||||
std::string url{};
|
||||
std::string owner{};
|
||||
std::string repo{};
|
||||
std::string tag{};
|
||||
std::string pre_install_message{};
|
||||
std::string post_install_message{};
|
||||
std::vector<AssetEntry> assets{};
|
||||
};
|
||||
|
||||
struct GhApiAsset {
|
||||
std::string name;
|
||||
std::string content_type;
|
||||
u64 size;
|
||||
u64 download_count;
|
||||
std::string browser_download_url;
|
||||
std::string name{};
|
||||
std::string content_type{};
|
||||
u64 size{};
|
||||
u64 download_count{};
|
||||
std::string browser_download_url{};
|
||||
};
|
||||
|
||||
struct GhApiEntry {
|
||||
std::string tag_name;
|
||||
std::string name;
|
||||
std::vector<GhApiAsset> assets;
|
||||
std::string tag_name{};
|
||||
std::string name{};
|
||||
std::vector<GhApiAsset> assets{};
|
||||
};
|
||||
|
||||
struct Menu final : MenuBase {
|
||||
@@ -66,9 +66,9 @@ private:
|
||||
void UpdateSubheading();
|
||||
|
||||
private:
|
||||
std::vector<Entry> m_entries;
|
||||
std::vector<Entry> m_entries{};
|
||||
s64 m_index{};
|
||||
std::unique_ptr<List> m_list;
|
||||
std::unique_ptr<List> m_list{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::gh
|
||||
|
||||
@@ -18,7 +18,7 @@ enum SortType {
|
||||
};
|
||||
|
||||
enum OrderType {
|
||||
OrderType_Decending,
|
||||
OrderType_Descending,
|
||||
OrderType_Ascending,
|
||||
};
|
||||
|
||||
@@ -50,12 +50,12 @@ struct Menu final : MenuBase {
|
||||
private:
|
||||
static constexpr inline const char* INI_SECTION = "homebrew";
|
||||
|
||||
std::vector<NroEntry> m_entries;
|
||||
std::vector<NroEntry> m_entries{};
|
||||
s64 m_index{}; // where i am in the array
|
||||
std::unique_ptr<List> m_list;
|
||||
std::unique_ptr<List> m_list{};
|
||||
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_AlphabeticalStar};
|
||||
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Decending};
|
||||
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending};
|
||||
option::OptionBool m_hide_sphaira{INI_SECTION, "hide_sphaira", false};
|
||||
};
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ private:
|
||||
void UpdateVars();
|
||||
|
||||
private:
|
||||
std::string m_title;
|
||||
std::string m_title_sub_heading;
|
||||
std::string m_sub_heading;
|
||||
std::string m_title{};
|
||||
std::string m_title_sub_heading{};
|
||||
std::string m_sub_heading{};
|
||||
|
||||
struct tm m_tm{};
|
||||
TimeStamp m_poll_timestamp{};
|
||||
|
||||
@@ -42,79 +42,50 @@ enum class PageLoadState {
|
||||
Error,
|
||||
};
|
||||
|
||||
// all commented out entries are those that we don't query for.
|
||||
// this saves time not only processing the json, but also the download
|
||||
// of said json.
|
||||
// by reducing the fields to only what we need, the size is 4-5x smaller.
|
||||
|
||||
struct Creator {
|
||||
std::string id;
|
||||
std::string display_name;
|
||||
std::string id{};
|
||||
std::string display_name{};
|
||||
};
|
||||
|
||||
struct Details {
|
||||
std::string name;
|
||||
// std::string description;
|
||||
std::string name{};
|
||||
};
|
||||
|
||||
struct Preview {
|
||||
// std::string original;
|
||||
std::string thumb;
|
||||
LazyImage lazy_image;
|
||||
std::string thumb{};
|
||||
LazyImage lazy_image{};
|
||||
};
|
||||
|
||||
struct DownloadPack {
|
||||
std::string filename;
|
||||
std::string url;
|
||||
std::string mimetype;
|
||||
std::string filename{};
|
||||
std::string url{};
|
||||
std::string mimetype{};
|
||||
};
|
||||
|
||||
using DownloadTheme = DownloadPack;
|
||||
|
||||
struct ThemeEntry {
|
||||
std::string id;
|
||||
// Creator creator;
|
||||
// Details details;
|
||||
// std::string last_updated;
|
||||
// u64 dl_count;
|
||||
// u64 like_count;
|
||||
// std::vector<std::string> categories;
|
||||
// std::string target;
|
||||
Preview preview;
|
||||
std::string id{};
|
||||
Preview preview{};
|
||||
};
|
||||
|
||||
// struct Pack {
|
||||
// std::string id;
|
||||
// Creator creator;
|
||||
// Details details;
|
||||
// std::string last_updated;
|
||||
// std::vector<std::string> categories;
|
||||
// u64 dl_count;
|
||||
// u64 like_count;
|
||||
// std::vector<ThemeEntry> themes;
|
||||
// };
|
||||
|
||||
struct PackListEntry {
|
||||
std::string id;
|
||||
Creator creator;
|
||||
Details details;
|
||||
// std::string last_updated;
|
||||
// std::vector<std::string> categories;
|
||||
// u64 dl_count;
|
||||
// u64 like_count;
|
||||
std::vector<ThemeEntry> themes;
|
||||
std::string id{};
|
||||
Creator creator{};
|
||||
Details details{};
|
||||
std::vector<ThemeEntry> themes{};
|
||||
};
|
||||
|
||||
struct Pagination {
|
||||
u64 page;
|
||||
u64 limit;
|
||||
u64 page_count;
|
||||
u64 item_count;
|
||||
u64 page{};
|
||||
u64 limit{};
|
||||
u64 page_count{};
|
||||
u64 item_count{};
|
||||
};
|
||||
|
||||
struct PackList {
|
||||
std::vector<PackListEntry> packList;
|
||||
Pagination pagination;
|
||||
std::vector<PackListEntry> packList{};
|
||||
Pagination pagination{};
|
||||
};
|
||||
|
||||
struct Config {
|
||||
@@ -123,10 +94,10 @@ struct Config {
|
||||
u32 sort_index{};
|
||||
u32 order_index{};
|
||||
// search query, if empty, its not used
|
||||
std::string query;
|
||||
std::string query{};
|
||||
// this is actually an array of creator ids, but we don't support that feature
|
||||
// if empty, its not used
|
||||
std::string creator;
|
||||
std::string creator{};
|
||||
// defaults
|
||||
u32 page{1};
|
||||
u32 limit{18};
|
||||
@@ -152,7 +123,7 @@ struct Config {
|
||||
struct Menu; // fwd
|
||||
|
||||
struct PageEntry {
|
||||
std::vector<PackListEntry> m_packList;
|
||||
std::vector<PackListEntry> m_packList{};
|
||||
Pagination m_pagination{};
|
||||
PageLoadState m_ready{PageLoadState::None};
|
||||
};
|
||||
@@ -172,9 +143,6 @@ struct Menu final : MenuBase {
|
||||
}
|
||||
}
|
||||
|
||||
// void SetSearch(const std::string& term);
|
||||
// void SetAuthor();
|
||||
|
||||
void InvalidateAllPages();
|
||||
void PackListDownload();
|
||||
void OnPackListDownload();
|
||||
@@ -183,14 +151,14 @@ private:
|
||||
static constexpr inline const char* INI_SECTION = "themezer";
|
||||
static constexpr inline u32 MAX_ON_PAGE = 16; // same as website
|
||||
|
||||
std::vector<PageEntry> m_pages;
|
||||
std::vector<PageEntry> m_pages{};
|
||||
s64 m_page_index{};
|
||||
s64 m_page_index_max{1};
|
||||
|
||||
std::string m_search{};
|
||||
|
||||
s64 m_index{}; // where i am in the array
|
||||
std::unique_ptr<List> m_list;
|
||||
std::unique_ptr<List> m_list{};
|
||||
|
||||
// options
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", 0};
|
||||
|
||||
@@ -22,9 +22,9 @@ private:
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
|
||||
private:
|
||||
std::string m_text;
|
||||
std::string m_text{};
|
||||
std::size_t m_count{180}; // count down to zero
|
||||
Side m_side;
|
||||
Side m_side{};
|
||||
bool m_bounds_measured{};
|
||||
};
|
||||
|
||||
@@ -47,8 +47,8 @@ private:
|
||||
void Draw(NVGcontext* vg, Theme* theme, Entries& entries);
|
||||
|
||||
private:
|
||||
Entries m_entries_left;
|
||||
Entries m_entries_right;
|
||||
Entries m_entries_left{};
|
||||
Entries m_entries_right{};
|
||||
Mutex m_mutex{};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.hpp"
|
||||
#include <stop_token>
|
||||
|
||||
namespace sphaira::ui {
|
||||
|
||||
class Object {
|
||||
public:
|
||||
Object() = default;
|
||||
virtual ~Object() = default;
|
||||
virtual ~Object() {
|
||||
m_stop_source.request_stop();
|
||||
}
|
||||
|
||||
virtual auto Draw(NVGcontext* vg, Theme* theme) -> void = 0;
|
||||
|
||||
@@ -71,8 +74,14 @@ public:
|
||||
m_hidden = value;
|
||||
}
|
||||
|
||||
auto GetToken() const {
|
||||
return m_stop_source.get_token();
|
||||
}
|
||||
|
||||
protected:
|
||||
Vec4 m_pos{};
|
||||
// used for lifetime management across threads.
|
||||
std::stop_source m_stop_source{};
|
||||
bool m_hidden{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
private:
|
||||
|
||||
private:
|
||||
std::string m_text;
|
||||
std::string m_text{};
|
||||
Vec2 m_text_pos{};
|
||||
bool m_selected{false};
|
||||
};
|
||||
@@ -48,13 +48,13 @@ private:
|
||||
void SetIndex(s64 index);
|
||||
|
||||
private:
|
||||
std::string m_message;
|
||||
Callback m_callback;
|
||||
std::string m_message{};
|
||||
Callback m_callback{};
|
||||
|
||||
Vec4 m_spacer_line{};
|
||||
|
||||
s64 m_index{};
|
||||
std::vector<OptionBoxEntry> m_entries;
|
||||
std::vector<OptionBoxEntry> m_entries{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui
|
||||
|
||||
@@ -32,12 +32,12 @@ private:
|
||||
static constexpr float m_text_xoffset{15.f};
|
||||
static constexpr float m_line_width{1220.f};
|
||||
|
||||
std::string m_title;
|
||||
Items m_items;
|
||||
Callback m_callback;
|
||||
s64 m_index; // index in list array
|
||||
std::string m_title{};
|
||||
Items m_items{};
|
||||
Callback m_callback{};
|
||||
s64 m_index{}; // index in list array
|
||||
|
||||
std::unique_ptr<List> m_list;
|
||||
std::unique_ptr<List> m_list{};
|
||||
|
||||
float m_yoff{};
|
||||
float m_line_top{};
|
||||
|
||||
@@ -42,9 +42,9 @@ struct ProgressBox final : Widget {
|
||||
|
||||
public:
|
||||
struct ThreadData {
|
||||
ProgressBox* pbox;
|
||||
ProgressBoxCallback callback;
|
||||
bool result;
|
||||
ProgressBox* pbox{};
|
||||
ProgressBoxCallback callback{};
|
||||
bool result{};
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -57,7 +57,6 @@ private:
|
||||
std::string m_transfer{};
|
||||
s64 m_size{};
|
||||
s64 m_offset{};
|
||||
bool m_exit_requested{};
|
||||
};
|
||||
|
||||
// this is a helper function that does many things.
|
||||
|
||||
@@ -14,15 +14,15 @@ struct ScrollableText final : Widget {
|
||||
// float m_y_off = m_y_off_base;
|
||||
// static constexpr float m_clip_y = 250.0F;
|
||||
|
||||
static constexpr inline float m_step = 30;
|
||||
const float m_font_size;
|
||||
const float m_y_off_base;
|
||||
float m_y_off;
|
||||
const float m_clip_y;
|
||||
const float m_end_w;
|
||||
static constexpr float m_step = 30;
|
||||
|
||||
int m_index = 0;
|
||||
const float m_font_size;
|
||||
float m_bounds[4];
|
||||
float m_y_off{};
|
||||
int m_index{};
|
||||
float m_bounds[4]{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui
|
||||
|
||||
@@ -323,37 +323,36 @@ inline ActionType operator|(ActionType a, ActionType b) {
|
||||
}
|
||||
|
||||
struct Action final {
|
||||
using CallbackEmpty = std::function<void()>;
|
||||
using CallbackWithBool = std::function<void(bool)>;
|
||||
using Callback = std::variant<
|
||||
std::function<void()>,
|
||||
std::function<void(bool)>
|
||||
CallbackEmpty,
|
||||
CallbackWithBool
|
||||
>;
|
||||
|
||||
Action(Callback cb) : m_type{ActionType::DOWN}, m_hint{""}, m_callback{cb}, m_hidden{true} {}
|
||||
Action(std::string hint, Callback cb) : m_type{ActionType::DOWN}, m_hint{hint}, m_callback{cb} {}
|
||||
Action(u8 type, Callback cb) : m_type{type}, m_hint{""}, m_callback{cb}, m_hidden{true} {}
|
||||
Action(u8 type, std::string hint, Callback cb) : m_type{type}, m_hint{hint}, m_callback{cb} {}
|
||||
Action(Callback cb) : Action{ActionType::DOWN, "", cb} {}
|
||||
Action(std::string hint, Callback cb) : Action{ActionType::DOWN, hint, cb} {}
|
||||
Action(u8 type, Callback cb) : Action{type, "", cb} {}
|
||||
Action(u8 type, std::string hint, Callback cb) : m_type{type}, m_callback{cb}, m_hint{hint} {}
|
||||
|
||||
auto IsHidden() const noexcept { return m_hidden; }
|
||||
auto IsHidden() const noexcept { return m_hint.empty(); }
|
||||
|
||||
auto Invoke(bool down) const {
|
||||
// todo: make this a visit
|
||||
switch (m_callback.index()) {
|
||||
case 0:
|
||||
std::get<0>(m_callback)();
|
||||
break;
|
||||
case 1:
|
||||
std::get<1>(m_callback)(down);
|
||||
break;
|
||||
}
|
||||
// std::visit([down, this](auto& cb){
|
||||
// cb(down);
|
||||
// }), m_callback;
|
||||
std::visit([down](auto&& arg){
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr(std::is_same_v<T, CallbackEmpty>) {
|
||||
arg();
|
||||
} else if constexpr(std::is_same_v<T, CallbackWithBool>) {
|
||||
arg(down);
|
||||
} else {
|
||||
static_assert(false, "non-exhaustive visitor!");
|
||||
}
|
||||
}, m_callback);
|
||||
}
|
||||
|
||||
u8 m_type;
|
||||
std::string m_hint; // todo: make optional
|
||||
Callback m_callback;
|
||||
bool m_hidden{false}; // replace this optional text
|
||||
u8 m_type{};
|
||||
Callback m_callback{};
|
||||
std::string m_hint{};
|
||||
};
|
||||
|
||||
struct Controller {
|
||||
|
||||
@@ -78,7 +78,7 @@ struct Widget : public Object {
|
||||
|
||||
auto GetUiButtons() const -> uiButtons;
|
||||
|
||||
Actions m_actions;
|
||||
Actions m_actions{};
|
||||
Vec2 m_button_pos{1220, 675};
|
||||
bool m_focus{false};
|
||||
bool m_pop{false};
|
||||
|
||||
@@ -45,6 +45,11 @@ struct ThemeIdPair {
|
||||
ElementType type{ElementType::None};
|
||||
};
|
||||
|
||||
struct FrameBufferSize {
|
||||
Vec2 size;
|
||||
Vec2 scale;
|
||||
};
|
||||
|
||||
constexpr ThemeIdPair THEME_ENTRIES[] = {
|
||||
{ "background", ThemeEntryID_BACKGROUND },
|
||||
{ "grid", ThemeEntryID_GRID },
|
||||
@@ -223,6 +228,26 @@ void appplet_hook_calback(AppletHookType type, void *param) {
|
||||
}
|
||||
}
|
||||
|
||||
auto GetFrameBufferSize() -> FrameBufferSize {
|
||||
FrameBufferSize fb{};
|
||||
|
||||
switch (appletGetOperationMode()) {
|
||||
case AppletOperationMode_Handheld:
|
||||
fb.size.x = 1280;
|
||||
fb.size.y = 720;
|
||||
break;
|
||||
|
||||
case AppletOperationMode_Console:
|
||||
fb.size.x = 1920;
|
||||
fb.size.y = 1080;
|
||||
break;
|
||||
}
|
||||
|
||||
fb.scale.x = fb.size.x / SCREEN_WIDTH;
|
||||
fb.scale.y = fb.size.y / SCREEN_HEIGHT;
|
||||
return fb;
|
||||
}
|
||||
|
||||
// this will try to decompress the icon and then re-convert it to jpg
|
||||
// in order to strip exif data.
|
||||
// this doesn't take long at all, but it's very overkill.
|
||||
@@ -327,7 +352,7 @@ void LoadThemeInternal(ThemeMeta meta, ThemeData& theme_data, int inherit_level
|
||||
if (!ini_browse(cb, &theme_data, meta.ini_path)) {
|
||||
log_write("failed to open ini: %s\n", meta.ini_path.s);
|
||||
} else {
|
||||
log_write("opened ini: %s\n", meta.ini_path);
|
||||
log_write("opened ini: %s\n", meta.ini_path.s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -421,31 +446,21 @@ void App::Loop() {
|
||||
}
|
||||
} else if constexpr(std::is_same_v<T, curl::DownloadEventData>) {
|
||||
log_write("[DownloadEventData] got event\n");
|
||||
arg.callback(arg.result);
|
||||
if (arg.callback && !arg.stoken.stop_requested()) {
|
||||
arg.callback(arg.result);
|
||||
}
|
||||
} else {
|
||||
static_assert(false, "non-exhaustive visitor!");
|
||||
}
|
||||
}, event.value());
|
||||
}
|
||||
|
||||
u32 w{},h{};
|
||||
switch (appletGetOperationMode()) {
|
||||
case AppletOperationMode_Handheld:
|
||||
w = 1280;
|
||||
h = 720;
|
||||
break;
|
||||
|
||||
case AppletOperationMode_Console:
|
||||
w = 1920;
|
||||
h = 1080;
|
||||
break;
|
||||
}
|
||||
|
||||
if (w != s_width || h != s_height) {
|
||||
s_width = w;
|
||||
s_height = h;
|
||||
m_scale.x = (float)s_width / SCREEN_WIDTH;
|
||||
m_scale.y = (float)s_height / SCREEN_HEIGHT;
|
||||
const auto fb = GetFrameBufferSize();
|
||||
if (fb.size.x != s_width || fb.size.y != s_height) {
|
||||
s_width = fb.size.x;
|
||||
s_height = fb.size.y;
|
||||
m_scale = fb.scale;
|
||||
this->destroyFramebufferResources();
|
||||
this->createFramebufferResources();
|
||||
renderer->UpdateViewSize(s_width, s_height);
|
||||
}
|
||||
@@ -589,6 +604,10 @@ auto App::GetTextScrollSpeed() -> long {
|
||||
return g_app->m_text_scroll_speed.Get();
|
||||
}
|
||||
|
||||
auto App::Get12HourTimeEnable() -> bool {
|
||||
return g_app->m_12hour_time.Get();
|
||||
}
|
||||
|
||||
void App::SetNxlinkEnable(bool enable) {
|
||||
if (App::GetNxlinkEnable() != enable) {
|
||||
g_app->m_nxlink_enabled.Set(enable);
|
||||
@@ -616,13 +635,11 @@ void App::SetReplaceHbmenuEnable(bool enable) {
|
||||
g_app->m_replace_hbmenu.Set(enable);
|
||||
if (!enable) {
|
||||
// check we have already replaced hbmenu with sphaira
|
||||
NacpStruct hbmenu_nacp;
|
||||
if (R_FAILED(nro_get_nacp("/hbmenu.nro", hbmenu_nacp))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::strcmp(hbmenu_nacp.lang[0].name, "sphaira")) {
|
||||
return;
|
||||
NacpStruct hbmenu_nacp{};
|
||||
if (R_SUCCEEDED(nro_get_nacp("/hbmenu.nro", hbmenu_nacp))) {
|
||||
if (std::strcmp(hbmenu_nacp.lang[0].name, "sphaira")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ask user if they want to restore hbmenu
|
||||
@@ -664,7 +681,7 @@ void App::SetReplaceHbmenuEnable(bool enable) {
|
||||
if (R_SUCCEEDED(rc) && !std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
|
||||
if (std::strcmp(sphaira_nacp.display_version, hbmenu_nacp.display_version) < 0) {
|
||||
if (R_FAILED(rc = fs.copy_entire_file(sphaira_path, "/hbmenu.nro"))) {
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", sphaira_path, rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", sphaira_path.s, rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
} else {
|
||||
log_write("success with updating hbmenu!\n");
|
||||
}
|
||||
@@ -733,6 +750,10 @@ void App::SetThemeMusicEnable(bool enable) {
|
||||
PlaySoundEffect(SoundEffect::SoundEffect_Music);
|
||||
}
|
||||
|
||||
void App::Set12HourTimeEnable(bool enable) {
|
||||
g_app->m_12hour_time.Set(enable);
|
||||
}
|
||||
|
||||
void App::SetMtpEnable(bool enable) {
|
||||
if (App::GetMtpEnable() != enable) {
|
||||
g_app->m_mtp_enabled.Set(enable);
|
||||
@@ -870,23 +891,26 @@ void App::Poll() {
|
||||
}
|
||||
|
||||
auto gesture = gestures[i];
|
||||
if (gesture_count && gesture.type == HidGestureType_Swipe) {
|
||||
log_write("[SWIPE] got gesture type: %d direction: %d sampling_number: %d context_number: %d\n", gesture.type, gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
if (gesture_count && gesture.type == HidGestureType_Touch) {
|
||||
log_write("[TOUCH] got gesture attr: %u direction: %u sampling_number: %zu context_number: %zu\n", gesture.attributes, gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
}
|
||||
else if (gesture_count && gesture.type == HidGestureType_Swipe) {
|
||||
log_write("[SWIPE] got gesture direction: %u sampling_number: %zu context_number: %zu\n", gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
}
|
||||
else if (gesture_count && gesture.type == HidGestureType_Tap) {
|
||||
log_write("[TAP] got gesture type: %d direction: %d sampling_number: %d context_number: %d\n", gesture.type, gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
log_write("[TAP] got gesture direction: %u sampling_number: %zu context_number: %zu\n", gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
}
|
||||
else if (gesture_count && gesture.type == HidGestureType_Press) {
|
||||
log_write("[PRESS] got gesture type: %d direction: %d sampling_number: %d context_number: %d\n", gesture.type, gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
log_write("[PRESS] got gesture direction: %u sampling_number: %zu context_number: %zu\n", gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
}
|
||||
else if (gesture_count && gesture.type == HidGestureType_Cancel) {
|
||||
log_write("[CANCEL] got gesture type: %d direction: %d sampling_number: %d context_number: %d\n", gesture.type, gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
log_write("[CANCEL] got gesture direction: %u sampling_number: %zu context_number: %zu\n", gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
}
|
||||
else if (gesture_count && gesture.type == HidGestureType_Complete) {
|
||||
log_write("[COMPLETE] got gesture type: %d direction: %d sampling_number: %d context_number: %d\n", gesture.type, gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
log_write("[COMPLETE] got gesture direction: %u sampling_number: %zu context_number: %zu\n", gesture.direction, gesture.sampling_number, gesture.context_number);
|
||||
}
|
||||
else if (gesture_count && gesture.type == HidGestureType_Pan) {
|
||||
log_write("[PAN] got gesture sampling_number: %d context_number: %d x: %d y: %d dx: %d dy: %d vx: %.2f vy: %.2f count: %d\n", gesture.sampling_number, gesture.context_number, gesture.x, gesture.y, gesture.delta_x, gesture.delta_y, gesture.velocity_x, gesture.velocity_y, gesture.point_count);
|
||||
log_write("[PAN] got gesture direction: %u sampling_number: %zu context_number: %zu x: %d y: %d dx: %d dy: %d vx: %.2f vy: %.2f count: %d\n", gesture.direction, gesture.sampling_number, gesture.context_number, gesture.x, gesture.y, gesture.delta_x, gesture.delta_y, gesture.velocity_x, gesture.velocity_y, gesture.point_count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1205,6 +1229,12 @@ App::App(const char* argv0) {
|
||||
|
||||
curl::Init();
|
||||
|
||||
// get current size of the framebuffer
|
||||
const auto fb = GetFrameBufferSize();
|
||||
s_width = fb.size.x;
|
||||
s_height = fb.size.y;
|
||||
m_scale = fb.scale;
|
||||
|
||||
// Create the deko3d device
|
||||
this->device = dk::DeviceMaker{}
|
||||
.setCbDebug(deko3d_error_cb)
|
||||
@@ -1228,7 +1258,7 @@ App::App(const char* argv0) {
|
||||
// Create the framebuffer resources
|
||||
this->createFramebufferResources();
|
||||
|
||||
this->renderer.emplace(SCREEN_WIDTH, SCREEN_HEIGHT, this->device, this->queue, *this->pool_images, *this->pool_code, *this->pool_data);
|
||||
this->renderer.emplace(s_width, s_height, this->device, this->queue, *this->pool_images, *this->pool_code, *this->pool_data);
|
||||
this->vg = nvgCreateDk(&*this->renderer, NVG_ANTIALIAS | NVG_STENCIL_STROKES);
|
||||
|
||||
i18n::init(GetLanguage());
|
||||
@@ -1260,22 +1290,31 @@ App::App(const char* argv0) {
|
||||
}
|
||||
}
|
||||
|
||||
// disable audio in applet mode with a suspended application due to audren fatal.
|
||||
// see: https://github.com/ITotalJustice/sphaira/issues/92
|
||||
if (IsAppletWithSuspendedApp()) {
|
||||
App::Notify("Audio disabled due to suspended game"_i18n);
|
||||
} else {
|
||||
plsrPlayerInit();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(romfsMountDataStorageFromProgram(0x0100000000001000, "qlaunch"))) {
|
||||
ON_SCOPE_EXIT(romfsUnmount("qlaunch"));
|
||||
plsrPlayerInit();
|
||||
plsrBFSAROpen("qlaunch:/sound/qlaunch.bfsar", &m_qlaunch_bfsar);
|
||||
ON_SCOPE_EXIT(plsrBFSARClose(&m_qlaunch_bfsar));
|
||||
PLSR_BFSAR qlaunch_bfsar;
|
||||
if (R_SUCCEEDED(plsrBFSAROpen("qlaunch:/sound/qlaunch.bfsar", &qlaunch_bfsar))) {
|
||||
ON_SCOPE_EXIT(plsrBFSARClose(&qlaunch_bfsar));
|
||||
|
||||
plsrPlayerLoadSoundByName(&m_qlaunch_bfsar, "SeGameIconFocus", &m_sound_ids[SoundEffect_Focus]);
|
||||
plsrPlayerLoadSoundByName(&m_qlaunch_bfsar, "SeGameIconScroll", &m_sound_ids[SoundEffect_Scroll]);
|
||||
plsrPlayerLoadSoundByName(&m_qlaunch_bfsar, "SeGameIconLimit", &m_sound_ids[SoundEffect_Limit]);
|
||||
plsrPlayerLoadSoundByName(&m_qlaunch_bfsar, "SeStartupMenu_game", &m_sound_ids[SoundEffect_Startup]);
|
||||
plsrPlayerLoadSoundByName(&m_qlaunch_bfsar, "SeGameIconAdd", &m_sound_ids[SoundEffect_Install]);
|
||||
plsrPlayerLoadSoundByName(&m_qlaunch_bfsar, "SeInsertError", &m_sound_ids[SoundEffect_Error]);
|
||||
plsrPlayerLoadSoundByName(&qlaunch_bfsar, "SeGameIconFocus", &m_sound_ids[SoundEffect_Focus]);
|
||||
plsrPlayerLoadSoundByName(&qlaunch_bfsar, "SeGameIconScroll", &m_sound_ids[SoundEffect_Scroll]);
|
||||
plsrPlayerLoadSoundByName(&qlaunch_bfsar, "SeGameIconLimit", &m_sound_ids[SoundEffect_Limit]);
|
||||
plsrPlayerLoadSoundByName(&qlaunch_bfsar, "SeStartupMenu_game", &m_sound_ids[SoundEffect_Startup]);
|
||||
plsrPlayerLoadSoundByName(&qlaunch_bfsar, "SeGameIconAdd", &m_sound_ids[SoundEffect_Install]);
|
||||
plsrPlayerLoadSoundByName(&qlaunch_bfsar, "SeInsertError", &m_sound_ids[SoundEffect_Error]);
|
||||
|
||||
plsrPlayerSetVolume(m_sound_ids[SoundEffect_Limit], 2.0f);
|
||||
plsrPlayerSetVolume(m_sound_ids[SoundEffect_Focus], 0.5f);
|
||||
PlaySoundEffect(SoundEffect_Startup);
|
||||
plsrPlayerSetVolume(m_sound_ids[SoundEffect_Limit], 2.0f);
|
||||
plsrPlayerSetVolume(m_sound_ids[SoundEffect_Focus], 0.5f);
|
||||
PlaySoundEffect(SoundEffect_Startup);
|
||||
}
|
||||
} else {
|
||||
log_write("failed to mount romfs 0x0100000000001000\n");
|
||||
}
|
||||
@@ -1333,7 +1372,7 @@ App::App(const char* argv0) {
|
||||
log_write("launching from sphaira created forwarder\n");
|
||||
m_is_launched_via_sphaira_forwader = true;
|
||||
} else {
|
||||
log_write("launching from unknown forwader: %.*s size: %zu\n", loader_info_size, envGetLoaderInfo(), loader_info_size);
|
||||
log_write("launching from unknown forwader: %.*s size: %zu\n", (int)loader_info_size, envGetLoaderInfo(), loader_info_size);
|
||||
}
|
||||
} else {
|
||||
log_write("not launching from forwarder\n");
|
||||
@@ -1407,11 +1446,8 @@ App::~App() {
|
||||
}
|
||||
}
|
||||
|
||||
// Close the archive
|
||||
plsrBFSARClose(&m_qlaunch_bfsar);
|
||||
|
||||
// De-initialize our player
|
||||
plsrPlayerExit();
|
||||
plsrPlayerExit();
|
||||
|
||||
this->destroyFramebufferResources();
|
||||
nvgDeleteDk(this->vg);
|
||||
@@ -1433,7 +1469,7 @@ App::~App() {
|
||||
}
|
||||
|
||||
if (R_FAILED(rc = fs.copy_entire_file("/hbmenu.nro", GetExePath()))) {
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", GetExePath(), rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", GetExePath().s, rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
} else {
|
||||
log_write("success with copying over root file!\n");
|
||||
}
|
||||
@@ -1458,7 +1494,7 @@ App::~App() {
|
||||
if (R_SUCCEEDED(rc) && !std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
|
||||
if (std::strcmp(hbmenu_nacp.display_version, sphaira_nacp.display_version) < 0) {
|
||||
if (R_FAILED(rc = fs.copy_entire_file(GetExePath(), sphaira_path))) {
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", sphaira_path, rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", sphaira_path.s, rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
} else {
|
||||
log_write("success with updating hbmenu!\n");
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace {
|
||||
log_write("curl_share_setopt(%s, %s) msg: %s\n", #opt, #v, curl_share_strerror(r)); \
|
||||
} \
|
||||
|
||||
#define USE_THREAD_QUEUE 1
|
||||
constexpr auto API_AGENT = "ITotalJustice";
|
||||
constexpr u64 CHUNK_SIZE = 1024*1024;
|
||||
constexpr auto MAX_THREADS = 4;
|
||||
@@ -302,18 +301,19 @@ struct ThreadQueue {
|
||||
}
|
||||
|
||||
auto Add(const Api& api) -> bool {
|
||||
if (api.GetUrl().empty() || api.GetPath().empty() || !api.GetOnComplete()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mutexLock(&m_mutex);
|
||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||
|
||||
ThreadQueueEntry entry{};
|
||||
entry.api = api;
|
||||
|
||||
switch (api.m_prio) {
|
||||
switch (api.GetPriority()) {
|
||||
case Priority::Normal:
|
||||
m_entries.emplace_back(entry);
|
||||
m_entries.emplace_back(api);
|
||||
break;
|
||||
case Priority::High:
|
||||
m_entries.emplace_front(entry);
|
||||
m_entries.emplace_front(api);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -350,13 +350,13 @@ auto ProgressCallbackFunc1(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
|
||||
}
|
||||
|
||||
auto ProgressCallbackFunc2(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) -> size_t {
|
||||
if (!g_running) {
|
||||
auto api = static_cast<Api*>(clientp);
|
||||
if (!g_running || api->GetToken().stop_requested()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// log_write("pcall called %u %u %u %u\n", dltotal, dlnow, ultotal, ulnow);
|
||||
auto callback = *static_cast<OnProgress*>(clientp);
|
||||
if (!callback(dltotal, dlnow, ultotal, ulnow)) {
|
||||
if (!api->GetOnProgress()(dltotal, dlnow, ultotal, ulnow)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -443,12 +443,17 @@ auto header_callback(char* b, size_t size, size_t nitems, void* userdata) -> siz
|
||||
}
|
||||
|
||||
auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
||||
// check if stop has been requested before starting download
|
||||
if (e.GetToken().stop_requested()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
fs::FsPath tmp_buf;
|
||||
const bool has_file = !e.m_path.empty() && e.m_path != "";
|
||||
const bool has_post = !e.m_fields.m_str.empty() && e.m_fields.m_str != "";
|
||||
const bool has_file = !e.GetPath().empty() && e.GetPath() != "";
|
||||
const bool has_post = !e.GetFields().empty() && e.GetFields() != "";
|
||||
|
||||
DataStruct chunk;
|
||||
Header header_in = e.m_header;
|
||||
Header header_in = e.GetHeader();
|
||||
Header header_out;
|
||||
fs::FsNativeSd fs;
|
||||
|
||||
@@ -457,17 +462,17 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
||||
fs.CreateDirectoryRecursivelyWithPath(tmp_buf);
|
||||
|
||||
if (auto rc = fs.CreateFile(tmp_buf, 0, 0); R_FAILED(rc) && rc != FsError_PathAlreadyExists) {
|
||||
log_write("failed to create file: %s\n", tmp_buf);
|
||||
log_write("failed to create file: %s\n", tmp_buf.s);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (R_FAILED(fs.OpenFile(tmp_buf, FsOpenMode_Write|FsOpenMode_Append, &chunk.f))) {
|
||||
log_write("failed to open file: %s\n", tmp_buf);
|
||||
log_write("failed to open file: %s\n", tmp_buf.s);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (e.m_flags.m_flags & Flag_Cache) {
|
||||
g_cache.get(e.m_path, header_in);
|
||||
if (e.GetFlags() & Flag_Cache) {
|
||||
g_cache.get(e.GetPath(), header_in);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,7 +480,7 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
||||
chunk.data.reserve(CHUNK_SIZE);
|
||||
|
||||
curl_easy_reset(curl);
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_URL, e.m_url.m_str.c_str());
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_URL, e.GetUrl().c_str());
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_USERAGENT, "TotalJustice");
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
@@ -487,8 +492,8 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_HEADERDATA, &header_out);
|
||||
|
||||
if (has_post) {
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_POSTFIELDS, e.m_fields.m_str.c_str());
|
||||
log_write("setting post field: %s\n", e.m_fields.m_str.c_str());
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_POSTFIELDS, e.GetFields().c_str());
|
||||
log_write("setting post field: %s\n", e.GetFields().c_str());
|
||||
}
|
||||
|
||||
struct curl_slist* list = NULL;
|
||||
@@ -517,8 +522,8 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
||||
}
|
||||
|
||||
// progress calls.
|
||||
if (e.m_on_progress) {
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_XFERINFODATA, &e.m_on_progress);
|
||||
if (e.GetOnProgress()) {
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_XFERINFODATA, &e);
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallbackFunc2);
|
||||
} else {
|
||||
CURL_EASY_SETOPT_LOG(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallbackFunc1);
|
||||
@@ -546,16 +551,16 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
||||
|
||||
if (res == CURLE_OK) {
|
||||
if (http_code == 304) {
|
||||
log_write("cached download: %s\n", e.m_url.m_str.c_str());
|
||||
log_write("cached download: %s\n", e.GetUrl().c_str());
|
||||
} else {
|
||||
log_write("un-cached download: %s code: %u\n", e.m_url.m_str.c_str(), http_code);
|
||||
if (e.m_flags.m_flags & Flag_Cache) {
|
||||
g_cache.set(e.m_path, header_out);
|
||||
log_write("un-cached download: %s code: %lu\n", e.GetUrl().c_str(), http_code);
|
||||
if (e.GetFlags() & Flag_Cache) {
|
||||
g_cache.set(e.GetPath(), header_out);
|
||||
}
|
||||
|
||||
fs.DeleteFile(e.m_path);
|
||||
fs.CreateDirectoryRecursivelyWithPath(e.m_path);
|
||||
if (R_FAILED(fs.RenameFile(tmp_buf, e.m_path))) {
|
||||
fs.DeleteFile(e.GetPath());
|
||||
fs.CreateDirectoryRecursivelyWithPath(e.GetPath());
|
||||
if (R_FAILED(fs.RenameFile(tmp_buf, e.GetPath()))) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
@@ -568,8 +573,8 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
||||
}
|
||||
}
|
||||
|
||||
log_write("Downloaded %s %s\n", e.m_url.m_str.c_str(), curl_easy_strerror(res));
|
||||
return {success, http_code, header_out, chunk.data, e.m_path};
|
||||
log_write("Downloaded %s %s\n", e.GetUrl().c_str(), curl_easy_strerror(res));
|
||||
return {success, http_code, header_out, chunk.data, e.GetPath()};
|
||||
}
|
||||
|
||||
auto DownloadInternal(const Api& e) -> ApiResult {
|
||||
@@ -596,23 +601,18 @@ void ThreadEntry::ThreadFunc(void* p) {
|
||||
auto rc = waitSingle(waiterForUEvent(&data->m_uevent), UINT64_MAX);
|
||||
// log_write("woke up\n");
|
||||
if (!g_running) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if 1
|
||||
const auto result = DownloadInternal(data->m_curl, data->m_api);
|
||||
if (g_running) {
|
||||
const DownloadEventData event_data{data->m_api.m_on_complete, result};
|
||||
if (g_running && data->m_api.GetOnComplete() && !data->m_api.GetToken().stop_requested()) {
|
||||
const DownloadEventData event_data{data->m_api.GetOnComplete(), result, data->m_api.GetToken()};
|
||||
evman::push(std::move(event_data), false);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
// mutexLock(&data->m_mutex);
|
||||
// ON_SCOPE_EXIT(mutexUnlock(&data->m_mutex));
|
||||
|
||||
data->m_in_progress = false;
|
||||
// notify the queue that there's a space free
|
||||
@@ -736,53 +736,35 @@ void Exit() {
|
||||
}
|
||||
|
||||
auto ToMemory(const Api& e) -> ApiResult {
|
||||
if (!e.m_path.empty()) {
|
||||
if (!e.GetPath().empty()) {
|
||||
return {};
|
||||
}
|
||||
return DownloadInternal(e);
|
||||
}
|
||||
|
||||
auto ToFile(const Api& e) -> ApiResult {
|
||||
if (e.m_path.empty()) {
|
||||
if (e.GetPath().empty()) {
|
||||
return {};
|
||||
}
|
||||
return DownloadInternal(e);
|
||||
}
|
||||
|
||||
auto ToMemoryAsync(const Api& api) -> bool {
|
||||
#if USE_THREAD_QUEUE
|
||||
return g_thread_queue.Add(api);
|
||||
#else
|
||||
// mutexLock(&g_thread_queue.m_mutex);
|
||||
// ON_SCOPE_EXIT(mutexUnlock(&g_thread_queue.m_mutex));
|
||||
|
||||
for (auto& entry : g_threads) {
|
||||
if (!entry.InProgress()) {
|
||||
return entry.Setup(callback, url);
|
||||
}
|
||||
}
|
||||
|
||||
log_write("failed to start download, no avaliable threads\n");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto ToFileAsync(const Api& e) -> bool {
|
||||
#if USE_THREAD_QUEUE
|
||||
return g_thread_queue.Add(e);
|
||||
#else
|
||||
// mutexLock(&g_thread_queue.m_mutex);
|
||||
// ON_SCOPE_EXIT(mutexUnlock(&g_thread_queue.m_mutex));
|
||||
}
|
||||
|
||||
for (auto& entry : g_threads) {
|
||||
if (!entry.InProgress()) {
|
||||
return entry.Setup(callback, url, out);
|
||||
}
|
||||
auto EscapeString(const std::string& str) -> std::string {
|
||||
std::string result;
|
||||
const auto s = curl_escape(str.data(), str.length());
|
||||
if (s) {
|
||||
result = s;
|
||||
curl_free(s);
|
||||
}
|
||||
|
||||
log_write("failed to start download, no avaliable threads\n");
|
||||
return false;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace sphaira::curl
|
||||
|
||||
@@ -135,7 +135,7 @@ Result CreateDirectoryRecursively(FsFileSystem* fs, const FsPath& _path, bool ig
|
||||
}
|
||||
|
||||
if (R_FAILED(rc) && rc != FsError_PathAlreadyExists) {
|
||||
log_write("failed to create folder: %s\n", path);
|
||||
log_write("failed to create folder: %s\n", path.s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ Result CreateDirectoryRecursivelyWithPath(FsFileSystem* fs, const FsPath& _path,
|
||||
}
|
||||
|
||||
if (R_FAILED(rc) && rc != FsError_PathAlreadyExists) {
|
||||
log_write("failed to create folder recursively: %s\n", path);
|
||||
log_write("failed to create folder recursively: %s\n", path.s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ 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: break;
|
||||
}
|
||||
|
||||
const fs::FsPath sdmc_path = "/config/sphaira/i18n/" + lang_name + ".json";
|
||||
|
||||
@@ -4,14 +4,19 @@
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds="
|
||||
#include "nanovg/stb_image.h"
|
||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_STATIC
|
||||
#define STBI_WRITE_NO_STDIO
|
||||
#include "stb_image_write.h"
|
||||
#include <stb_image_write.h>
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#define STB_IMAGE_RESIZE_STATIC
|
||||
#include "stb_image_resize2.h"
|
||||
#include <stb_image_resize2.h>
|
||||
#pragma GCC diagnostic pop
|
||||
#pragma GCC diagnostic pop
|
||||
#pragma GCC diagnostic pop
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ auto nro_scan_internal(const fs::FsPath& path, std::vector<NroEntry>& nros, bool
|
||||
if (e.type == FsDirEntryType_Dir) {
|
||||
// assert(!root && "dir should only be scanned on non-root!");
|
||||
fs::FsPath fullpath;
|
||||
std::snprintf(fullpath, sizeof(fullpath), "%s/%s/%s.nro", path, e.name, e.name);
|
||||
std::snprintf(fullpath, sizeof(fullpath), "%s/%s/%s.nro", path.s, e.name, e.name);
|
||||
|
||||
// fast path for detecting an nro in a folder
|
||||
NroEntry entry;
|
||||
@@ -133,12 +133,12 @@ auto nro_scan_internal(const fs::FsPath& path, std::vector<NroEntry>& nros, bool
|
||||
nros.emplace_back(entry);
|
||||
} else {
|
||||
// slow path...
|
||||
std::snprintf(fullpath, sizeof(fullpath), "%s/%s", path, e.name);
|
||||
std::snprintf(fullpath, sizeof(fullpath), "%s/%s", path.s, e.name);
|
||||
nro_scan_internal(fullpath, nros, hide_sphaira, nested, scan_all_dir, false);
|
||||
}
|
||||
} else if (e.type == FsDirEntryType_File && std::string_view{e.name}.ends_with(".nro")) {
|
||||
fs::FsPath fullpath;
|
||||
std::snprintf(fullpath, sizeof(fullpath), "%s/%s", path, e.name);
|
||||
std::snprintf(fullpath, sizeof(fullpath), "%s/%s", path.s, e.name);
|
||||
|
||||
NroEntry entry;
|
||||
if (R_SUCCEEDED(nro_parse_internal(fs, fullpath, entry))) {
|
||||
@@ -148,7 +148,7 @@ auto nro_scan_internal(const fs::FsPath& path, std::vector<NroEntry>& nros, bool
|
||||
R_SUCCEED();
|
||||
}
|
||||
} else {
|
||||
log_write("error when trying to parse %s\n", fullpath);
|
||||
log_write("error when trying to parse %s\n", fullpath.s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ void loop(void* args) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log_write("got name: %s\n", name);
|
||||
log_write("got name: %s\n", name.s);
|
||||
|
||||
u32 filesize{};
|
||||
if (!recvall(connfd, &filesize, sizeof(filesize))) {
|
||||
|
||||
@@ -655,7 +655,7 @@ void patch_npdm(std::vector<u8>& npdm, const NpdmPatch& patch) {
|
||||
splGetConfig(SplConfigItem_ExosphereVersion, &ver);
|
||||
ver >>= 40;
|
||||
|
||||
if (ver >= MAKEHOSVERSION(1,7,1)) {
|
||||
if (ver >= MAKEHOSVERSION(1,8,0)) {
|
||||
npdm_patch_kc(npdm, meta.aci0_offset + aci0.kac_offset, aci0.kac_size, 16, BIT(19));
|
||||
npdm_patch_kc(npdm, meta.acid_offset + acid.kac_offset, acid.kac_size, 16, BIT(19));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <yyjson.h>
|
||||
#include <nanovg/stb_image.h>
|
||||
#include <stb_image.h>
|
||||
#include <minizip/unzip.h>
|
||||
#include <mbedtls/md5.h>
|
||||
#include <ranges>
|
||||
@@ -65,60 +65,27 @@ auto BuildIconUrl(const Entry& e) -> std::string {
|
||||
return out;
|
||||
}
|
||||
|
||||
#if 0
|
||||
auto BuildInfoUrl(const Entry& e) -> std::string {
|
||||
char out[0x100];
|
||||
std::snprintf(out, sizeof(out), "%s/packages/%s/info.json", URL_BASE, e.name.c_str());
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto BuildBannerUrl(const Entry& e) -> std::string {
|
||||
char out[0x100];
|
||||
std::snprintf(out, sizeof(out), "%s/packages/%s/screen.png", URL_BASE, e.name.c_str());
|
||||
return out;
|
||||
}
|
||||
|
||||
#if 0
|
||||
auto BuildScreensUrl(const Entry& e, u8 num) -> std::string {
|
||||
char out[0x100];
|
||||
std::snprintf(out, sizeof(out), "%s/packages/%s/screen%u.png", URL_BASE, e.name.c_str(), num+1);
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto BuildMainifestUrl(const Entry& e) -> std::string {
|
||||
char out[0x100];
|
||||
std::snprintf(out, sizeof(out), "%s/packages/%s/manifest.install", URL_BASE, e.name.c_str());
|
||||
return out;
|
||||
}
|
||||
|
||||
auto BuildZipUrl(const Entry& e) -> std::string {
|
||||
char out[0x100];
|
||||
std::snprintf(out, sizeof(out), "%s/zips/%s.zip", URL_BASE, e.name.c_str());
|
||||
return out;
|
||||
}
|
||||
|
||||
auto BuildFeedbackUrl(std::span<u32> ids) -> std::string {
|
||||
std::string out{"https://wiiubru.com/feedback/messages?ids="};
|
||||
for (u32 i = 0; i < ids.size(); i++) {
|
||||
if (i != 0) {
|
||||
out.push_back(',');
|
||||
}
|
||||
out += std::to_string(ids[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
auto BuildIconCachePath(const Entry& e) -> fs::FsPath {
|
||||
fs::FsPath out;
|
||||
std::snprintf(out, sizeof(out), "%s/icons/%s.png", CACHE_PATH, e.name.c_str());
|
||||
std::snprintf(out, sizeof(out), "%s/icons/%s.png", CACHE_PATH.s, e.name.c_str());
|
||||
return out;
|
||||
}
|
||||
|
||||
auto BuildBannerCachePath(const Entry& e) -> fs::FsPath {
|
||||
fs::FsPath out;
|
||||
std::snprintf(out, sizeof(out), "%s/banners/%s.png", CACHE_PATH, e.name.c_str());
|
||||
std::snprintf(out, sizeof(out), "%s/banners/%s.png", CACHE_PATH.s, e.name.c_str());
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -302,7 +269,6 @@ auto AppDlToStr(u32 value) -> std::string {
|
||||
|
||||
void ReadFromInfoJson(Entry& e) {
|
||||
const auto info_path = BuildInfoCachePath(e);
|
||||
const auto manifest_path = BuildManifestCachePath(e);
|
||||
|
||||
yyjson_read_err err;
|
||||
auto doc = yyjson_read_file(info_path, YYJSON_READ_NOFLAG, nullptr, &err);
|
||||
@@ -340,9 +306,9 @@ auto UninstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
const auto safe_buf = fs::AppendPath("/", e.path);
|
||||
// this will handle read only files, ie, hbmenu.nro
|
||||
if (R_FAILED(fs.DeleteFile(safe_buf))) {
|
||||
log_write("failed to delete file: %s\n", safe_buf);
|
||||
log_write("failed to delete file: %s\n", safe_buf.s);
|
||||
} else {
|
||||
log_write("deleted file: %s\n", safe_buf);
|
||||
log_write("deleted file: %s\n", safe_buf.s);
|
||||
// todo: delete empty directories!
|
||||
// fs::delete_directory(safe_buf);
|
||||
}
|
||||
@@ -353,9 +319,9 @@ auto UninstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
const auto dir = BuildPackageCachePath(entry);
|
||||
pbox->NewTransfer("Removing "_i18n + dir);
|
||||
if (R_FAILED(fs.DeleteDirectoryRecursively(dir))) {
|
||||
log_write("failed to delete folder: %s\n", dir);
|
||||
log_write("failed to delete folder: %s\n", dir.s);
|
||||
} else {
|
||||
log_write("deleted: %s\n", dir);
|
||||
log_write("deleted: %s\n", dir.s);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -458,7 +424,7 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
if (!pbox->ShouldExit()) {
|
||||
auto zfile = unzOpen64(zip_out);
|
||||
if (!zfile) {
|
||||
log_write("failed to open zip: %s\n", zip_out);
|
||||
log_write("failed to open zip: %s\n", zip_out.s);
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT(unzClose(zfile));
|
||||
@@ -501,7 +467,7 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
pbox->NewTransfer(inzip);
|
||||
|
||||
if (UNZ_END_OF_LIST_OF_FILE == unzLocateFile(zfile, inzip, 0)) {
|
||||
log_write("failed to find %s\n", inzip);
|
||||
log_write("failed to find %s\n", inzip.s);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -526,19 +492,19 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
|
||||
Result rc;
|
||||
if (R_FAILED(rc = fs.CreateFile(output, info.uncompressed_size, 0)) && rc != FsError_PathAlreadyExists) {
|
||||
log_write("failed to create file: %s 0x%04X\n", output, rc);
|
||||
log_write("failed to create file: %s 0x%04X\n", output.s, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
FsFile f;
|
||||
if (R_FAILED(rc = fs.OpenFile(output, FsOpenMode_Write, &f))) {
|
||||
log_write("failed to open file: %s 0x%04X\n", output, rc);
|
||||
log_write("failed to open file: %s 0x%04X\n", output.s, 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", output, rc);
|
||||
log_write("failed to set file size: %s 0x%04X\n", output.s, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -551,12 +517,12 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
|
||||
const auto bytes_read = unzReadCurrentFile(zfile, buf.data(), buf.size());
|
||||
if (bytes_read <= 0) {
|
||||
log_write("failed to read zip file: %s\n", inzip);
|
||||
log_write("failed to read zip file: %s\n", inzip.s);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (R_FAILED(rc = fsFileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None))) {
|
||||
log_write("failed to write file: %s 0x%04X\n", output, rc);
|
||||
log_write("failed to write file: %s 0x%04X\n", output.s, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -615,9 +581,9 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> bool {
|
||||
const auto safe_buf = fs::AppendPath("/", old_entry.path);
|
||||
// std::strcat(safe_buf, old_entry.path);
|
||||
if (R_FAILED(fs.DeleteFile(safe_buf))) {
|
||||
log_write("failed to delete: %s\n", safe_buf);
|
||||
log_write("failed to delete: %s\n", safe_buf.s);
|
||||
} else {
|
||||
log_write("deleted file: %s\n", safe_buf);
|
||||
log_write("deleted file: %s\n", safe_buf.s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -663,6 +629,7 @@ EntryMenu::EntryMenu(Entry& entry, const LazyImage& default_icon, Menu& menu)
|
||||
curl::Url{URL_POST_FEEDBACK},
|
||||
curl::Path{file},
|
||||
curl::Fields{post},
|
||||
curl::StopToken{this->GetToken()},
|
||||
curl::OnComplete{[](auto& result){
|
||||
if (result.success) {
|
||||
log_write("got feedback!\n");
|
||||
@@ -697,6 +664,7 @@ EntryMenu::EntryMenu(Entry& entry, const LazyImage& default_icon, Menu& menu)
|
||||
curl::Url{url},
|
||||
curl::Path{path},
|
||||
curl::Flags{curl::Flag_Cache},
|
||||
curl::StopToken{this->GetToken()},
|
||||
curl::OnComplete{[this, path](auto& result){
|
||||
if (result.success) {
|
||||
if (result.code == 304) {
|
||||
@@ -759,13 +727,9 @@ 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->GetColour(ThemeEntryID_TEXT), "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) {
|
||||
const auto& text_col = theme->GetColour(ThemeEntryID_TEXT);
|
||||
|
||||
// todo: rewrite this mess and use list
|
||||
constexpr float mm = 0;//20;
|
||||
constexpr Vec4 block{968.f + mm, 110.f, 256.f - mm*2, 60.f};
|
||||
constexpr float text_xoffset{15.f};
|
||||
const float x = block.x;
|
||||
float y = 1.f + text_start_y + (text_inc_y * 3) ;
|
||||
const float h = block.h;
|
||||
@@ -779,7 +743,7 @@ void EntryMenu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
gfx::drawRectOutline(vg, theme, 4.f, Vec4{x, y, w, h});
|
||||
}
|
||||
|
||||
gfx::drawTextArgs(vg, x + w / 2, y + h / 2, 22, NVG_ALIGN_MIDDLE | NVG_ALIGN_CENTER, theme->GetColour(ThemeEntryID_TEXT), option.display_text.c_str());
|
||||
gfx::drawTextArgs(vg, x + w / 2, y + h / 2, 22, NVG_ALIGN_MIDDLE | NVG_ALIGN_CENTER, theme->GetColour(text_id), option.display_text.c_str());
|
||||
y -= block.h + 18;
|
||||
}
|
||||
|
||||
@@ -960,7 +924,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"AppStore"_i18n}
|
||||
sort_items.push_back("Alphabetical"_i18n);
|
||||
|
||||
SidebarEntryArray::Items order_items;
|
||||
order_items.push_back("Decending"_i18n);
|
||||
order_items.push_back("Descending"_i18n);
|
||||
order_items.push_back("Ascending"_i18n);
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Filter"_i18n, filter_items, [this, filter_items](s64& index_out){
|
||||
@@ -990,6 +954,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"AppStore"_i18n}
|
||||
curl::Url{URL_JSON},
|
||||
curl::Path{REPO_PATH},
|
||||
curl::Flags{curl::Flag_Cache},
|
||||
curl::StopToken{this->GetToken()},
|
||||
curl::OnComplete{[this](auto& result){
|
||||
if (result.success) {
|
||||
m_repo_download_state = ImageDownloadState::Done;
|
||||
@@ -1071,6 +1036,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
curl::Url{url},
|
||||
curl::Path{path},
|
||||
curl::Flags{curl::Flag_Cache},
|
||||
curl::StopToken{this->GetToken()},
|
||||
curl::OnComplete{[this, &image](auto& result) {
|
||||
if (result.success) {
|
||||
image.state = ImageDownloadState::Done;
|
||||
@@ -1294,7 +1260,7 @@ void Menu::Sort() {
|
||||
case SortType_Updated: {
|
||||
if (lhs.updated_num == rhs.updated_num) {
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
||||
} else if (m_order == OrderType_Decending) {
|
||||
} else if (m_order == OrderType_Descending) {
|
||||
return lhs.updated_num > rhs.updated_num;
|
||||
} else {
|
||||
return lhs.updated_num < rhs.updated_num;
|
||||
@@ -1303,7 +1269,7 @@ void Menu::Sort() {
|
||||
case SortType_Downloads: {
|
||||
if (lhs.app_dls == rhs.app_dls) {
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
||||
} else if (m_order == OrderType_Decending) {
|
||||
} else if (m_order == OrderType_Descending) {
|
||||
return lhs.app_dls > rhs.app_dls;
|
||||
} else {
|
||||
return lhs.app_dls < rhs.app_dls;
|
||||
@@ -1312,14 +1278,14 @@ void Menu::Sort() {
|
||||
case SortType_Size: {
|
||||
if (lhs.extracted == rhs.extracted) {
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
||||
} else if (m_order == OrderType_Decending) {
|
||||
} else if (m_order == OrderType_Descending) {
|
||||
return lhs.extracted > rhs.extracted;
|
||||
} else {
|
||||
return lhs.extracted < rhs.extracted;
|
||||
}
|
||||
} break;
|
||||
case SortType_Alphabetical: {
|
||||
if (m_order == OrderType_Decending) {
|
||||
if (m_order == OrderType_Descending) {
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
||||
} else {
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) > 0;
|
||||
|
||||
@@ -128,7 +128,7 @@ auto GetRomDatabaseFromPath(std::string_view path) -> int {
|
||||
if ((
|
||||
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());
|
||||
log_write("found it :) %.*s\n", (int)p.database.length(), p.database.data());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ auto GetRomDatabaseFromPath(std::string_view path) -> int {
|
||||
if ((
|
||||
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());
|
||||
log_write("found it :) %.*s\n", (int)p.database.length(), p.database.data());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -352,7 +352,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
sort_items.push_back("Alphabetical"_i18n);
|
||||
|
||||
SidebarEntryArray::Items order_items;
|
||||
order_items.push_back("Decending"_i18n);
|
||||
order_items.push_back("Descending"_i18n);
|
||||
order_items.push_back("Ascending"_i18n);
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this](s64& index_out){
|
||||
@@ -452,9 +452,8 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
Scan(m_path);
|
||||
} else {
|
||||
char msg[FS_MAX_PATH];
|
||||
std::snprintf(msg, sizeof(msg), "Failed to rename file: %s", entry.name);
|
||||
App::Push(std::make_shared<ErrorBox>(rc, msg));
|
||||
const auto msg = std::string("Failed to rename file: ") + entry.name;
|
||||
App::Push(std::make_shared<ErrorBox>(rc, msg.c_str()));
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -478,10 +477,10 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
|
||||
m_fs->CreateDirectoryRecursivelyWithPath(full_path);
|
||||
if (R_SUCCEEDED(m_fs->CreateFile(full_path, 0, 0))) {
|
||||
log_write("created file: %s\n", full_path);
|
||||
log_write("created file: %s\n", full_path.s);
|
||||
Scan(m_path);
|
||||
} else {
|
||||
log_write("failed to create file: %s\n", full_path);
|
||||
log_write("failed to create file: %s\n", full_path.s);
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -499,10 +498,10 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(m_fs->CreateDirectoryRecursively(full_path))) {
|
||||
log_write("created dir: %s\n", full_path);
|
||||
log_write("created dir: %s\n", full_path.s);
|
||||
Scan(m_path);
|
||||
} else {
|
||||
log_write("failed to create dir: %s\n", full_path);
|
||||
log_write("failed to create dir: %s\n", full_path.s);
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -777,7 +776,7 @@ void Menu::InstallForwarder() {
|
||||
}
|
||||
|
||||
auto Menu::Scan(const fs::FsPath& new_path, bool is_walk_up) -> Result {
|
||||
log_write("new scan path: %s\n", new_path);
|
||||
log_write("new scan path: %s\n", new_path.s);
|
||||
if (!is_walk_up && !m_path.empty() && !m_entries_current.empty()) {
|
||||
const LastFile f(GetEntry().name, m_index, m_list->GetYoff(), m_entries_current.size());
|
||||
m_previous_highlighted_file.emplace_back(f);
|
||||
@@ -794,7 +793,7 @@ auto Menu::Scan(const fs::FsPath& new_path, bool is_walk_up) -> Result {
|
||||
}
|
||||
|
||||
FsDir d;
|
||||
R_TRY(m_fs->OpenDirectory(new_path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize, &d));
|
||||
R_TRY(m_fs->OpenDirectory(new_path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, &d));
|
||||
ON_SCOPE_EXIT(fsDirClose(&d));
|
||||
|
||||
s64 count;
|
||||
@@ -868,7 +867,7 @@ auto Menu::FindFileAssocFor() -> std::vector<FileAssocEntry> {
|
||||
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());
|
||||
log_write("found ext: %s assoc_ext: %s assoc.ext: %s\n", assoc.path.s, assoc_ext.c_str(), extension.c_str());
|
||||
out_entries.emplace_back(assoc);
|
||||
}
|
||||
}
|
||||
@@ -886,7 +885,7 @@ auto Menu::FindFileAssocFor() -> std::vector<FileAssocEntry> {
|
||||
if (assoc.database.empty()) {
|
||||
for (const auto& assoc_ext : assoc.ext) {
|
||||
if (assoc_ext == extension) {
|
||||
log_write("found ext: %s\n", assoc.path);
|
||||
log_write("found ext: %s\n", assoc.path.s);
|
||||
out_entries.emplace_back(assoc);
|
||||
}
|
||||
}
|
||||
@@ -1034,14 +1033,14 @@ void Menu::Sort() {
|
||||
case SortType_Size: {
|
||||
if (lhs.file_size == rhs.file_size) {
|
||||
return strncasecmp(lhs.name, rhs.name, sizeof(lhs.name)) < 0;
|
||||
} else if (order == OrderType_Decending) {
|
||||
} else if (order == OrderType_Descending) {
|
||||
return lhs.file_size > rhs.file_size;
|
||||
} else {
|
||||
return lhs.file_size < rhs.file_size;
|
||||
}
|
||||
} break;
|
||||
case SortType_Alphabetical: {
|
||||
if (order == OrderType_Decending) {
|
||||
if (order == OrderType_Descending) {
|
||||
return strncasecmp(lhs.name, rhs.name, sizeof(lhs.name)) < 0;
|
||||
} else {
|
||||
return strncasecmp(lhs.name, rhs.name, sizeof(lhs.name)) > 0;
|
||||
@@ -1099,7 +1098,6 @@ void Menu::SetIndexFromLastFile(const LastFile& last_file) {
|
||||
}
|
||||
}
|
||||
SetIndex(index);
|
||||
log_write("\nnew index: %zu %zu mod: %zu\n", m_index, index % 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1148,7 +1146,7 @@ void Menu::OnDeleteCallback() {
|
||||
if (p.IsDir()) {
|
||||
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);
|
||||
log_write("failed to get dir collection: %s\n", full_path.s);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1166,10 +1164,10 @@ void Menu::OnDeleteCallback() {
|
||||
const auto full_path = GetNewPath(c.path, p.name);
|
||||
pbox->NewTransfer("Deleting "_i18n + full_path);
|
||||
if (p.type == FsDirEntryType_Dir) {
|
||||
log_write("deleting dir: %s\n", full_path);
|
||||
log_write("deleting dir: %s\n", full_path.s);
|
||||
m_fs->DeleteDirectory(full_path);
|
||||
} else {
|
||||
log_write("deleting file: %s\n", full_path);
|
||||
log_write("deleting file: %s\n", full_path.s);
|
||||
m_fs->DeleteFile(full_path);
|
||||
}
|
||||
}
|
||||
@@ -1194,10 +1192,10 @@ void Menu::OnDeleteCallback() {
|
||||
pbox->NewTransfer("Deleting "_i18n + full_path);
|
||||
|
||||
if (p.IsDir()) {
|
||||
log_write("deleting dir: %s\n", full_path);
|
||||
log_write("deleting dir: %s\n", full_path.s);
|
||||
m_fs->DeleteDirectory(full_path);
|
||||
} else {
|
||||
log_write("deleting file: %s\n", full_path);
|
||||
log_write("deleting file: %s\n", full_path.s);
|
||||
m_fs->DeleteFile(full_path);
|
||||
}
|
||||
}
|
||||
@@ -1212,8 +1210,6 @@ void Menu::OnDeleteCallback() {
|
||||
}
|
||||
|
||||
void Menu::OnPasteCallback() {
|
||||
bool use_progress_box{true};
|
||||
|
||||
// check if we only have 1 file / folder and is cut (rename)
|
||||
if (m_selected_files.size() == 1 && m_selected_type == SelectedType::Cut) {
|
||||
const auto& entry = m_selected_files[0];
|
||||
@@ -1263,7 +1259,7 @@ void Menu::OnPasteCallback() {
|
||||
if (p.IsDir()) {
|
||||
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);
|
||||
log_write("failed to get dir collection: %s\n", full_path.s);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1300,7 +1296,7 @@ void Menu::OnPasteCallback() {
|
||||
const auto src_path = GetNewPath(c.path, p.name);
|
||||
const auto dst_path = GetNewPath(base_dst_path, p.name);
|
||||
|
||||
log_write("creating: %s to %s\n", src_path, dst_path);
|
||||
log_write("creating: %s to %s\n", src_path.s, dst_path.s);
|
||||
pbox->NewTransfer("Creating "_i18n + dst_path);
|
||||
m_fs->CreateDirectory(dst_path);
|
||||
}
|
||||
@@ -1315,7 +1311,7 @@ void Menu::OnPasteCallback() {
|
||||
const auto dst_path = GetNewPath(base_dst_path, p.name);
|
||||
|
||||
pbox->NewTransfer("Copying "_i18n + src_path);
|
||||
log_write("copying: %s to %s\n", src_path, dst_path);
|
||||
log_write("copying: %s to %s\n", src_path.s, dst_path.s);
|
||||
R_TRY_RESULT(pbox->CopyFile(src_path, dst_path), false);
|
||||
}
|
||||
}
|
||||
@@ -1415,7 +1411,7 @@ auto Menu::get_collections(const fs::FsPath& path, const fs::FsPath& parent_name
|
||||
// get a list of all the files / dirs
|
||||
FsDirCollection collection;
|
||||
R_TRY(get_collection(path, parent_name, collection, true, true, false));
|
||||
log_write("got collection: %s parent_name: %s files: %zu dirs: %zu\n", path, parent_name, collection.files.size(), collection.dirs.size());
|
||||
log_write("got collection: %s parent_name: %s files: %zu dirs: %zu\n", path.s, parent_name.s, collection.files.size(), collection.dirs.size());
|
||||
out.emplace_back(collection);
|
||||
|
||||
// for (size_t i = 0; i < collection.dirs.size(); i++) {
|
||||
@@ -1423,7 +1419,7 @@ auto Menu::get_collections(const fs::FsPath& path, const fs::FsPath& parent_name
|
||||
// use heap as to not explode the stack
|
||||
const auto new_path = std::make_unique<fs::FsPath>(Menu::GetNewPath(path, p.name));
|
||||
const auto new_parent_name = std::make_unique<fs::FsPath>(Menu::GetNewPath(parent_name, p.name));
|
||||
log_write("trying to get nested collection: %s parent_name: %s\n", *new_path, *new_parent_name);
|
||||
log_write("trying to get nested collection: %s parent_name: %s\n", new_path->s, new_parent_name->s);
|
||||
R_TRY(get_collections(*new_path, *new_parent_name, out));
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ auto DownloadApp(ProgressBox* pbox, const GhApiAsset& gh_asset, const AssetEntry
|
||||
log_write("found zip\n");
|
||||
auto zfile = unzOpen64(temp_file);
|
||||
if (!zfile) {
|
||||
log_write("failed to open zip: %s\n", temp_file);
|
||||
log_write("failed to open zip: %s\n", temp_file.s);
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT(unzClose(zfile));
|
||||
@@ -155,29 +155,29 @@ auto DownloadApp(ProgressBox* pbox, const GhApiAsset& gh_asset, const AssetEntry
|
||||
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);
|
||||
log_write("failed to create folder: %s 0x%04X\n", file_path.s, rc);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (R_FAILED(rc = fs.CreateDirectoryRecursivelyWithPath(file_path)) && rc != FsError_PathAlreadyExists) {
|
||||
log_write("failed to create folder: %s 0x%04X\n", file_path, rc);
|
||||
log_write("failed to create folder: %s 0x%04X\n", file_path.s, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
log_write("failed to create file: %s 0x%04X\n", file_path.s, 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);
|
||||
log_write("failed to open file: %s 0x%04X\n", file_path.s, 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);
|
||||
log_write("failed to set file size: %s 0x%04X\n", file_path.s, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace {
|
||||
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);
|
||||
std::snprintf(out, sizeof(out), "%.*s.%s.star", int(dilem - nro_path.s + 1), nro_path.s, dilem + 1);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
|
||||
sort_items.push_back("Size (Star)"_i18n);
|
||||
|
||||
SidebarEntryArray::Items order_items;
|
||||
order_items.push_back("Decending"_i18n);
|
||||
order_items.push_back("Descending"_i18n);
|
||||
order_items.push_back("Ascending"_i18n);
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](s64& index_out){
|
||||
@@ -237,8 +237,6 @@ void Menu::SetIndex(s64 index) {
|
||||
m_list->SetYoff(0);
|
||||
}
|
||||
|
||||
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)) {
|
||||
@@ -279,8 +277,8 @@ void Menu::ScanHomebrew() {
|
||||
|
||||
struct IniUser {
|
||||
std::vector<NroEntry>& entires;
|
||||
Hbini* ini;
|
||||
std::string last_section;
|
||||
Hbini* ini{};
|
||||
std::string last_section{};
|
||||
} ini_user{ m_entries };
|
||||
|
||||
ini_browse([](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) -> int {
|
||||
@@ -331,13 +329,13 @@ void Menu::Sort() {
|
||||
const auto name_cmp = [order](const NroEntry& lhs, const NroEntry& rhs) -> bool {
|
||||
auto r = strcasecmp(lhs.GetName(), rhs.GetName());
|
||||
if (!r) {
|
||||
auto r = strcasecmp(lhs.GetAuthor(), rhs.GetAuthor());
|
||||
r = strcasecmp(lhs.GetAuthor(), rhs.GetAuthor());
|
||||
if (!r) {
|
||||
auto r = strcasecmp(lhs.path, rhs.path);
|
||||
r = strcasecmp(lhs.path, rhs.path);
|
||||
}
|
||||
}
|
||||
|
||||
if (order == OrderType_Decending) {
|
||||
if (order == OrderType_Descending) {
|
||||
return r < 0;
|
||||
} else {
|
||||
return r > 0;
|
||||
@@ -351,6 +349,7 @@ void Menu::Sort() {
|
||||
} else if (!lhs.has_star.value() && rhs.has_star.value()) {
|
||||
return false;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case SortType_Updated: {
|
||||
auto lhs_timestamp = lhs.hbini.timestamp;
|
||||
auto rhs_timestamp = rhs.hbini.timestamp;
|
||||
@@ -363,7 +362,7 @@ void Menu::Sort() {
|
||||
|
||||
if (lhs_timestamp == rhs_timestamp) {
|
||||
return name_cmp(lhs, rhs);
|
||||
} else if (order == OrderType_Decending) {
|
||||
} else if (order == OrderType_Descending) {
|
||||
return lhs_timestamp > rhs_timestamp;
|
||||
} else {
|
||||
return lhs_timestamp < rhs_timestamp;
|
||||
@@ -376,10 +375,11 @@ void Menu::Sort() {
|
||||
} else if (!lhs.has_star.value() && rhs.has_star.value()) {
|
||||
return false;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case SortType_Size: {
|
||||
if (lhs.size == rhs.size) {
|
||||
return name_cmp(lhs, rhs);
|
||||
} else if (order == OrderType_Decending) {
|
||||
} else if (order == OrderType_Descending) {
|
||||
return lhs.size > rhs.size;
|
||||
} else {
|
||||
return lhs.size < rhs.size;
|
||||
@@ -392,6 +392,7 @@ void Menu::Sort() {
|
||||
} else if (!lhs.has_star.value() && rhs.has_star.value()) {
|
||||
return false;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case SortType_Alphabetical: {
|
||||
return name_cmp(lhs, rhs);
|
||||
} break;
|
||||
|
||||
@@ -258,8 +258,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const auto scale = std::min(scale_x, scale_y);
|
||||
w = m_irs_width * scale;
|
||||
h = m_irs_height * scale;
|
||||
cx = (m_pos.x + m_pos.w / 2.0) - w / 2.0;
|
||||
cy = (m_pos.y + m_pos.h / 2.0) - h / 2.0;
|
||||
cx = (m_pos.x + m_pos.w / 2.F) - w / 2.F;
|
||||
cy = (m_pos.y + m_pos.h / 2.F) - h / 2.F;
|
||||
angle = 0;
|
||||
} break;
|
||||
case Rotation_90: {
|
||||
@@ -268,8 +268,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const auto scale = std::min(scale_x, scale_y);
|
||||
w = m_irs_width * scale;
|
||||
h = m_irs_height * scale;
|
||||
cx = (m_pos.x + m_pos.w / 2.0) + h / 2.0;
|
||||
cy = (m_pos.y + m_pos.h / 2.0) - w / 2.0;
|
||||
cx = (m_pos.x + m_pos.w / 2.F) + h / 2.F;
|
||||
cy = (m_pos.y + m_pos.h / 2.F) - w / 2.F;
|
||||
angle = 90;
|
||||
} break;
|
||||
case Rotation_180: {
|
||||
@@ -278,8 +278,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const auto scale = std::min(scale_x, scale_y);
|
||||
w = m_irs_width * scale;
|
||||
h = m_irs_height * scale;
|
||||
cx = (m_pos.x + m_pos.w / 2.0) + w / 2.0;
|
||||
cy = (m_pos.y + m_pos.h / 2.0) + h / 2.0;
|
||||
cx = (m_pos.x + m_pos.w / 2.F) + w / 2.F;
|
||||
cy = (m_pos.y + m_pos.h / 2.F) + h / 2.F;
|
||||
angle = 180;
|
||||
} break;
|
||||
case Rotation_270: {
|
||||
@@ -288,8 +288,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const auto scale = std::min(scale_x, scale_y);
|
||||
w = m_irs_width * scale;
|
||||
h = m_irs_height * scale;
|
||||
cx = (m_pos.x + m_pos.w / 2.0) - h / 2.0;
|
||||
cy = (m_pos.y + m_pos.h / 2.0) + w / 2.0;
|
||||
cx = (m_pos.x + m_pos.w / 2.F) - h / 2.F;
|
||||
cy = (m_pos.y + m_pos.h / 2.F) + w / 2.F;
|
||||
angle = 270;
|
||||
} break;
|
||||
}
|
||||
@@ -382,7 +382,7 @@ void Menu::LoadDefaultConfig() {
|
||||
irsGetClusteringProcessorDefaultConfig(&m_clustering_config);
|
||||
irsGetIrLedProcessorDefaultConfig(&m_led_config);
|
||||
|
||||
m_tera_config;
|
||||
m_tera_config = {};
|
||||
m_adaptive_config = {};
|
||||
m_hand_config = {};
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string v
|
||||
if (!pbox->ShouldExit()) {
|
||||
auto zfile = unzOpen64(zip_out);
|
||||
if (!zfile) {
|
||||
log_write("failed to open zip: %s\n", zip_out);
|
||||
log_write("failed to open zip: %s\n", zip_out.s);
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT(unzClose(zfile));
|
||||
@@ -96,25 +96,25 @@ auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string v
|
||||
Result rc;
|
||||
if (file_path[strlen(file_path) -1] == '/') {
|
||||
if (R_FAILED(rc = fs.CreateDirectoryRecursively(file_path)) && rc != FsError_PathAlreadyExists) {
|
||||
log_write("failed to create folder: %s 0x%04X\n", file_path, rc);
|
||||
log_write("failed to create folder: %s 0x%04X\n", file_path.s, 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);
|
||||
log_write("failed to create file: %s 0x%04X\n", file_path.s, 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);
|
||||
log_write("failed to open file: %s 0x%04X\n", file_path.s, 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);
|
||||
log_write("failed to set file size: %s 0x%04X\n", file_path.s, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string v
|
||||
}
|
||||
|
||||
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);
|
||||
log_write("failed to write file: %s 0x%04X\n", file_path.s, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ MainMenu::MainMenu() {
|
||||
curl::Url{GITHUB_URL},
|
||||
curl::Path{CACHE_PATH},
|
||||
curl::Flags{curl::Flag_Cache},
|
||||
curl::StopToken{this->GetToken()},
|
||||
curl::Header{
|
||||
{ "Accept", "application/vnd.github+json" },
|
||||
},
|
||||
@@ -252,6 +253,10 @@ MainMenu::MainMenu() {
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Music"_i18n, App::GetThemeMusicEnable(), [this](bool& enable){
|
||||
App::SetThemeMusicEnable(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("12 Hour Time"_i18n, App::Get12HourTimeEnable(), [this](bool& enable){
|
||||
App::Set12HourTimeEnable(enable);
|
||||
}, "Enabled"_i18n, "Disabled"_i18n));
|
||||
}));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Network"_i18n, [this](){
|
||||
|
||||
@@ -38,22 +38,28 @@ void MenuBase::Draw(NVGcontext* vg, Theme* theme) {
|
||||
|
||||
nvgFontSize(vg, font_size);
|
||||
|
||||
#define draw(...) \
|
||||
#define draw(colour, ...) \
|
||||
gfx::textBounds(vg, 0, 0, bounds, __VA_ARGS__); \
|
||||
start_x -= bounds[2] - bounds[0]; \
|
||||
gfx::drawTextArgs(vg, start_x, start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_BOTTOM, theme->GetColour(ThemeEntryID_TEXT), __VA_ARGS__); \
|
||||
gfx::drawTextArgs(vg, start_x, start_y, font_size, NVG_ALIGN_LEFT | NVG_ALIGN_BOTTOM, theme->GetColour(colour), __VA_ARGS__); \
|
||||
start_x -= spacing;
|
||||
|
||||
// draw("version %s", APP_VERSION);
|
||||
draw("%u\uFE6A", m_battery_percetange);
|
||||
draw("%02u:%02u:%02u", m_tm.tm_hour, m_tm.tm_min, m_tm.tm_sec);
|
||||
if (m_ip) {
|
||||
draw("%u.%u.%u.%u", m_ip&0xFF, (m_ip>>8)&0xFF, (m_ip>>16)&0xFF, (m_ip>>24)&0xFF);
|
||||
draw(ThemeEntryID_TEXT, "%u\uFE6A", m_battery_percetange);
|
||||
|
||||
if (App::Get12HourTimeEnable()) {
|
||||
draw(ThemeEntryID_TEXT, "%02u:%02u:%02u %s", (m_tm.tm_hour == 0 || m_tm.tm_hour == 12) ? 12 : m_tm.tm_hour % 12, m_tm.tm_min, m_tm.tm_sec, (m_tm.tm_hour < 12) ? "AM" : "PM");
|
||||
} else {
|
||||
draw(("No Internet"_i18n).c_str());
|
||||
draw(ThemeEntryID_TEXT, "%02u:%02u:%02u", m_tm.tm_hour, m_tm.tm_min, m_tm.tm_sec);
|
||||
}
|
||||
|
||||
if (m_ip) {
|
||||
draw(ThemeEntryID_TEXT, "%u.%u.%u.%u", m_ip&0xFF, (m_ip>>8)&0xFF, (m_ip>>16)&0xFF, (m_ip>>24)&0xFF);
|
||||
} else {
|
||||
draw(ThemeEntryID_TEXT, ("No Internet"_i18n).c_str());
|
||||
}
|
||||
if (!App::IsApplication()) {
|
||||
draw(("[Applet Mode]"_i18n).c_str());
|
||||
draw(ThemeEntryID_ERROR, ("[Applet Mode]"_i18n).c_str());
|
||||
}
|
||||
|
||||
#undef draw
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "i18n.hpp"
|
||||
|
||||
#include <minIni.h>
|
||||
#include <nanovg/stb_image.h>
|
||||
#include <stb_image.h>
|
||||
#include <cstring>
|
||||
#include <minizip/unzip.h>
|
||||
#include <yyjson.h>
|
||||
@@ -68,7 +68,7 @@ auto apiBuildUrlListInternal(const Config& e, bool is_pack) -> std::string {
|
||||
if (is_pack) {
|
||||
cmd = "packList";
|
||||
// fields += ",themes{id,creator{display_name},details{name,description},last_updated,dl_count,like_count,target,preview{original,thumb}}";
|
||||
fields += ",themes{id, preview{thumb}}";
|
||||
fields += ",themes{id,preview{thumb}}";
|
||||
} else {
|
||||
cmd = "themeList";
|
||||
p0 += ",$target:String";
|
||||
@@ -92,7 +92,9 @@ auto apiBuildUrlListInternal(const Config& e, bool is_pack) -> std::string {
|
||||
json += ",\"query\":\"" + e.query + "\"";
|
||||
}
|
||||
|
||||
return api+"("+p0+"){"+cmd+"("+p1+")"+fields+"}}&variables={"+json+"}";
|
||||
json = curl::EscapeString('{'+json+'}');
|
||||
|
||||
return api+"("+p0+"){"+cmd+"("+p1+")"+fields+"}}&variables="+json;
|
||||
}
|
||||
|
||||
auto apiBuildUrlDownloadInternal(const std::string& id, bool is_pack) -> std::string {
|
||||
@@ -102,32 +104,10 @@ auto apiBuildUrlDownloadInternal(const std::string& id, bool is_pack) -> std::st
|
||||
// https://api.themezer.net/?query=query{downloadPack(id:"11"){filename,url,mimetype}}
|
||||
}
|
||||
|
||||
auto apiBuildUrlDownloadTheme(const ThemeEntry& e) -> std::string {
|
||||
return apiBuildUrlDownloadInternal(e.id, false);
|
||||
}
|
||||
|
||||
auto apiBuildUrlDownloadPack(const PackListEntry& e) -> std::string {
|
||||
return apiBuildUrlDownloadInternal(e.id, true);
|
||||
}
|
||||
|
||||
auto apiBuildFilePack(const PackListEntry& e) -> fs::FsPath {
|
||||
fs::FsPath path;
|
||||
std::snprintf(path, sizeof(path), "%s/%s By %s/", THEME_FOLDER, e.details.name.c_str(), e.creator.display_name.c_str());
|
||||
return path;
|
||||
}
|
||||
|
||||
#if 0
|
||||
auto apiBuildUrlPack(const PackListEntry& e) -> std::string {
|
||||
char url[2048];
|
||||
std::snprintf(url, sizeof(url), "https://api.themezer.net/?query=query($id:String!){pack(id:$id){id,creator{display_name},details{name,description},last_updated,categories,dl_count,like_count,themes{id,details{name},layout{id,details{name}},categories,target,preview{original,thumb},last_updated,dl_count,like_count}}}&variables={\"id\":\"%s\"}", e.id.c_str());
|
||||
return url;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto apiBuildUrlThemeList(const Config& e) -> std::string {
|
||||
return apiBuildUrlListInternal(e, false);
|
||||
}
|
||||
|
||||
auto apiBuildUrlListPacks(const Config& e) -> std::string {
|
||||
return apiBuildUrlListInternal(e, true);
|
||||
}
|
||||
@@ -171,7 +151,6 @@ auto loadThemeImage(ThemeEntry& e) -> bool {
|
||||
|
||||
if (!image.image) {
|
||||
log_write("failed to load image from file: %s\n", path.s);
|
||||
log_write("failed to load image from file: %s\n", path);
|
||||
return false;
|
||||
} else {
|
||||
// log_write("loaded image from file: %s\n", path);
|
||||
@@ -189,35 +168,18 @@ void from_json(yyjson_val* json, Creator& e) {
|
||||
void from_json(yyjson_val* json, Details& e) {
|
||||
JSON_OBJ_ITR(
|
||||
JSON_SET_STR(name);
|
||||
// JSON_SET_STR(description);
|
||||
);
|
||||
}
|
||||
|
||||
void from_json(yyjson_val* json, Preview& e) {
|
||||
JSON_OBJ_ITR(
|
||||
// JSON_SET_STR(original);
|
||||
JSON_SET_STR(thumb);
|
||||
);
|
||||
}
|
||||
|
||||
void from_json(yyjson_val* json, DownloadPack& e) {
|
||||
JSON_OBJ_ITR(
|
||||
JSON_SET_STR(filename);
|
||||
JSON_SET_STR(url);
|
||||
JSON_SET_STR(mimetype);
|
||||
);
|
||||
}
|
||||
|
||||
void from_json(yyjson_val* json, ThemeEntry& e) {
|
||||
JSON_OBJ_ITR(
|
||||
JSON_SET_STR(id);
|
||||
// JSON_SET_OBJ(creator);
|
||||
// JSON_SET_OBJ(details);
|
||||
// JSON_SET_STR(last_updated);
|
||||
// JSON_SET_UINT(dl_count);
|
||||
// JSON_SET_UINT(like_count);
|
||||
// JSON_SET_ARR_STR(categories);
|
||||
// JSON_SET_STR(target);
|
||||
JSON_SET_OBJ(preview);
|
||||
);
|
||||
}
|
||||
@@ -227,10 +189,6 @@ void from_json(yyjson_val* json, PackListEntry& e) {
|
||||
JSON_SET_STR(id);
|
||||
JSON_SET_OBJ(creator);
|
||||
JSON_SET_OBJ(details);
|
||||
// JSON_SET_STR(last_updated);
|
||||
// JSON_SET_ARR_STR(categories);
|
||||
// JSON_SET_UINT(dl_count);
|
||||
// JSON_SET_UINT(like_count);
|
||||
JSON_SET_ARR_OBJ(themes);
|
||||
);
|
||||
}
|
||||
@@ -246,7 +204,6 @@ void from_json(yyjson_val* json, Pagination& e) {
|
||||
|
||||
void from_json(const std::vector<u8>& data, DownloadPack& e) {
|
||||
JSON_INIT_VEC(data, "data");
|
||||
// JSON_GET_OBJ("downloadTheme");
|
||||
JSON_GET_OBJ("downloadPack");
|
||||
JSON_OBJ_ITR(
|
||||
JSON_SET_STR(filename);
|
||||
@@ -310,14 +267,14 @@ auto InstallTheme(ProgressBox* pbox, const PackListEntry& entry) -> bool {
|
||||
|
||||
// create directories
|
||||
fs::FsPath dir_path;
|
||||
std::snprintf(dir_path, sizeof(dir_path), "%s/%s - By %s", THEME_FOLDER, entry.details.name.c_str(), entry.creator.display_name.c_str());
|
||||
std::snprintf(dir_path, sizeof(dir_path), "%s/%s - By %s", THEME_FOLDER.s, entry.details.name.c_str(), entry.creator.display_name.c_str());
|
||||
fs.CreateDirectoryRecursively(dir_path);
|
||||
|
||||
// 3. extract the zip
|
||||
if (!pbox->ShouldExit()) {
|
||||
auto zfile = unzOpen64(zip_out);
|
||||
if (!zfile) {
|
||||
log_write("failed to open zip: %s\n", zip_out);
|
||||
log_write("failed to open zip: %s\n", zip_out.s);
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT(unzClose(zfile));
|
||||
@@ -352,19 +309,19 @@ auto InstallTheme(ProgressBox* pbox, const PackListEntry& entry) -> bool {
|
||||
|
||||
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);
|
||||
log_write("failed to create file: %s 0x%04X\n", file_path.s, 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);
|
||||
log_write("failed to open file: %s 0x%04X\n", file_path.s, 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);
|
||||
log_write("failed to set file size: %s 0x%04X\n", file_path.s, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -382,7 +339,7 @@ auto InstallTheme(ProgressBox* pbox, const PackListEntry& entry) -> bool {
|
||||
}
|
||||
|
||||
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);
|
||||
log_write("failed to write file: %s 0x%04X\n", file_path.s, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -618,7 +575,6 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
}
|
||||
|
||||
const float xoff = (350 - 320) / 2;
|
||||
const float yoff = (350 - 320) / 2;
|
||||
|
||||
// lazy load image
|
||||
if (e.themes.size()) {
|
||||
@@ -638,7 +594,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
switch (image.state) {
|
||||
case ImageDownloadState::None: {
|
||||
const auto path = apiBuildIconCache(theme);
|
||||
log_write("downloading theme!: %s\n", path);
|
||||
log_write("downloading theme!: %s\n", path.s);
|
||||
|
||||
const auto url = theme.preview.thumb;
|
||||
log_write("downloading url: %s\n", url.c_str());
|
||||
@@ -647,6 +603,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
curl::Url{url},
|
||||
curl::Path{path},
|
||||
curl::Flags{curl::Flag_Cache},
|
||||
curl::StopToken{this->GetToken()},
|
||||
curl::OnComplete{[this, &image](auto& result) {
|
||||
if (result.success) {
|
||||
image.state = ImageDownloadState::Done;
|
||||
@@ -733,6 +690,7 @@ void Menu::PackListDownload() {
|
||||
curl::Url{packList_url},
|
||||
curl::Path{packlist_path},
|
||||
curl::Flags{curl::Flag_Cache},
|
||||
curl::StopToken{this->GetToken()},
|
||||
curl::OnComplete{[this, page_index](auto& result){
|
||||
log_write("got themezer data\n");
|
||||
if (!result.success) {
|
||||
@@ -757,8 +715,8 @@ void Menu::PackListDownload() {
|
||||
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);
|
||||
log_write("a.pagination.page_count: %u\n", a.pagination.page_count);
|
||||
log_write("a.pagination.page: %zu\n", a.pagination.page);
|
||||
log_write("a.pagination.page_count: %zu\n", a.pagination.page_count);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace sphaira::ui::gfx {
|
||||
namespace {
|
||||
|
||||
static constexpr std::array buttons = {
|
||||
constexpr std::array buttons = {
|
||||
std::pair{Button::A, "\uE0E0"},
|
||||
std::pair{Button::B, "\uE0E1"},
|
||||
std::pair{Button::X, "\uE0E2"},
|
||||
@@ -60,18 +60,18 @@ void drawRectOutlineInternal(NVGcontext* vg, const Theme* theme, float size, con
|
||||
float gradientX, gradientY, color;
|
||||
getHighlightAnimation(&gradientX, &gradientY, &color);
|
||||
|
||||
const auto strokeWidth = 5.0;
|
||||
const auto strokeWidth = 5.F;
|
||||
auto v2 = v;
|
||||
v2.x -= strokeWidth / 2.0;
|
||||
v2.y -= strokeWidth / 2.0;
|
||||
v2.x -= strokeWidth / 2.F;
|
||||
v2.y -= strokeWidth / 2.F;
|
||||
v2.w += strokeWidth;
|
||||
v2.h += strokeWidth;
|
||||
const auto corner_radius = 0.5;
|
||||
const auto corner_radius = 0.5F;
|
||||
|
||||
const auto shadow_width = 2.0f;
|
||||
const auto shadow_offset = 10.0f;
|
||||
const auto shadow_feather = 10.0f;
|
||||
const auto shadow_opacity = 128.0f;
|
||||
const auto shadow_width = 2.F;
|
||||
const auto shadow_offset = 10.F;
|
||||
const auto shadow_feather = 10.F;
|
||||
const auto shadow_opacity = 128.F;
|
||||
|
||||
// Shadow
|
||||
NVGpaint shadowPaint = nvgBoxGradient(vg,
|
||||
@@ -136,15 +136,6 @@ void drawRectOutlineInternal(NVGcontext* vg, const Theme* theme, float size, con
|
||||
nvgFill(vg);
|
||||
}
|
||||
|
||||
void drawRectOutlineInternal(NVGcontext* vg, const Theme* theme, float size, const Vec4& v, const NVGpaint& p) {
|
||||
const auto corner_radius = 0.5;
|
||||
drawRectOutlineInternal(vg, theme, size, v);
|
||||
nvgBeginPath(vg);
|
||||
nvgRoundedRect(vg, v.x, v.y, v.w, v.h, corner_radius);
|
||||
nvgFillPaint(vg, p);
|
||||
nvgFill(vg);
|
||||
}
|
||||
|
||||
void drawTextIntenal(NVGcontext* vg, const Vec2& v, float size, const char* str, const char* end, int align, const NVGcolor& c) {
|
||||
nvgBeginPath(vg);
|
||||
nvgFontSize(vg, size);
|
||||
@@ -173,20 +164,6 @@ void drawTextArgs(NVGcontext* vg, float x, float y, float size, int align, const
|
||||
drawText(vg, x, y, size, buffer, nullptr, align, c);
|
||||
}
|
||||
|
||||
static void drawImageInternal(NVGcontext* vg, const Vec4& v, int texture, int rounded = 0) {
|
||||
const auto paint = nvgImagePattern(vg, v.x, v.y, v.w, v.h, 0, texture, 1.f);
|
||||
// drawRect(vg, x, y, w, h, paint);
|
||||
nvgBeginPath(vg);
|
||||
// nvgRect(vg, x, y, w, h);
|
||||
if (rounded == 0) {
|
||||
nvgRect(vg, v.x, v.y, v.w, v.h);
|
||||
} else {
|
||||
nvgRoundedRect(vg, v.x, v.y, v.w, v.h, rounded);
|
||||
}
|
||||
nvgFillPaint(vg, paint);
|
||||
nvgFill(vg);
|
||||
}
|
||||
|
||||
void drawImage(NVGcontext* vg, const Vec4& v, int texture) {
|
||||
const auto paint = nvgImagePattern(vg, v.x, v.y, v.w, v.h, 0, texture, 1.f);
|
||||
drawRect(vg, v, paint, false);
|
||||
|
||||
@@ -31,7 +31,6 @@ 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;
|
||||
@@ -52,9 +51,7 @@ ProgressBox::ProgressBox(const std::string& title, ProgressBoxCallback callback,
|
||||
}
|
||||
|
||||
ProgressBox::~ProgressBox() {
|
||||
mutexLock(&m_mutex);
|
||||
m_exit_requested = true;
|
||||
mutexUnlock(&m_mutex);
|
||||
m_stop_source.request_stop();
|
||||
|
||||
if (R_FAILED(threadWaitForExit(&m_thread))) {
|
||||
log_write("failed to join thread\n");
|
||||
@@ -101,7 +98,7 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
|
||||
gfx::drawTextArgs(vg, center_x, m_pos.y + 60, 25, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), title.c_str());
|
||||
if (!transfer.empty()) {
|
||||
gfx::drawTextArgs(vg, center_x, prog_bar.y - 15 - 20 * 1.5, 20, NVG_ALIGN_CENTER, theme->GetColour(ThemeEntryID_TEXT), "%s", transfer.c_str());
|
||||
gfx::drawTextArgs(vg, center_x, prog_bar.y - 15 - 20 * 1.5F, 20, NVG_ALIGN_CENTER, theme->GetColour(ThemeEntryID_TEXT), "%s", transfer.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,16 +122,11 @@ auto ProgressBox::UpdateTransfer(s64 offset, s64 size) -> ProgressBox& {
|
||||
}
|
||||
|
||||
void ProgressBox::RequestExit() {
|
||||
mutexLock(&m_mutex);
|
||||
m_exit_requested = true;
|
||||
mutexUnlock(&m_mutex);
|
||||
m_stop_source.request_stop();
|
||||
}
|
||||
|
||||
auto ProgressBox::ShouldExit() -> bool {
|
||||
mutexLock(&m_mutex);
|
||||
const auto exit_requested = m_exit_requested;
|
||||
mutexUnlock(&m_mutex);
|
||||
return exit_requested;
|
||||
return m_stop_source.stop_requested();
|
||||
}
|
||||
|
||||
auto ProgressBox::CopyFile(const fs::FsPath& src_path, const fs::FsPath& dst_path) -> Result {
|
||||
|
||||
@@ -9,9 +9,9 @@ namespace sphaira::ui {
|
||||
ScrollableText::ScrollableText(const std::string& text, float x, float y, float y_clip, float w, float font_size)
|
||||
: m_font_size{font_size}
|
||||
, m_y_off_base{y}
|
||||
, m_y_off{y}
|
||||
, m_clip_y{y_clip}
|
||||
, m_end_w{w}
|
||||
, m_y_off{y}
|
||||
{
|
||||
SetActions(
|
||||
std::make_pair(Button::LS_DOWN, Action{[this](){
|
||||
|
||||
@@ -274,7 +274,7 @@ auto Sidebar::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
gfx::drawRect(vg, m_pos, theme->GetColour(ThemeEntryID_SIDEBAR));
|
||||
gfx::drawText(vg, m_title_pos, m_title_size, theme->GetColour(ThemeEntryID_TEXT), m_title.c_str());
|
||||
if (!m_sub.empty()) {
|
||||
gfx::drawTextArgs(vg, m_pos.x + m_pos.w - 30.f, m_title_pos.y + 10.f, 18, NVG_ALIGN_TOP | NVG_ALIGN_RIGHT, theme->GetColour(ThemeEntryID_TEXT), m_sub.c_str());
|
||||
gfx::drawTextArgs(vg, m_pos.x + m_pos.w - 30.f, m_title_pos.y + 10.f, 16, NVG_ALIGN_TOP | NVG_ALIGN_RIGHT, theme->GetColour(ThemeEntryID_TEXT_INFO), m_sub.c_str());
|
||||
}
|
||||
gfx::drawRect(vg, m_top_bar, theme->GetColour(ThemeEntryID_LINE));
|
||||
gfx::drawRect(vg, m_bottom_bar, theme->GetColour(ThemeEntryID_LINE));
|
||||
@@ -328,7 +328,7 @@ void Sidebar::SetupButtons() {
|
||||
RemoveActions();
|
||||
|
||||
// add entry actions
|
||||
for (const auto& [button, action] : m_items[m_index]->GetActions()) {
|
||||
for (const auto& [button, action] : m_items[m_index]->GetActions()) {
|
||||
SetAction(button, action);
|
||||
}
|
||||
|
||||
|
||||