19 Commits
0.3.0 ... 0.4.0

Author SHA1 Message Date
ITotalJustice
b2e032ca1f add updater, remove white theme (it was unfished), remove more dead code, bump version for release 2024-12-17 23:21:30 +00:00
Battosai94
0585bec6e5 Update fr.json (#17)
Minor changes to FR translation
2024-12-17 23:15:53 +00:00
Yorunokyujitsu
11f4f3000a Korean translation (#19) 2024-12-17 23:15:30 +00:00
Sanras
474843915c Add OLED Black Theme (#20) 2024-12-17 23:12:37 +00:00
Ny'hrarr
3146b951f2 New icon (#18)
* New icon
2024-12-17 22:54:45 +00:00
ITotalJustice
2db9b72416 improve fr translation (credit to @Battosai94)
see: https://github.com/ITotalJustice/sphaira/issues/2#issuecomment-2549628348
2024-12-17 21:12:26 +00:00
ITotalJustice
9b4710d386 show time in main menu, change battery % symbol to use a small version 2024-12-17 21:04:36 +00:00
Aurelia
ddf5b94f4d i18n: improve de locale (#16)
revised some wording, false friends and neologisms
2024-12-17 20:50:15 +00:00
ITotalJustice
ecb2567757 add workflow 2024-12-17 20:05:33 +00:00
ITotalJustice
b59a162473 fix not exiting to home menu whilst replacing hbmenu 2024-12-17 19:54:49 +00:00
ITotalJustice
ef5ff520d1 Add files via upload (#11)
Corrected Japanese translation

Co-authored-by: HoRy205 <101063179+HoRy205@users.noreply.github.com>
2024-12-17 16:56:00 +00:00
ITotalJustice
433c2e220c fix overlapping text in fs
fixes #13
2024-12-17 16:21:14 +00:00
do-kiss
98ad2f485b Chinese translation (#12)
Chinese translation
2024-12-17 16:05:07 +00:00
ITotalJustice
9966e57e12 option to install nro from fs, swap LR display, load translations locally, fix scrolling sound, add file name to rename swkdb
- reduce nxlink svcsleep to reduce latency between polling.
- translations can now be loaded from /config/sphaira/i18n/name.json, this is to help aid those creating translations.
- swap LR position in display. the fix is a hack, but it'll do for now.
- sound effects are now consistent throught the app.
- renaming a file will now show the current file name in swkbd, makes it easier to rename from config.ini.template -> config.ini
- removed some dead code that was unused.
- add credits to the readme.
- speed up playlog ini parsing by browsing the ini rather that doing a query for each entry.
2024-12-17 16:03:05 +00:00
shadow2560
c11990e1bd Improve french language (#10)
Signed-off-by: shadow2560 <24191064+shadow2560@users.noreply.github.com>
2024-12-17 13:29:18 +00:00
LNLenost
9b1c0226e1 Added Italian translation (#8)
* Delete assets/romfs/i18n/it.json (old Italian translation)

* Uploaded new, correct Italian translations.
2024-12-17 13:13:36 +00:00
WE1ZARD
f38a671a7f use translatation from native Chinese (#3) 2024-12-17 12:58:17 +00:00
Ny'hrarr
fe952dc9f2 Improve Portuguese translation (#1)
* Improved Portuguese translation

* Update pt.json
2024-12-17 01:49:21 +00:00
ITotalJustice
d063ffcb20 remove old theme entries, make "Back" be the default hovered option when installing forwarder 2024-12-16 22:51:58 +00:00
36 changed files with 679 additions and 585 deletions

35
.github/workflows/build_presets.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: build
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
preset: [Release, RelWithDebInfo, MinSizeRel, Debug]
runs-on: ${{ matrix.os }}
container: devkitpro/devkita64:latest
steps:
- uses: actions/checkout@v3
# fetch latest cmake
- uses: lukka/get-cmake@latest
- name: Configure CMake
run: |
cmake --preset ${{ matrix.preset }}
- name: Build
run: cmake --build --preset ${{ matrix.preset }} --parallel 4
- uses: actions/upload-artifact@master
with:
name: sphaira-${{ matrix.preset }}
path: build/${{ matrix.preset }}/sphaira.nro

View File

@@ -51,3 +51,5 @@ see `assets/romfs/assoc/` for more examples of file assoc entries
- libpulsar - libpulsar
- minIni - minIni
- gbatemp - gbatemp
- hb-appstore
- everyone who has contributed to this project!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -14,57 +14,57 @@
"Info": "Info", "Info": "Info",
"Delete": "Löschen", "Delete": "Löschen",
"Hide Sphaira": "Sphaira verstecken", "Hide Sphaira": "Sphaira verstecken",
"Are you sure you want to delete ": "Sind Sie sicher, dass Sie löschen möchten? ", "Are you sure you want to delete ": "Mit dem Löschvorgang fortfahren?",
"Install Forwarder": "Weiterleitung installieren", "Install Forwarder": "Forwarder installieren",
"WARNING: Installing forwarders will lead to a ban!": "ACHTUNG: Der Einbau von Forwardern führt zu einem Verbot!", "WARNING: Installing forwarders will lead to a ban!": "ACHTUNG: Die Installation von Forwardern führt zu einem Ban!",
"Back": "Zurück", "Back": "Zurück",
"Install": "Installieren", "Install": "Installieren",
"Fs": "Fs", "Fs": "Fs",
"App": "App", "App": "App",
"Menu": "Speisekarte", "Menu": "Menu",
"Homebrew": "Homebrew", "Homebrew": "Homebrew",
"FileBrowser": "DateiBrowser", "FileBrowser": "DateiBrowser",
"Open": "Offen", "Open": "Öffnen",
"Theme Options": "Themenoptionen", "Theme Options": "Themenoptionen",
"Select Theme": "Wählen Sie Thema aus", "Select Theme": "Wählen Sie Theme aus",
"Shuffle": "Shuffle", "Shuffle": "Shuffle",
"Music": "Musik", "Music": "Musik",
"Show Hidden": "Versteckt anzeigen", "Show Hidden": "Versteckte anzeigen",
"Folders First": "Ordner zuerst", "Folders First": "Ordner zuerst",
"Hidden Last": "Zuletzt versteckt", "Hidden Last": "Zuletzt versteckt",
"Yes": "Ja", "Yes": "Ja",
"No": "NEIN", "No": "Nein",
"Network Options": "Netzwerkoptionen", "Network Options": "Netzwerkoptionen",
"Nxlink": "Nxlink", "Nxlink": "Nxlink",
"Check for update": "Suchen Sie nach Updates", "Check for update": "Nach Updates suchen",
"File Options": "Dateioptionen", "File Options": "Dateioptionen",
"Cut": "Schneiden", "Cut": "Ausschneiden",
"Copy": "Kopie", "Copy": "Kopieren",
"Rename": "Umbenennen", "Rename": "Umbenennen",
"Advanced Options": "Datei erstellen", "Advanced Options": "Erweiterte Optionen",
"Create File": "Datei erstellen", "Create File": "Datei erstellen",
"Create Folder": "Ordner erstellen", "Create Folder": "Ordner erstellen",
"View as text": "Als Text anzeigen", "View as text": "Als Text anzeigen",
"View as text (unfinished)": "Als Text anzeigen (unvollendet)", "View as text (unfinished)": "Als Text anzeigen (unfertig)",
"Set Archive Bit": "Archivbit setzen", "Set Archive Bit": "Archivbit setzen",
"AppStore Options": "AppStore-Optionen", "AppStore Options": "AppStore-Optionen",
"All": "Alle", "All": "Alle",
"Games": "Spiele", "Games": "Spiele",
"Emulators": "Emulatoren", "Emulators": "Emulatoren",
"Tools": "Werkzeuge", "Tools": "Werkzeuge",
"Advanced": "Fortschrittlich", "Advanced": "Erweitert",
"Themes": "Themen", "Themes": "Themes",
"Legacy": "Vermächtnis", "Legacy": "Legacy",
"Misc": "Sonstiges", "Misc": "Sonstiges",
"Downloads": "Downloads", "Downloads": "Downloads",
"Filter": "Filter", "Filter": "Filter",
"Search": "Suchen", "Search": "Suchen",
"Menu Options": "Menüoptionen", "Menu Options": "Menüoptionen",
"Header": "Kopfzeile", "Header": "Header",
"Theme": "Thema", "Theme": "Theme",
"Network": "Netzwerk", "Network": "Netzwerk",
"Logging": "Protokollierung", "Logging": "Logging",
"Enabled": "Ermöglicht", "Enabled": "Aktiviert",
"Disabled": "Deaktiviert", "Disabled": "Deaktiviert",
"Replace hbmenu on exit": "Ersetzen Sie hbmenu beim Beenden", "Replace hbmenu on exit": "Ersetzen Sie hbmenu beim Beenden",
"Misc Options": "Verschiedene Optionen", "Misc Options": "Verschiedene Optionen",
@@ -98,16 +98,16 @@
"80x60": "80x60", "80x60": "80x60",
"40x30": "40x30", "40x30": "40x30",
"20x15": "20x15", "20x15": "20x15",
"Controller": "Regler", "Controller": "Controller",
"Rotation": "Drehung", "Rotation": "Drehung",
"Colour": "Farbe", "Colour": "Farbe",
"Light Target": "Leichtes Ziel", "Light Target": "Leichtes Ziel",
"Gain": "Gewinnen", "Gain": "Gain",
"Negative Image": "Negatives Bild", "Negative Image": "Negatives Bild",
"Format": "Format", "Format": "Format",
"Trimming Format": "Zuschneideformat", "Trimming Format": "Zuschneideformat",
"External Light Filter": "Externer Lichtfilter", "External Light Filter": "Externer Lichtfilter",
"Load Default": "Standard laden", "Load Default": "Standardoptionen laden",
"No Internet": "Kein Internet", "No Internet": "Kein Internet",
"[Applet Mode]": "[Applet-Modus]", "[Applet Mode]": "[Applet-Modus]",
"Language": "Sprache" "Language": "Sprache"

View File

@@ -1,52 +1,52 @@
{ {
"Launch": "Lancement", "Launch": "Exécuter",
"Options": "Possibilités", "Options": "Options",
"Homebrew Options": "Options de brassage maison", "Homebrew Options": "Options Homebrew",
"Sort By": "Trier par", "Sort By": "Tri Par",
"Sort Options": "Options de tri", "Sort Options": "Options de Tri",
"Updated": "Mis à jour", "Updated": "Mis à jour",
"Size": "Taille", "Size": "Taille",
"Alphabetical": "Alphabétique", "Alphabetical": "Alphabétique",
"Decending": "Décroissant", "Decending": "Décroissant",
"Ascending": "Ascendant", "Ascending": "Croissant",
"Sort": "Trier", "Sort": "Tri",
"Order": "Commande", "Order": "Ordre",
"Info": "Informations", "Info": "Info.",
"Delete": "Supprimer", "Delete": "Supprimer",
"Hide Sphaira": "Masquer Sphaira", "Hide Sphaira": "Masquer Sphaira",
"Are you sure you want to delete ": "Etes-vous sûr de vouloir supprimer ", "Are you sure you want to delete ": "Êtes-vous sûr de vouloir supprimer ",
"Install Forwarder": "Installer le redirecteur", "Install Forwarder": "Installer le Forwarder",
"WARNING: Installing forwarders will lead to a ban!": "ATTENTION : L'installation de transitaires entraînera une interdiction !", "WARNING: Installing forwarders will lead to a ban!": "ATTENTION: L'installation de forwarders entraînera un ban!",
"Back": "Dos", "Back": "Retour",
"Install": "Installer", "Install": "Installer",
"Fs": "Fs", "Fs": "Fs",
"App": "Application", "App": "App.",
"Menu": "Menu", "Menu": "Menu",
"Homebrew": "Homebrew", "Homebrew": "Homebrew",
"FileBrowser": "Navigateur de fichiers", "FileBrowser": "Navigateur de Fichiers",
"Open": "Ouvrir", "Open": "Ouvrir",
"Theme Options": "Options de thème", "Theme Options": "Options de Thème",
"Select Theme": "Sélectionnez un thème", "Select Theme": "Choisir un Thème",
"Shuffle": "Mélanger", "Shuffle": "Aléatoire",
"Music": "Musique", "Music": "Musique",
"Show Hidden": "Afficher masqué", "Show Hidden": "Afficher Masqués",
"Folders First": "Les dossiers d'abord", "Folders First": "Dossiers en Premier",
"Hidden Last": "Dernier caché", "Hidden Last": "Masqués en Dernier",
"Yes": "Oui", "Yes": "Oui",
"No": "Non", "No": "Non",
"Network Options": "Options réseau", "Network Options": "Options Réseau",
"Nxlink": "Nxlien", "Nxlink": "Nxlink",
"Check for update": "Vérifier la mise à jour", "Check for update": "Vérification d'une mise à jour",
"File Options": "Options de fichier", "File Options": "Options de Fichier",
"Cut": "Couper", "Cut": "Couper",
"Copy": "Copie", "Copy": "Copier",
"Rename": "Rebaptiser", "Rename": "Renommer",
"Advanced Options": "Créer un fichier", "Advanced Options": "Options Avancées",
"Create File": "Créer un fichier", "Create File": "Créer un Fichier",
"Create Folder": "Créer un dossier", "Create Folder": "Créer un Dossier",
"View as text": "Afficher sous forme de texte", "View as text": "Afficher sous forme de texte",
"View as text (unfinished)": "Afficher sous forme de texte (inachevé)", "View as text (unfinished)": "Afficher sous forme de texte (inachevé)",
"Set Archive Bit": "Définir le bit d'archive", "Set Archive Bit": "Définir le Bit d'Archive",
"AppStore Options": "Options de l'AppStore", "AppStore Options": "Options de l'AppStore",
"All": "Tous", "All": "Tous",
"Games": "Jeux", "Games": "Jeux",
@@ -54,45 +54,45 @@
"Tools": "Outils", "Tools": "Outils",
"Advanced": "Avancé", "Advanced": "Avancé",
"Themes": "Thèmes", "Themes": "Thèmes",
"Legacy": "Héritage", "Legacy": "Legacy",
"Misc": "Divers", "Misc": "Divers",
"Downloads": "Téléchargements", "Downloads": "Téléchargements",
"Filter": "Filtre", "Filter": "Filtre",
"Search": "Recherche", "Search": "Recherche",
"Menu Options": "Options des menus", "Menu Options": "Options des Menus",
"Header": "En-tête", "Header": "En-tête",
"Theme": "Thème", "Theme": "Thème",
"Network": "Réseau", "Network": "Réseau",
"Logging": "Enregistrement", "Logging": "Journalisation",
"Enabled": "Activé", "Enabled": "Activé(e)",
"Disabled": "Désactivé", "Disabled": "Désactivé(e)",
"Replace hbmenu on exit": "Remplacer hbmenu à la sortie", "Replace hbmenu on exit": "Remplacer hbmenu en sortie",
"Misc Options": "Diverses options", "Misc Options": "Options Diverses",
"Themezer": "Thème", "Themezer": "Themezer",
"Irs": "Irs", "Irs": "Irs",
"Web": "Web", "Web": "Web",
"Download": "Télécharger", "Download": "Télécharger",
"Next Page": "Page suivante", "Next Page": "Page Suiv.",
"Prev Page": "Page précédente", "Prev Page": "Page Préc.",
"Pad ": "Tampon ", "Pad ": "Manette ",
" (Unconnected)": " (Sans rapport)", " (Unconnected)": " (Non connectée)",
"HandHeld": "Portable", "HandHeld": "Portable",
" (Available)": " (Disponible)", " (Available)": " (Disponible)",
"0 (Sideways)": "0 (latéralement)", "0 (Sideways)": "0 (Paysage)",
"90 (Flat)": "90 (plat)", "90 (Flat)": "90 (Portrait)",
"180 (-Sideways)": "180 (-Côté)", "180 (-Sideways)": "180 (-Paysage)",
"270 (Upside down)": "270 (à l'envers)", "270 (Upside down)": "270 (Inversé)",
"Grey": "Gris", "Grey": "Gris",
"Ironbow": "Arc de fer", "Ironbow": "Ironbow",
"Green": "Vert", "Green": "Vert",
"Red": "Rouge", "Red": "Rouge",
"Blue": "Bleu", "Blue": "Bleu",
"All leds": "Toutes les LED", "All leds": "Toutes les LED",
"Bright group": "Groupe lumineux", "Bright group": "Groupe lumineux",
"Dim group": "Groupe de gradation", "Dim group": "Groupe sombre",
"None": "Aucun", "None": "Aucun",
"Normal image": "Image normale", "Normal image": "Image normale",
"Negative image": "Image négative", "Negative image": "Négatif",
"320x240": "320x240", "320x240": "320x240",
"160x120": "160x120", "160x120": "160x120",
"80x60": "80x60", "80x60": "80x60",
@@ -101,13 +101,13 @@
"Controller": "Contrôleur", "Controller": "Contrôleur",
"Rotation": "Rotation", "Rotation": "Rotation",
"Colour": "Couleur", "Colour": "Couleur",
"Light Target": "Cible légère", "Light Target": "Luminosité",
"Gain": "Gagner", "Gain": "Gain",
"Negative Image": "Image négative", "Negative Image": "Négatif",
"Format": "Format", "Format": "Format",
"Trimming Format": "Format de découpage", "Trimming Format": "Format de Découpe",
"External Light Filter": "Filtre de lumière externe", "External Light Filter": "Filtre de Lumière Externe",
"Load Default": "Charger par défaut", "Load Default": "Charger par Défaut",
"No Internet": "Pas d'Internet", "No Internet": "Pas d'Internet",
"[Applet Mode]": "[Mode Applet]", "[Applet Mode]": "[Mode Applet]",
"Language": "Langue" "Language": "Langue"

View File

@@ -1,69 +1,69 @@
{ {
"Launch": "Lancio", "Launch": "Lancia",
"Options": "Opzioni", "Options": "Opzioni",
"Homebrew Options": "Opzioni per l'homebrew", "Homebrew Options": "Opzioni Homebrew",
"Sort By": "Ordina per", "Sort By": "Ordina per",
"Sort Options": "Opzioni di ordinamento", "Sort Options": "Opzioni filtro",
"Updated": "Aggiornato", "Updated": "Aggiornato",
"Size": "Misurare", "Size": "Misurare",
"Alphabetical": "Alfabetico", "Alphabetical": "Alfabetico",
"Decending": "Decrescente", "Decending": "Decrescente",
"Ascending": "Ascendente", "Ascending": "Crescente",
"Sort": "Ordinare", "Sort": "Riordina",
"Order": "Ordine", "Order": "Ordina",
"Info": "Informazioni", "Info": "Informazioni",
"Delete": "Eliminare", "Delete": "Elimina",
"Hide Sphaira": "Nascondi Sphaira", "Hide Sphaira": "Nascondi Sphaira",
"Are you sure you want to delete ": "Sei sicuro di voler eliminare? ", "Are you sure you want to delete ": "Sei sicuro di voler eliminare? ",
"Install Forwarder": "Installa lo spedizioniere", "Install Forwarder": "Installa forwarder",
"WARNING: Installing forwarders will lead to a ban!": "ATTENZIONE: l'installazione di forwarder porterà al ban!", "WARNING: Installing forwarders will lead to a ban!": "ATTENZIONE: l'installazione di forwarder porterà al ban!",
"Back": "Indietro", "Back": "Indietro",
"Install": "Installare", "Install": "Installa",
"Fs": "Fs", "Fs": "Fs",
"App": "App", "App": "App",
"Menu": "Menu", "Menu": "Menu",
"Homebrew": "Birra fatta in casa", "Homebrew": "Homebrew",
"FileBrowser": "FileBrowser", "FileBrowser": "FileBrowser",
"Open": "Aprire", "Open": "Apri",
"Theme Options": "Opzioni del tema", "Theme Options": "Opzioni tema",
"Select Theme": "Seleziona Tema", "Select Theme": "Seleziona tema",
"Shuffle": "Mescola", "Shuffle": "Mescola",
"Music": "Musica", "Music": "Musica",
"Show Hidden": "Mostra nascosto", "Show Hidden": "Mostra nascosto",
"Folders First": "Prima le cartelle", "Folders First": "Prima le cartelle",
"Hidden Last": "Ultimo nascosto", "Hidden Last": "Ultimo nascosto",
"Yes": "SÌ", "Yes": "Sì",
"No": "NO", "No": "No",
"Network Options": "Opzioni di rete", "Network Options": "Opzioni di rete",
"Nxlink": "Nxlink", "Nxlink": "Nxlink",
"Check for update": "Controlla l'aggiornamento", "Check for update": "Controlla aggiornamenti",
"File Options": "Opzioni file", "File Options": "Opzioni file",
"Cut": "Taglio", "Cut": "Taglia",
"Copy": "Copia", "Copy": "Copia",
"Rename": "Rinominare", "Rename": "Rinomina",
"Advanced Options": "Crea file", "Advanced Options": "Opzioni avanzate",
"Create File": "Crea file", "Create File": "Crea file",
"Create Folder": "Crea cartella", "Create Folder": "Crea cartella",
"View as text": "Visualizza come testo", "View as text": "Visualizza come testo",
"View as text (unfinished)": "Visualizza come testo (non finito)", "View as text (unfinished)": "Visualizza come testo (non finito)",
"Set Archive Bit": "Imposta bit di archivio", "Set Archive Bit": "Imposta Archive Bit",
"AppStore Options": "Opzioni dell'App Store", "AppStore Options": "Opzioni dell'App Store",
"All": "Tutto", "All": "Tutto",
"Games": "Giochi", "Games": "Giochi",
"Emulators": "Emulatori", "Emulators": "Emulatori",
"Tools": "Utensili", "Tools": "Strumenti",
"Advanced": "Avanzato", "Advanced": "Avanzato",
"Themes": "Temi", "Themes": "Temi",
"Legacy": "Eredità", "Legacy": "Legacy",
"Misc": "Varie", "Misc": "Varie",
"Downloads": "Download", "Downloads": "Download",
"Filter": "Filtro", "Filter": "Filtro",
"Search": "Ricerca", "Search": "Ricerca",
"Menu Options": "Opzioni del menu", "Menu Options": "Opzioni menu",
"Header": "Intestazione", "Header": "Intestazione",
"Theme": "Tema", "Theme": "Tema",
"Network": "Rete", "Network": "Rete",
"Logging": "Registrazione", "Logging": "Logging",
"Enabled": "Abilitato", "Enabled": "Abilitato",
"Disabled": "Disabilitato", "Disabled": "Disabilitato",
"Replace hbmenu on exit": "Sostituisci hbmenu all'uscita", "Replace hbmenu on exit": "Sostituisci hbmenu all'uscita",
@@ -71,19 +71,19 @@
"Themezer": "Themezer", "Themezer": "Themezer",
"Irs": "Irs", "Irs": "Irs",
"Web": "Rete", "Web": "Rete",
"Download": "Scaricamento", "Download": "Download",
"Next Page": "Pagina successiva", "Next Page": "Pagina successiva",
"Prev Page": "Pagina precedente", "Prev Page": "Pagina precedente",
"Pad ": "Pad ", "Pad ": "Pad ",
" (Unconnected)": " (Non connesso)", " (Unconnected)": " (Non connesso)",
"HandHeld": "Tenuto in mano", "HandHeld": "HandHeld",
" (Available)": " (Disponibile)", " (Available)": " (Disponibile)",
"0 (Sideways)": "0 (lateralmente)", "0 (Sideways)": "0 (Di lato)",
"90 (Flat)": "90 (Piatto)", "90 (Flat)": "90 (Piatto)",
"180 (-Sideways)": "180 (-lateralmente)", "180 (-Sideways)": "180 (-Di lato)",
"270 (Upside down)": "270 (Capovolto)", "270 (Upside down)": "270 (Capovolto)",
"Grey": "Grigio", "Grey": "Grigio",
"Ironbow": "Arco di ferro", "Ironbow": "Ironbow",
"Green": "Verde", "Green": "Verde",
"Red": "Rosso", "Red": "Rosso",
"Blue": "Blu", "Blue": "Blu",
@@ -98,7 +98,7 @@
"80x60": "80x60", "80x60": "80x60",
"40x30": "40x30", "40x30": "40x30",
"20x15": "20×15", "20x15": "20×15",
"Controller": "Controllore", "Controller": "Controller",
"Rotation": "Rotazione", "Rotation": "Rotazione",
"Colour": "Colore", "Colour": "Colore",
"Light Target": "Bersaglio leggero", "Light Target": "Bersaglio leggero",

View File

@@ -1,44 +1,44 @@
{ {
"Launch": "打ち上げ", "Launch": "起動",
"Options": "オプション", "Options": "設定",
"Homebrew Options": "自作オプション", "Homebrew Options": "Homebrew設定",
"Sort By": "並べ替え", "Sort By": "並べ替え",
"Sort Options": "並べ替えオプション", "Sort Options": "並べ替え設定",
"Updated": "更新されました", "Updated": "最近使った順",
"Size": "サイズ", "Size": "ファイルサイズ",
"Alphabetical": "アルファベット順", "Alphabetical": "アルファベット順",
"Decending": "降順", "Decending": "降順",
"Ascending": "上昇", "Ascending": "上昇",
"Sort": "選別", "Sort": "並べ替え",
"Order": "注文", "Order": "順番",
"Info": "情報", "Info": "情報",
"Delete": "消去", "Delete": "消去",
"Hide Sphaira": "ハイド・スファイラ", "Hide Sphaira": "Sphairaを非表示",
"Are you sure you want to delete ": "削除してもよろしいですか ", "Are you sure you want to delete ": "消去してもよろしいですか ",
"Install Forwarder": "フォワーダーのインストール", "Install Forwarder": "Forwarderのインストール",
"WARNING: Installing forwarders will lead to a ban!": "警告: フォワーダーをインストールすると禁止されます。", "WARNING: Installing forwarders will lead to a ban!": "警告: ForwarderをインストールするとBANされます。",
"Back": "戻る", "Back": "戻る",
"Install": "インストール", "Install": "インストール",
"Fs": "Fs", "Fs": "ファイル",
"App": "アプリ", "App": "アプリ",
"Menu": "メニュー", "Menu": "メニュー",
"Homebrew": "自作", "Homebrew": "Homebrew",
"FileBrowser": "ファイルブラウザ", "FileBrowser": "ファイルブラウザ",
"Open": "開ける", "Open": "開",
"Theme Options": "テーマのオプション", "Theme Options": "テーマ設定",
"Select Theme": "テーマの選択", "Select Theme": "テーマを選ぶ",
"Shuffle": "シャッフル", "Shuffle": "シャッフル",
"Music": "音楽", "Music": "BGM",
"Show Hidden": "非表示を表示", "Show Hidden": "非表示ファイルを表示",
"Folders First": "フォルダーを最初に", "Folders First": "フォルダーを優先",
"Hidden Last": "隠された最後", "Hidden Last": "非表示ファイルを劣後",
"Yes": "はい", "Yes": "はい",
"No": "いいえ", "No": "いいえ",
"Network Options": "ネットワークオプション", "Network Options": "ネットワーク設定",
"Nxlink": "Nxlink", "Nxlink": "Nxlink",
"Check for update": "アップデート確認する", "Check for update": "アップデート確認",
"File Options": "ファイルオプション", "File Options": "ファイル設定",
"Cut": "カット", "Cut": "切り取り",
"Copy": "コピー", "Copy": "コピー",
"Rename": "名前の変更", "Rename": "名前の変更",
"Advanced Options": "ファイルの作成", "Advanced Options": "ファイルの作成",
@@ -47,30 +47,30 @@
"View as text": "テキストとして表示", "View as text": "テキストとして表示",
"View as text (unfinished)": "テキストとして表示 (未完成)", "View as text (unfinished)": "テキストとして表示 (未完成)",
"Set Archive Bit": "アーカイブビットの設定", "Set Archive Bit": "アーカイブビットの設定",
"AppStore Options": "AppStore オプション", "AppStore Options": "AppStoreの設定",
"All": "全て", "All": "全て",
"Games": "ゲーム", "Games": "ゲーム",
"Emulators": "エミュレータ", "Emulators": "エミュレータ",
"Tools": "ツール", "Tools": "ツール",
"Advanced": "高度な", "Advanced": "高度な",
"Themes": "テーマ", "Themes": "テーマ",
"Legacy": "遺産", "Legacy": "レガシー",
"Misc": "その他", "Misc": "その他",
"Downloads": "ダウンロード", "Downloads": "ダウンロード",
"Filter": "フィルター", "Filter": "フィルター",
"Search": "検索", "Search": "検索",
"Menu Options": "メニューオプション", "Menu Options": "メニュー設定",
"Header": "ヘッダ", "Header": "ヘッダ",
"Theme": "テーマ", "Theme": "テーマ",
"Network": "ネットワーク", "Network": "ネットワーク",
"Logging": "ロギング", "Logging": "ログの取得",
"Enabled": "有効", "Enabled": "有効",
"Disabled": "無効", "Disabled": "無効",
"Replace hbmenu on exit": "終了時に hbmenu を置き換える", "Replace hbmenu on exit": "終了時に hbmenu を置き換える",
"Misc Options": "その他のオプション", "Misc Options": "その他",
"Themezer": "テーマ設定者", "Themezer": "Themezer",
"Irs": "イルス", "Irs": "Joy-Con IRカメラ",
"Web": "ウェブ", "Web": "ウェブブラウザ",
"Download": "ダウンロード", "Download": "ダウンロード",
"Next Page": "次のページ", "Next Page": "次のページ",
"Prev Page": "前のページ", "Prev Page": "前のページ",
@@ -98,7 +98,7 @@
"80x60": "80×60", "80x60": "80×60",
"40x30": "40×30", "40x30": "40×30",
"20x15": "20x15", "20x15": "20x15",
"Controller": "コントローラ", "Controller": "コントローラ",
"Rotation": "回転", "Rotation": "回転",
"Colour": "色", "Colour": "色",
"Light Target": "ライトターゲット", "Light Target": "ライトターゲット",
@@ -109,6 +109,6 @@
"External Light Filter": "外光フィルター", "External Light Filter": "外光フィルター",
"Load Default": "デフォルトをロード", "Load Default": "デフォルトをロード",
"No Internet": "インターネットなし", "No Internet": "インターネットなし",
"[Applet Mode]": "アプレットモード]", "[Applet Mode]": "Appletモード]",
"Language": "言語" "Language": "言語"
} }

View File

@@ -1,114 +1,114 @@
{ {
"Launch": "시작하다", "Launch": "실행",
"Options": "옵션", "Options": "설정",
"Homebrew Options": "홈브류 옵션", "Homebrew Options": "홈브류 설정",
"Sort By": "정렬 기준", "Sort By": "정렬",
"Sort Options": "정렬 옵션", "Sort Options": "정렬 설정",
"Updated": "업데이트", "Updated": "업데이트",
"Size": "크기", "Size": "크기",
"Alphabetical": "알파벳순", "Alphabetical": "알파벳순",
"Decending": "내림차순", "Decending": "내림차순",
"Ascending": "오름차순", "Ascending": "오름차순",
"Sort": "류", "Sort": "류",
"Order": "주문하다", "Order": "정렬",
"Info": "정보", "Info": "정보",
"Delete": "삭제", "Delete": "삭제",
"Hide Sphaira": "스파이라 숨기기", "Hide Sphaira": "Sphaira 숨기기",
"Are you sure you want to delete ": "삭제하시겠습니까? ", "Are you sure you want to delete ": "정말 삭제하시겠습니까? ",
"Install Forwarder": "포워더 설치", "Install Forwarder": "바로가기 설치",
"WARNING: Installing forwarders will lead to a ban!": "경고: 전달자를 설치하면 금지됩니다!", "WARNING: Installing forwarders will lead to a ban!": "주의: 바로가기 설치시 BAN 위험이 있습니다!",
"Back": "뒤쪽에", "Back": "뒤",
"Install": "설치하다", "Install": "설치",
"Fs": "Fs", "Fs": "파일 탐색기",
"App": "앱", "App": "앱",
"Menu": "메뉴", "Menu": "메뉴",
"Homebrew": "홈브류", "Homebrew": "홈브류",
"FileBrowser": "파일브라우저", "FileBrowser": "파일 탐색기",
"Open": "열려 있는", "Open": "열",
"Theme Options": "테마 옵션", "Theme Options": "테마 설정",
"Select Theme": "테마 선택", "Select Theme": "테마 선택",
"Shuffle": "혼합", "Shuffle": "셔플",
"Music": "음악", "Music": "BGM",
"Show Hidden": "숨겨진 표시", "Show Hidden": "숨겨진 항목 표시",
"Folders First": "폴더 먼저", "Folders First": "폴더 우선 정렬",
"Hidden Last": "숨겨진 마지막", "Hidden Last": "숨겨진 항목 후순 정렬",
"Yes": "예", "Yes": "예",
"No": "아니요", "No": "아니요",
"Network Options": "네트워크 옵션", "Network Options": "네트워크 설정",
"Nxlink": "Nxlink", "Nxlink": "Nxlink",
"Check for update": "업데이트 확인", "Check for update": "업데이트 확인",
"File Options": "파일 옵션", "File Options": "파일 설정",
"Cut": "자르다", "Cut": "잘라내기",
"Copy": "복사", "Copy": "복사",
"Rename": "이름 바꾸기", "Rename": "이름 바꾸기",
"Advanced Options": "파일 생성", "Advanced Options": "고급 설정",
"Create File": "파일 생성", "Create File": "파일 생성",
"Create Folder": "폴더 생성", "Create Folder": "폴더",
"View as text": "텍스트로 보기", "View as text": "텍스트로 보기",
"View as text (unfinished)": "텍스트로 보기(미완성)", "View as text (unfinished)": "텍스트로 보기 (미완성)",
"Set Archive Bit": "보관 비트 설정", "Set Archive Bit": "아카이브 비트 설정",
"AppStore Options": "앱스토어 옵션", "AppStore Options": "앱스토어 설정",
"All": "모두", "All": "전체",
"Games": "계략", "Games": "게임",
"Emulators": "에뮬레이터", "Emulators": "에뮬레이터",
"Tools": "도구", "Tools": "도구",
"Advanced": "고급", "Advanced": "고급",
"Themes": "테마", "Themes": "테마",
"Legacy": "유산", "Legacy": "레거시",
"Misc": "기타", "Misc": "기타",
"Downloads": "다운로드", "Downloads": "다운로드",
"Filter": "필터", "Filter": "필터",
"Search": "찾다", "Search": "검색",
"Menu Options": "메뉴 옵션", "Menu Options": "메뉴",
"Header": "헤더", "Header": "헤더",
"Theme": "주제", "Theme": "테마",
"Network": "회로망", "Network": "네트워크",
"Logging": "벌채 반출", "Logging": "로깅",
"Enabled": "활성화됨", "Enabled": "",
"Disabled": "장애가 있는", "Disabled": "",
"Replace hbmenu on exit": "종료 시 hbmenu 교체", "Replace hbmenu on exit": "종료 시 hbmenu 교체",
"Misc Options": "기타 옵션", "Misc Options": "기타",
"Themezer": "테마저", "Themezer": "Themezer",
"Irs": "국세청", "Irs": "Joy-Con IR 카메라",
"Web": "편물", "Web": "웹 브라우저",
"Download": "다운로드", "Download": "다운로드",
"Next Page": "다음 페이지", "Next Page": "다음 페이지",
"Prev Page": "이전 페이지", "Prev Page": "이전 페이지",
"Pad ": "인주 ", "Pad ": "Joy-Con ",
" (Unconnected)": " (연결되지 않음)", " (Unconnected)": " (연결음)",
"HandHeld": "휴대용", "HandHeld": "본체 연결",
" (Available)": " (사용 가능)", " (Available)": " (사용 가능)",
"0 (Sideways)": "0(가로)", "0 (Sideways)": "0 (좌회전)",
"90 (Flat)": "90(플랫)", "90 (Flat)": "90 (정방향)",
"180 (-Sideways)": "180 (-옆으로)", "180 (-Sideways)": "180 (우회전)",
"270 (Upside down)": "270 (거꾸로)", "270 (Upside down)": "270 (역전)",
"Grey": "회색", "Grey": "그레이",
"Ironbow": "아이언보우", "Ironbow": "아이언보우",
"Green": "녹색", "Green": "그린",
"Red": "빨간색", "Red": "레드",
"Blue": "파란색", "Blue": "블루",
"All leds": "모든 LED", "All leds": "모든 LED 켜기",
"Bright group": "밝은 그룹", "Bright group": "Bright LED 켜기",
"Dim group": "희미한 그룹", "Dim group": "Dim LED 켜기",
"None": "없음", "None": "LED 끄기",
"Normal image": "일반 이미지", "Normal image": "일반",
"Negative image": "부정적인 이미지", "Negative image": "반전",
"320x240": "320x240", "320x240": "320×240",
"160x120": "160x120", "160x120": "160×120",
"80x60": "80x60", "80x60": "80×60",
"40x30": "40x30", "40x30": "40×30",
"20x15": "20x15", "20x15": "20×15",
"Controller": "제어 장치", "Controller": "컨트롤러",
"Rotation": "회전", "Rotation": "화면 회전",
"Colour": "색상", "Colour": "팔레트",
"Light Target": "라이트 타겟", "Light Target": "반사 표적",
"Gain": "얻다", "Gain": "대비",
"Negative Image": "네거티브 이미지", "Negative Image": "화상 이미지",
"Format": "체재", "Format": "해상도",
"Trimming Format": "트리밍 형식", "Trimming Format": "트리밍 해상도",
"External Light Filter": "외부 조명 필터", "External Light Filter": "외부 조명 필터",
"Load Default": "기본값 로드", "Load Default": "기본값으로 설정",
"No Internet": "인터넷 없음", "No Internet": "네트워크 연결 없음",
"[Applet Mode]": "[애플릿 모드]", "[Applet Mode]": "[애플릿 모드]",
"Language": "언어" "Language": "언어"
} }

View File

@@ -1,7 +1,7 @@
{ {
"Launch": "Lançar", "Launch": "Iniciar",
"Options": "Opções", "Options": "Opções",
"Homebrew Options": "Opções de fermentação caseira", "Homebrew Options": "Opções do Homebrew",
"Sort By": "Ordenar por", "Sort By": "Ordenar por",
"Sort Options": "Opções de classificação", "Sort Options": "Opções de classificação",
"Updated": "Atualizado", "Updated": "Atualizado",
@@ -14,19 +14,19 @@
"Info": "Informações", "Info": "Informações",
"Delete": "Excluir", "Delete": "Excluir",
"Hide Sphaira": "Esconder Sphaira", "Hide Sphaira": "Esconder Sphaira",
"Are you sure you want to delete ": "Tem certeza de que deseja excluir ", "Are you sure you want to delete ": "Excluir ",
"Install Forwarder": "Instalar encaminhador", "Install Forwarder": "Instalar forwarder",
"WARNING: Installing forwarders will lead to a ban!": "AVISO: A instalação de encaminhadores levará ao banimento!", "WARNING: Installing forwarders will lead to a ban!": "AVISO: Isso pode resultar em um banimento!",
"Back": "Voltar", "Back": "Voltar",
"Install": "Instalar", "Install": "Instalar",
"Fs": "Fs", "Fs": "Fs",
"App": "Aplicativo", "App": "Aplicativo",
"Menu": "Menu", "Menu": "Menu",
"Homebrew": "Cerveja caseira", "Homebrew": "Homebrew",
"FileBrowser": "Navegador de arquivos", "FileBrowser": "Navegador de arquivos",
"Open": "Abrir", "Open": "Abrir",
"Theme Options": "Opções de tema", "Theme Options": "Opções de tema",
"Select Theme": "Selecione o tema", "Select Theme": "Selecionar tema",
"Shuffle": "Embaralhar", "Shuffle": "Embaralhar",
"Music": "Música", "Music": "Música",
"Show Hidden": "Mostrar oculto", "Show Hidden": "Mostrar oculto",
@@ -36,10 +36,10 @@
"No": "Não", "No": "Não",
"Network Options": "Opções de rede", "Network Options": "Opções de rede",
"Nxlink": "Nxlink", "Nxlink": "Nxlink",
"Check for update": "Verifique se há atualização", "Check for update": "Verificar se há atualização",
"File Options": "Opções de arquivo", "File Options": "Opções de arquivo",
"Cut": "Corte", "Cut": "Cortar",
"Copy": "Cópia", "Copy": "Copiar",
"Rename": "Renomear", "Rename": "Renomear",
"Advanced Options": "Criar arquivo", "Advanced Options": "Criar arquivo",
"Create File": "Criar arquivo", "Create File": "Criar arquivo",
@@ -56,39 +56,39 @@
"Themes": "Temas", "Themes": "Temas",
"Legacy": "Legado", "Legacy": "Legado",
"Misc": "Diversos", "Misc": "Diversos",
"Downloads": "Transferências", "Downloads": "Downloads",
"Filter": "Filtro", "Filter": "Filtro",
"Search": "Procurar", "Search": "Procurar",
"Menu Options": "Opções de cardápio", "Menu Options": "Opções do menu",
"Header": "Cabeçalho", "Header": "Cabeçalho",
"Theme": "Tema", "Theme": "Tema",
"Network": "Rede", "Network": "Rede",
"Logging": "Registro", "Logging": "Logging",
"Enabled": "Habilitado", "Enabled": "Habilitado",
"Disabled": "Desabilitado", "Disabled": "Desabilitado",
"Replace hbmenu on exit": "Substitua hbmenu ao sair", "Replace hbmenu on exit": "Substitua hbmenu ao sair",
"Misc Options": "Opções diversas", "Misc Options": "Opções diversas",
"Themezer": "Temazer", "Themezer": "Themezer",
"Irs": "Receita Federal", "Irs": "Irs",
"Web": "Rede", "Web": "Rede",
"Download": "Download", "Download": "Download",
"Next Page": "Próxima página", "Next Page": "Próxima página",
"Prev Page": "Página anterior", "Prev Page": "Página anterior",
"Pad ": "Almofada ", "Pad ": "Pad ",
" (Unconnected)": " (Desconectado)", " (Unconnected)": " (Desconectado)",
"HandHeld": "Portátil", "HandHeld": "Portátil",
" (Available)": " (Disponível)", " (Available)": " (Disponível)",
"0 (Sideways)": "0 (lateralmente)", "0 (Sideways)": "0 (Lateralmente)",
"90 (Flat)": "90 (plano)", "90 (Flat)": "90 (plano)",
"180 (-Sideways)": "180 (-lateralmente)", "180 (-Sideways)": "180 (-Lateralmente)",
"270 (Upside down)": "270 (de cabeça para baixo)", "270 (Upside down)": "270 (De cabeça para baixo)",
"Grey": "Cinza", "Grey": "Cinza",
"Ironbow": "Arco de Ferro", "Ironbow": "Arco de ferro",
"Green": "Verde", "Green": "Verde",
"Red": "Vermelho", "Red": "Vermelho",
"Blue": "Azul", "Blue": "Azul",
"All leds": "Todos os LEDs", "All leds": "Todos os LEDs",
"Bright group": "Grupo brilhante", "Bright group": "Grupo claro",
"Dim group": "Grupo escuro", "Dim group": "Grupo escuro",
"None": "Nenhum", "None": "Nenhum",
"Normal image": "Imagem normal", "Normal image": "Imagem normal",
@@ -98,7 +98,7 @@
"80x60": "80x60", "80x60": "80x60",
"40x30": "40x30", "40x30": "40x30",
"20x15": "20x15", "20x15": "20x15",
"Controller": "Controlador", "Controller": "Controle",
"Rotation": "Rotação", "Rotation": "Rotação",
"Colour": "Cor", "Colour": "Cor",
"Light Target": "Alvo leve", "Light Target": "Alvo leve",
@@ -109,6 +109,6 @@
"External Light Filter": "Filtro de luz externo", "External Light Filter": "Filtro de luz externo",
"Load Default": "Carregar padrão", "Load Default": "Carregar padrão",
"No Internet": "Sem Internet", "No Internet": "Sem Internet",
"[Applet Mode]": "[Modo miniaplicativo]", "[Applet Mode]": "[Modo Applet]",
"Language": "Idioma" "Language": "Idioma"
} }

View File

@@ -1,98 +1,99 @@
{ {
"Launch": "发射", "Launch": "启动",
"Options": "选项", "Options": "选项",
"Homebrew Options": "自制选项", "Homebrew Options": "插件选项",
"Sort By": "排序方式", "Sort By": "排序方式",
"Sort Options": "排序选项", "Sort Options": "排序选项",
"Updated": "已更新", "Updated": "最近使用",
"Size": "尺寸", "Size": "大小",
"Alphabetical": "按字母顺序", "Alphabetical": "按字母顺序",
"Decending": "降序", "Decending": "降序",
"Ascending": "升序", "Ascending": "升序",
"Sort": "种类", "Sort": "排序",
"Order": "命令", "Order": "顺序",
"Info": "信息", "Info": "信息",
"Delete": "删除", "Delete": "删除",
"Hide Sphaira": "隐藏斯菲拉", "Hide Sphaira": "在插件列表中隐藏Sphaira",
"Are you sure you want to delete ": "您确定要删除吗 ", "Are you sure you want to delete ": "您确定要删除吗 ",
"Install Forwarder": "安装转发器", "Install Forwarder": "安装前端应用",
"WARNING: Installing forwarders will lead to a ban!": "警告:安装转发器将导致禁止", "WARNING: Installing forwarders will lead to a ban!": "警告:安装前端应用可能导致ban机",
"Back": "后退", "Back": "返回",
"Install": "安装", "Install": "安装",
"Fs": "FS", "Fs": "文件系统",
"App": "应用程序", "App": "插件",
"Menu": "菜单", "Menu": "菜单",
"Homebrew": "自制", "Homebrew": "插件列表",
"FileBrowser": "文件浏览器", "AppStore": "插件商店",
"FileBrowser": "文件浏览",
"Open": "打开", "Open": "打开",
"Theme Options": "主题选项", "Theme Options": "主题选项",
"Select Theme": "选择主题", "Select Theme": "选择主题",
"Shuffle": "随机播放", "Shuffle": "随机播放",
"Music": "音乐", "Music": "音乐",
"Show Hidden": "显示隐藏", "Show Hidden": "显示隐藏项目",
"Folders First": "文件夹优先", "Folders First": "文件夹靠前",
"Hidden Last": "隐藏后", "Hidden Last": "隐藏项目置后",
"Yes": "是", "Yes": "是",
"No": "", "No": "",
"Network Options": "网络选项", "Network Options": "网络选项",
"Nxlink": "恩克斯联", "Nxlink": "Nxlink开发工具",
"Check for update": "检查更新", "Check for update": "检查更新",
"File Options": "文件选项", "File Options": "文件选项",
"Cut": "切", "Cut": "切",
"Copy": "复制", "Copy": "复制",
"Rename": "重命名", "Rename": "重命名",
"Advanced Options": "创建文件", "Advanced Options": "高级选项",
"Create File": "建文件", "Create File": "建文件",
"Create Folder": "建文件夹", "Create Folder": "建文件夹",
"View as text": "以文本形式查看", "View as text": "以文本形式查看",
"View as text (unfinished)": "以文本形式查看(未完", "View as text (unfinished)": "以文本形式查看(未完",
"Set Archive Bit": "设置存档", "Set Archive Bit": "设置存档标志",
"AppStore Options": "应用商店选项", "AppStore Options": "插件商店选项",
"All": "全部", "All": "全部",
"Games": "游戏", "Games": "游戏",
"Emulators": "模拟器", "Emulators": "模拟器",
"Tools": "工具", "Tools": "工具",
"Advanced": "先进的", "Advanced": "高级",
"Themes": "主题", "Themes": "主题",
"Legacy": "遗产", "Legacy": "可更新",
"Misc": "杂项", "Misc": "杂项",
"Downloads": "下载", "Downloads": "下载",
"Filter": "筛选", "Filter": "筛选",
"Search": "搜索", "Search": "搜索",
"Menu Options": "菜单选项", "Menu Options": "菜单选项",
"Header": "标", "Header": "标",
"Theme": "主题", "Theme": "主题",
"Network": "网络", "Network": "网络",
"Logging": "记录", "Logging": "日志",
"Enabled": "启用", "Enabled": "启用",
"Disabled": "残疾人", "Disabled": "禁用",
"Replace hbmenu on exit": "退出替换 hbmenu", "Replace hbmenu on exit": "退出后用Sphaira替换hbmenu",
"Misc Options": "其他选项", "Misc Options": "杂项设置",
"Themezer": "主题", "Themezer": "在线主题",
"Irs": "国税局", "Irs": "红外成像",
"Web": "网", "Web": "网页浏览器",
"Download": "下载", "Download": "下载",
"Next Page": "下一页", "Next Page": "下一页",
"Prev Page": "上一页", "Prev Page": "上一页",
"Pad ": "软垫 ", "Pad ": "手柄 ",
" (Unconnected)": " (未连接)", " (Unconnected)": " (未连接)",
"HandHeld": "手持式", "HandHeld": "手持式",
" (Available)": " (可用的)", " (Available)": " (可用的)",
"0 (Sideways)": "0(横向)", "0 (Sideways)": "0",
"90 (Flat)": "90(平)", "90 (Flat)": "90",
"180 (-Sideways)": "180-横向)", "180 (-Sideways)": "180",
"270 (Upside down)": "270(颠倒)", "270 (Upside down)": "270",
"Grey": "灰色", "Grey": "灰色",
"Ironbow": "铁弓", "Ironbow": "紫黄",
"Green": "绿色", "Green": "绿色",
"Red": "红色", "Red": "红色",
"Blue": "蓝色", "Blue": "蓝色",
"All leds": "所有 LED", "All leds": "全部",
"Bright group": "光明集团", "Bright group": "亮色组",
"Dim group": "暗组", "Dim group": "暗组",
"None": "没有任何", "None": "",
"Normal image": "正常图像", "Normal image": "正常图像",
"Negative image": "负像", "Negative image": "负片图像",
"320x240": "320x240", "320x240": "320x240",
"160x120": "160x120", "160x120": "160x120",
"80x60": "80x60", "80x60": "80x60",
@@ -101,14 +102,14 @@
"Controller": "控制器", "Controller": "控制器",
"Rotation": "旋转", "Rotation": "旋转",
"Colour": "颜色", "Colour": "颜色",
"Light Target": "光目标", "Light Target": "光目标",
"Gain": "获得", "Gain": "增益",
"Negative Image": "负面形象", "Negative Image": "负片图像",
"Format": "格式", "Format": "格式",
"Trimming Format": "剪格式", "Trimming Format": "剪格式",
"External Light Filter": "外部滤光片", "External Light Filter": "外部光滤镜",
"Load Default": "加载默认值", "Load Default": "加载默认值",
"No Internet": "没有互联网", "No Internet": "网络未连接",
"[Applet Mode]": "[小程序模式]", "[Applet Mode]": "[小程序模式]",
"Language": "语言" "Language": "语言"
} }

View File

@@ -1,18 +1,18 @@
[meta] [meta]
name="White not finished" name=OLED Black
author=TotalJustice author=iTotalJustice/Sanras
version=1.0.0 version=1.0.0
preview=romfs:/theme/preview.jpg preview=romfs:/theme/preview.jpg
[theme] [theme]
background=0xEBEBEBff background=0x000000ff
cursor=romfs:/theme/cursor.png cursor=romfs:/theme/cursor.png
cursor_drag=romfs:/theme/cursor_drag.png cursor_drag=romfs:/theme/cursor_drag.png
grid=0x46464630 grid=0x46464640
selected=0x464646ff selected=0x323232ff
selected_overlay=0x00ffc8ff selected_overlay=0x00ffc8ff
text=0x2D2D2Dff text=0xfbfbfbff
text_selected=0x3A50F0ff text_selected=0x00ffc8ff
icon_audio=romfs:/theme/icon_audio.png icon_audio=romfs:/theme/icon_audio.png
icon_video=romfs:/theme/icon_video.png icon_video=romfs:/theme/icon_video.png

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
set(sphaira_VERSION 0.3.0) set(sphaira_VERSION 0.4.0)
project(sphaira project(sphaira
VERSION ${sphaira_VERSION} VERSION ${sphaira_VERSION}

View File

@@ -7,6 +7,8 @@ namespace sphaira::i18n {
bool init(long index); bool init(long index);
void exit(); void exit();
std::string get(const char* str);
} // namespace sphaira::i18n } // namespace sphaira::i18n
inline namespace literals { inline namespace literals {

View File

@@ -14,20 +14,20 @@ struct Hbini {
}; };
struct NroEntry { struct NroEntry {
fs::FsPath path; fs::FsPath path{};
s64 size; s64 size{};
NacpStruct nacp; NacpStruct nacp{};
std::vector<u8> icon; std::vector<u8> icon{};
u64 icon_size; u64 icon_size{};
u64 icon_offset; u64 icon_offset{};
FsTimeStampRaw timestamp; FsTimeStampRaw timestamp{};
Hbini hbini; Hbini hbini{};
int image; // nvg image int image{}; // nvg image
int x,y,w,h; // image int x,y,w,h{}; // image
bool is_nacp_valid; bool is_nacp_valid{};
auto GetName() const -> const char* { auto GetName() const -> const char* {
return nacp.lang[0].name; return nacp.lang[0].name;

View File

@@ -5,7 +5,7 @@
namespace sphaira::swkbd { namespace sphaira::swkbd {
Result ShowText(std::string& out, const char* guide = nullptr, s64 len_min = -1, s64 len_max = -1); Result ShowText(std::string& out, const char* guide = nullptr, const char* initial = nullptr, s64 len_min = -1, s64 len_max = FS_MAX_PATH);
Result ShowNumPad(s64& out, const char* guide = nullptr, s64 len_min = -1, s64 len_max = -1); Result ShowNumPad(s64& out, const char* guide = nullptr, const char* initial = nullptr, s64 len_min = -1, s64 len_max = FS_MAX_PATH);
} // namespace sphaira::swkbd } // namespace sphaira::swkbd

View File

@@ -36,6 +36,9 @@ struct Menu final : MenuBase {
return m_entries; return m_entries;
} }
static Result InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector<u8>& icon);
static Result InstallHomebrewFromPath(const fs::FsPath& path);
private: private:
static constexpr inline const char* INI_SECTION = "homebrew"; static constexpr inline const char* INI_SECTION = "homebrew";

View File

@@ -7,6 +7,15 @@
namespace sphaira::ui::menu::main { namespace sphaira::ui::menu::main {
enum class UpdateState {
// still downloading json from github
Pending,
// no update available.
None,
// update available!
Update,
};
// this holds 2 menus and allows for switching between them // this holds 2 menus and allows for switching between them
struct MainMenu final : Widget { struct MainMenu final : Widget {
MainMenu(); MainMenu();
@@ -31,7 +40,7 @@ private:
std::string m_update_url{}; std::string m_update_url{};
std::string m_update_version{}; std::string m_update_version{};
std::string m_update_description{}; std::string m_update_description{};
bool m_update_avaliable{}; UpdateState m_update_state{UpdateState::Pending};
}; };
} // namespace sphaira::ui::menu::main } // namespace sphaira::ui::menu::main

View File

@@ -144,10 +144,8 @@ struct ElementEntry {
enum ThemeEntryID { enum ThemeEntryID {
ThemeEntryID_BACKGROUND, ThemeEntryID_BACKGROUND,
ThemeEntryID_LOGO,
ThemeEntryID_GRID, ThemeEntryID_GRID,
ThemeEntryID_GRID_HOVER,
ThemeEntryID_SELECTED, ThemeEntryID_SELECTED,
ThemeEntryID_SELECTED_OVERLAY, ThemeEntryID_SELECTED_OVERLAY,
ThemeEntryID_TEXT, ThemeEntryID_TEXT,

View File

@@ -26,16 +26,6 @@ struct Widget : public Object {
return m_focus; return m_focus;
} }
// void PushWidget(std::shared_ptr<Widget> widget);
// void PopWidget();
void SetParent(Widget* parent) {
m_parent = parent;
}
auto GetParent() -> Widget* {
return m_parent;
}
auto HasAction(Button button) const -> bool; auto HasAction(Button button) const -> bool;
void SetAction(Button button, Action action); void SetAction(Button button, Action action);
void SetActions(std::same_as<std::pair<Button, Action>> auto ...args) { void SetActions(std::same_as<std::pair<Button, Action>> auto ...args) {
@@ -66,8 +56,6 @@ struct Widget : public Object {
using Actions = std::map<Button, Action>; using Actions = std::map<Button, Action>;
// using Actions = std::unordered_map<Button, Action>; // using Actions = std::unordered_map<Button, Action>;
Actions m_actions; Actions m_actions;
Widget* m_parent{};
// std::vector<std::shared_ptr<Widget>> widgets;
bool m_focus{false}; bool m_focus{false};
bool m_pop{false}; bool m_pop{false};
}; };

View File

@@ -524,7 +524,7 @@ void App::Poll() {
m_touch_info.finger_id = touch_state.touches[0].finger_id; m_touch_info.finger_id = touch_state.touches[0].finger_id;
m_touch_info.is_touching = true; m_touch_info.is_touching = true;
m_touch_info.is_tap = true; m_touch_info.is_tap = true;
PlaySoundEffect(SoundEffect_Limit); // PlaySoundEffect(SoundEffect_Limit);
} else if (touch_state.count >= 1 && m_touch_info.is_touching && m_touch_info.finger_id == touch_state.touches[0].finger_id) { } else if (touch_state.count >= 1 && m_touch_info.is_touching && m_touch_info.finger_id == touch_state.touches[0].finger_id) {
m_touch_info.prev_x = m_touch_info.cur_x; m_touch_info.prev_x = m_touch_info.cur_x;
m_touch_info.prev_y = m_touch_info.cur_y; m_touch_info.prev_y = m_touch_info.cur_y;
@@ -597,39 +597,6 @@ auto App::GetVg() -> NVGcontext* {
return g_app->vg; return g_app->vg;
} }
#if 0
void App::UpdateList() {
const auto index_copy = this->index;
const auto start_copy = this->start;
else if (controller.down) {
// todo: replace with actual focus
PlaySoundEffect(SoundEffect_Limit);
}
if (controller.any_direction()) {
if (index_copy != this->index) {
PlaySoundEffect(SoundEffect_Focus);
if (start_copy != this->start) {
// float r = randomGet64() % 100;
// float pitch = r / 100.0;
// plsrPlayerSetPitch(m_sound_ids[SoundEffect_Scroll], pitch);
PlaySoundEffect(SoundEffect_Scroll);
}
if (this->index == 0 || this->index == this->nro_entries.size() || ((controller.down & HidNpadButton_AnyLeft) && this->index && this->index % 3 == 0) || ((controller.down & HidNpadButton_AnyRight) && this->index && (this->index + 1) % 3 == 0)) {
PlaySoundEffect(SoundEffect_Limit);
}
} else {
const auto mask = HidNpadButton_AnyDown | HidNpadButton_AnyUp | HidNpadButton_AnyLeft | HidNpadButton_AnyRight;
if (controller.down & mask) {
PlaySoundEffect(SoundEffect_Limit);
}
}
}
}
#endif
void DrawElement(float x, float y, float w, float h, ThemeEntryID id) { void DrawElement(float x, float y, float w, float h, ThemeEntryID id) {
const auto& e = g_app->m_theme.elements[id]; const auto& e = g_app->m_theme.elements[id];
@@ -738,12 +705,8 @@ void App::LoadTheme(const fs::FsPath& path) {
app->PlaySoundEffect(SoundEffect_Music); app->PlaySoundEffect(SoundEffect_Music);
} }
} }
} else if (key == "logo") {
theme.elements[ThemeEntryID_LOGO] = app->LoadElement(value);
} else if (key == "grid") { } else if (key == "grid") {
theme.elements[ThemeEntryID_GRID] = app->LoadElement(value); theme.elements[ThemeEntryID_GRID] = app->LoadElement(value);
} else if (key == "grid_hover") {
theme.elements[ThemeEntryID_GRID_HOVER] = app->LoadElement(value);
} else if (key == "selected") { } else if (key == "selected") {
theme.elements[ThemeEntryID_SELECTED] = app->LoadElement(value); theme.elements[ThemeEntryID_SELECTED] = app->LoadElement(value);
} else if (key == "selected_overlay") { } else if (key == "selected_overlay") {
@@ -862,8 +825,8 @@ App::App(const char* argv0) {
m_app_path = argv0; m_app_path = argv0;
} }
// set pop-back if applet and we are hbmenu // set if we are hbmenu
if (!IsApplication() && IsHbmenu()) { if (IsHbmenu()) {
__nx_applet_exit_mode = 1; __nx_applet_exit_mode = 1;
} }
@@ -1083,6 +1046,29 @@ App::~App() {
} else { } else {
log_write("success with copying over root file!\n"); log_write("success with copying over root file!\n");
} }
} else if (IsHbmenu()) {
// check we have a version that's newer than current.
fs::FsNativeSd fs;
NacpStruct sphaira_nacp;
fs::FsPath sphaira_path = "/switch/sphaira/sphaira.nro";
Result rc;
rc = nro_get_nacp(sphaira_path, sphaira_nacp);
if (R_FAILED(rc) || std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
sphaira_path = "/switch/sphaira.nro";
rc = nro_get_nacp(sphaira_path, sphaira_nacp);
}
// found sphaira, now lets get compare version
if (R_SUCCEEDED(rc) && !std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
if (std::strcmp(APP_VERSION, sphaira_nacp.display_version) < 0) {
if (R_FAILED(rc = fs.copy_entire_file(GetExePath(), sphaira_path, true))) {
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", sphaira_path, rc, R_MODULE(rc), R_DESCRIPTION(rc));
} else {
log_write("success with updating hbmenu!\n");
}
}
}
} }
if (App::GetNxlinkEnable()) { if (App::GetNxlinkEnable()) {

View File

@@ -11,7 +11,7 @@ std::vector<u8> g_i18n_data;
yyjson_doc* json; yyjson_doc* json;
yyjson_val* root; yyjson_val* root;
std::string get(const char* str, size_t len) { std::string get_internal(const char* str, size_t len) {
if (!json || !root) { if (!json || !root) {
log_write("no json or root\n"); log_write("no json or root\n");
return str; return str;
@@ -78,8 +78,18 @@ bool init(long index) {
default: lang_name = "en"; break; default: lang_name = "en"; break;
} }
const fs::FsPath path = "romfs:/i18n/" + lang_name + ".json"; const fs::FsPath sdmc_path = "/config/sphaira/i18n/" + lang_name + ".json";
if (R_SUCCEEDED(fs::FsStdio().read_entire_file(path, g_i18n_data))) { const fs::FsPath romfs_path = "romfs:/i18n/" + lang_name + ".json";
fs::FsPath path = sdmc_path;
// try and load override translation first
Result rc = fs::FsNativeSd().read_entire_file(path, g_i18n_data);
if (R_FAILED(rc)) {
path = romfs_path;
rc = fs::FsStdio().read_entire_file(path, g_i18n_data);
}
if (R_SUCCEEDED(rc)) {
json = yyjson_read((const char*)g_i18n_data.data(), g_i18n_data.size(), YYJSON_READ_ALLOW_TRAILING_COMMAS|YYJSON_READ_ALLOW_COMMENTS|YYJSON_READ_ALLOW_INVALID_UNICODE); json = yyjson_read((const char*)g_i18n_data.data(), g_i18n_data.size(), YYJSON_READ_ALLOW_TRAILING_COMMAS|YYJSON_READ_ALLOW_COMMENTS|YYJSON_READ_ALLOW_INVALID_UNICODE);
if (json) { if (json) {
root = yyjson_doc_get_root(json); root = yyjson_doc_get_root(json);
@@ -107,12 +117,16 @@ void exit() {
g_i18n_data.clear(); g_i18n_data.clear();
} }
std::string get(const char* str) {
return get_internal(str, std::strlen(str));
}
} // namespace sphaira::i18n } // namespace sphaira::i18n
namespace literals { namespace literals {
std::string operator"" _i18n(const char* str, size_t len) { std::string operator"" _i18n(const char* str, size_t len) {
return sphaira::i18n::get(str, len); return sphaira::i18n::get_internal(str, len);
} }
} // namespace literals } // namespace literals

View File

@@ -71,7 +71,6 @@ auto nro_parse_internal(fs::FsNative& fs, const fs::FsPath& path, NroEntry& entr
} else { } else {
R_TRY(fsFileRead(&f, data.header.size + asset.nacp.offset, &entry.nacp, sizeof(entry.nacp), FsReadOption_None, &bytes_read)); R_TRY(fsFileRead(&f, data.header.size + asset.nacp.offset, &entry.nacp, sizeof(entry.nacp), FsReadOption_None, &bytes_read));
entry.is_nacp_valid = true; entry.is_nacp_valid = true;
log_write("got nacp\n");
} }
// lazy load the icons // lazy load the icons
@@ -241,7 +240,7 @@ auto nro_get_icon(const fs::FsPath& path) -> std::vector<u8> {
R_TRY_RESULT(fsFileRead(&f, data.header.size, &asset, sizeof(asset), FsReadOption_None, &bytes_read), {}); R_TRY_RESULT(fsFileRead(&f, data.header.size, &asset, sizeof(asset), FsReadOption_None, &bytes_read), {});
R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, {}); R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, {});
return nro_get_icon_internal(&f, asset.icon.size, asset.icon.offset); return nro_get_icon_internal(&f, asset.icon.size, data.header.size + asset.icon.offset);
} }
auto nro_get_nacp(const fs::FsPath& path, NacpStruct& nacp) -> Result { auto nro_get_nacp(const fs::FsPath& path, NacpStruct& nacp) -> Result {

View File

@@ -224,7 +224,7 @@ void loop(void* args) {
}; };
while (!g_quit) { while (!g_quit) {
svcSleepThread(33'333'333); svcSleepThread(1000000);
if (poll_network_change()) { if (poll_network_change()) {
continue; continue;
@@ -267,7 +267,7 @@ void loop(void* args) {
sockaddr_in sa_remote{}; sockaddr_in sa_remote{};
while (!g_quit) { while (!g_quit) {
svcSleepThread(33'333'333); svcSleepThread(10000);
if (poll_network_change()) { if (poll_network_change()) {
break; break;
@@ -297,7 +297,7 @@ void loop(void* args) {
} }
fs::FsPath name{}; fs::FsPath name{};
if (namelen > sizeof(name)) { if (namelen >= sizeof(name)) {
log_write("namelen is bigger than name: 0x%X\n", socketGetLastResult()); log_write("namelen is bigger than name: 0x%X\n", socketGetLastResult());
continue; continue;
} }

View File

@@ -10,7 +10,7 @@ struct Config {
bool numpad{}; bool numpad{};
}; };
Result ShowInternal(Config& cfg, const char* guide, s64 len_min, s64 len_max) { Result ShowInternal(Config& cfg, const char* guide, const char* initial, s64 len_min, s64 len_max) {
SwkbdConfig c; SwkbdConfig c;
R_TRY(swkbdCreate(&c, 0)); R_TRY(swkbdCreate(&c, 0));
swkbdConfigMakePresetDefault(&c); swkbdConfigMakePresetDefault(&c);
@@ -24,6 +24,10 @@ Result ShowInternal(Config& cfg, const char* guide, s64 len_min, s64 len_max) {
swkbdConfigSetGuideText(&c, guide); swkbdConfigSetGuideText(&c, guide);
} }
if (initial) {
swkbdConfigSetInitialText(&c, initial);
}
if (len_min >= 0) { if (len_min >= 0) {
swkbdConfigSetStringLenMin(&c, len_min); swkbdConfigSetStringLenMin(&c, len_min);
} }
@@ -37,16 +41,16 @@ Result ShowInternal(Config& cfg, const char* guide, s64 len_min, s64 len_max) {
} // namespace } // namespace
Result ShowText(std::string& out, const char* guide, s64 len_min, s64 len_max) { Result ShowText(std::string& out, const char* guide, const char* initial, s64 len_min, s64 len_max) {
Config cfg; Config cfg;
R_TRY(ShowInternal(cfg, guide, len_min, len_max)); R_TRY(ShowInternal(cfg, guide, initial, len_min, len_max));
out = cfg.out_text; out = cfg.out_text;
R_SUCCEED(); R_SUCCEED();
} }
Result ShowNumPad(s64& out, const char* guide, s64 len_min, s64 len_max) { Result ShowNumPad(s64& out, const char* guide, const char* initial, s64 len_min, s64 len_max) {
Config cfg; Config cfg;
R_TRY(ShowInternal(cfg, guide, len_min, len_max)); R_TRY(ShowInternal(cfg, guide, initial, len_min, len_max));
out = std::atoll(cfg.out_text); out = std::atoll(cfg.out_text);
R_SUCCEED(); R_SUCCEED();
} }

View File

@@ -661,11 +661,13 @@ EntryMenu::EntryMenu(Entry& entry, const LazyImage& default_icon, Menu& menu)
std::make_pair(Button::DPAD_DOWN | Button::RS_DOWN, Action{[this](){ std::make_pair(Button::DPAD_DOWN | Button::RS_DOWN, Action{[this](){
if (m_index < (m_options.size() - 1)) { if (m_index < (m_options.size() - 1)) {
SetIndex(m_index + 1); SetIndex(m_index + 1);
App::PlaySoundEffect(SoundEffect_Focus);
} }
}}), }}),
std::make_pair(Button::DPAD_UP | Button::RS_UP, Action{[this](){ std::make_pair(Button::DPAD_UP | Button::RS_UP, Action{[this](){
if (m_index != 0) { if (m_index != 0) {
SetIndex(m_index - 1); SetIndex(m_index - 1);
App::PlaySoundEffect(SoundEffect_Focus);
} }
}}), }}),
std::make_pair(Button::X, Action{"Options"_i18n, [this](){ std::make_pair(Button::X, Action{"Options"_i18n, [this](){
@@ -1436,7 +1438,7 @@ void Menu::Sort() {
char subheader[128]{}; char subheader[128]{};
std::snprintf(subheader, sizeof(subheader), "Sort: %s | Filter: %s | Order: %s", SORT_STR[m_sort], FILTER_STR[m_filter], ORDER_STR[m_order]); std::snprintf(subheader, sizeof(subheader), "Sort: %s | Filter: %s | Order: %s", i18n::get(SORT_STR[m_sort]), i18n::get(FILTER_STR[m_filter]), i18n::get(ORDER_STR[m_order]));
SetTitleSubHeading(subheader); SetTitleSubHeading(subheader);
std::sort(m_entries_current.begin(), m_entries_current.end(), sorter); std::sort(m_entries_current.begin(), m_entries_current.end(), sorter);

View File

@@ -1,4 +1,5 @@
#include "ui/menus/filebrowser.hpp" #include "ui/menus/filebrowser.hpp"
#include "ui/menus/homebrew.hpp"
#include "ui/sidebar.hpp" #include "ui/sidebar.hpp"
#include "ui/option_box.hpp" #include "ui/option_box.hpp"
#include "ui/popup_list.hpp" #include "ui/popup_list.hpp"
@@ -303,8 +304,6 @@ auto get_collection(fs::FsNative& fs, const fs::FsPath& path, const fs::FsPath&
R_SUCCEED(); R_SUCCEED();
} }
#if 1
// recursion
auto get_collections(fs::FsNative& fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out) -> Result { auto get_collections(fs::FsNative& fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out) -> Result {
// get a list of all the files / dirs // get a list of all the files / dirs
FsDirCollection collection; FsDirCollection collection;
@@ -323,48 +322,6 @@ auto get_collections(fs::FsNative& fs, const fs::FsPath& path, const fs::FsPath&
R_SUCCEED(); R_SUCCEED();
} }
#else
// normal
auto get_collections(fs::FsNative& fs, fs::FsPath path, fs::FsPath parent_name, FsDirCollections& out) -> Result {
// get a list of all the files / dirs
struct StoredStack {
fs::FsPath path;
fs::FsPath parent_name;
s64 index;
};
std::stack<StoredStack> stack;
s64 index{};
// std::vector<StoredStack> indexes;
while (true) {
FsDirCollection collection;
R_TRY(get_collection(fs, path, parent_name, collection, true, true, false));
out.emplace_back(collection);
if (collection.dirs.size()) {
stack.emplace(path, parent_name, index);
index = 0;
}
}
FsDirCollection collection;
R_TRY(get_collection(fs, 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());
out.emplace_back(collection);
// for (size_t i = 0; i < collection.dirs.size(); i++) {
for (const auto&p : collection.dirs) {
// 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);
R_TRY(get_collections(fs, *new_path, *new_parent_name, out));
}
R_SUCCEED();
}
#endif
auto get_collections(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out) -> Result { auto get_collections(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out) -> Result {
fs::FsNativeSd fs; fs::FsNativeSd fs;
@@ -392,6 +349,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
std::make_pair(Button::DOWN, Action{[this](){ std::make_pair(Button::DOWN, Action{[this](){
if (m_index < (m_entries_current.size() - 1)) { if (m_index < (m_entries_current.size() - 1)) {
SetIndex(m_index + 1); SetIndex(m_index + 1);
App::PlaySoundEffect(SoundEffect_Scroll);
if (m_index - m_index_offset >= 8) { if (m_index - m_index_offset >= 8) {
log_write("moved down\n"); log_write("moved down\n");
m_index_offset++; m_index_offset++;
@@ -401,6 +359,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
std::make_pair(Button::UP, Action{[this](){ std::make_pair(Button::UP, Action{[this](){
if (m_index != 0) { if (m_index != 0) {
SetIndex(m_index - 1); SetIndex(m_index - 1);
App::PlaySoundEffect(SoundEffect_Scroll);
if (m_index < m_index_offset ) { if (m_index < m_index_offset ) {
log_write("moved up\n"); log_write("moved up\n");
m_index_offset--; m_index_offset--;
@@ -571,12 +530,13 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
} }
// can't rename more than 1 file // can't rename more than 1 file
if (m_entries_current.size() && m_selected_count < 2) { if (m_entries_current.size() && !m_selected_count) {
options->Add(std::make_shared<SidebarEntryCallback>("Rename"_i18n, [this](){ options->Add(std::make_shared<SidebarEntryCallback>("Rename"_i18n, [this](){
std::string out; std::string out;
if (R_SUCCEEDED(swkbd::ShowText(out, "Set New File Name", -1, FS_MAX_PATH)) && !out.empty()) { const auto& entry = GetEntry();
const auto& entry = GetEntry(); const auto name = entry.GetName();
const auto src_path = GetNewPathCurrent(); if (R_SUCCEEDED(swkbd::ShowText(out, "Set New File Name", name.c_str())) && !out.empty() && out != name) {
const auto src_path = GetNewPath(entry);
const auto dst_path = GetNewPath(m_path, out); const auto dst_path = GetNewPath(m_path, out);
Result rc; Result rc;
@@ -603,7 +563,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
options->Add(std::make_shared<SidebarEntryCallback>("Create File"_i18n, [this](){ options->Add(std::make_shared<SidebarEntryCallback>("Create File"_i18n, [this](){
std::string out; std::string out;
if (R_SUCCEEDED(swkbd::ShowText(out, "Set File Name", -1, FS_MAX_PATH)) && !out.empty()) { if (R_SUCCEEDED(swkbd::ShowText(out, "Set File Name")) && !out.empty()) {
fs::FsPath full_path; fs::FsPath full_path;
if (out[0] == '/') { if (out[0] == '/') {
full_path = out; full_path = out;
@@ -624,7 +584,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
options->Add(std::make_shared<SidebarEntryCallback>("Create Folder"_i18n, [this](){ options->Add(std::make_shared<SidebarEntryCallback>("Create Folder"_i18n, [this](){
std::string out; std::string out;
if (R_SUCCEEDED(swkbd::ShowText(out, "Set Folder Name", -1, FS_MAX_PATH)) && !out.empty()) { if (R_SUCCEEDED(swkbd::ShowText(out, "Set Folder Name")) && !out.empty()) {
fs::FsPath full_path; fs::FsPath full_path;
if (out[0] == '/') { if (out[0] == '/') {
full_path = out; full_path = out;
@@ -648,18 +608,22 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
} }
if (m_entries_current.size()) { if (m_entries_current.size()) {
if (HasTypeInSelectedEntries(FsDirEntryType_File) && m_selected_count < 2 && !FindFileAssocFor().empty()) { if (HasTypeInSelectedEntries(FsDirEntryType_File) && !m_selected_count && (GetEntry().GetExtension() == "nro" || !FindFileAssocFor().empty())) {
options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){; options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){;
#if 1
App::Push(std::make_shared<OptionBox>( App::Push(std::make_shared<OptionBox>(
"WARNING: Installing forwarders will lead to a ban!"_i18n, "WARNING: Installing forwarders will lead to a ban!"_i18n,
"Back"_i18n, "Install"_i18n, 1, [this](auto op_index){ "Back"_i18n, "Install"_i18n, 0, [this](auto op_index){
if (op_index && *op_index) { if (op_index && *op_index) {
InstallForwarder(); if (GetEntry().GetExtension() == "nro") {
if (R_FAILED(homebrew::Menu::InstallHomebrewFromPath(GetNewPathCurrent()))) {
log_write("failed to create forwarder\n");
}
} else {
InstallForwarder();
}
} }
} }
)); ));
#endif
})); }));
} }
@@ -809,7 +773,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
} }
nvgSave(vg); nvgSave(vg);
nvgScissor(vg, x + text_xoffset+65, y, w-(x+text_xoffset+65+50), h); const auto txt_clip = std::min(GetY() + GetH(), y + h) - y;
nvgScissor(vg, x + text_xoffset+65, y, w-(x+text_xoffset+65+50), txt_clip);
gfx::drawText(vg, x + text_xoffset+65, y + (h / 2.f), 20.f, e.name, NULL, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->elements[text_id].colour); gfx::drawText(vg, x + text_xoffset+65, y + (h / 2.f), 20.f, e.name, NULL, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->elements[text_id].colour);
nvgRestore(vg); nvgRestore(vg);
@@ -910,25 +875,6 @@ void Menu::InstallForwarder() {
title, items, [this, assoc_list](auto op_index){ title, items, [this, assoc_list](auto op_index){
if (op_index) { if (op_index) {
const auto assoc = assoc_list[*op_index]; const auto assoc = assoc_list[*op_index];
#if 1
#if 0
NroEntry nro{};
log_write("parsing nro\n");
if (R_FAILED(nro_parse(assoc.path, nro))) {
log_write("failed nro parse\n");
return;
}
OwoConfig config{};
config.nro_path = nro.path.toString();
// config.args = nro_add_arg_file(GetNewPathCurrent());
config.name = nro.nacp.lang[0].name;// + std::string{" | "} + file_name;
// config.name = file_name;
config.nacp = nro.nacp;
// config.icon = GetRomIcon(pbox, file_name, extension, database, nro);
config.icon = nro.icon;// GetRomIcon(pbox, file_name, extension, database, nro);
App::Install(config);
#else
log_write("pushing it\n"); log_write("pushing it\n");
App::Push(std::make_shared<ProgressBox>("Installing Forwarder", [assoc, this](auto pbox) -> bool { App::Push(std::make_shared<ProgressBox>("Installing Forwarder", [assoc, this](auto pbox) -> bool {
log_write("inside callback\n"); log_write("inside callback\n");
@@ -961,36 +907,6 @@ void Menu::InstallForwarder() {
return R_SUCCEEDED(App::Install(pbox, config)); return R_SUCCEEDED(App::Install(pbox, config));
})); }));
#endif
#else
const auto& assoc = assoc_list[*op_index];
log_write("doing nro parse\n");
NroEntry nro{};
if (R_SUCCEEDED(nro_parse(assoc.path.c_str(), nro))) {
log_write("got nro data\n");
std::string file_name = GetEntry().GetInternalName();
std::string extension = GetEntry().GetInternalExtension();
if (auto pos = file_name.find_last_of('.'); pos != std::string::npos) {
log_write("got filename\n");
file_name = file_name.substr(0, pos);
log_write("got filename2: %s\n\n", file_name.c_str());
}
const auto database = GetRomDatabaseFromPath(m_path);
OwoConfig config{};
config.nro_path = assoc.path;
config.args = nro_add_arg_file(GetNewPathCurrent());
config.name = nro.nacp.lang[0].name + std::string{" | "} + file_name;
// config.name = file_name;
config.nacp = nro.nacp;
config.icon = GetRomIcon(file_name, extension, database, nro);
App::Install(config);
}
#endif
} else { } else {
log_write("pressed B to skip launch...\n"); log_write("pressed B to skip launch...\n");
} }

View File

@@ -17,38 +17,6 @@
namespace sphaira::ui::menu::homebrew { namespace sphaira::ui::menu::homebrew {
namespace { namespace {
constexpr const char* SORT_STR[] = {
"Updated",
"Size",
"Alphabetical",
};
constexpr const char* ORDER_STR[] = {
"Desc",
"Asc",
};
// returns seconds as: hh:mm:ss
auto TimeFormat(u64 sec) -> std::string {
char buf[9];
const auto s = sec % 60;
const auto h = sec / 60 % 60;
const auto d = sec / 60 / 60 % 24;
if (sec < 60) {
if (!sec) {
return "00:00:00";
}
std::snprintf(buf, sizeof(buf), "00:00:%02lu", s);
} else if (sec < 3600) {
std::snprintf(buf, sizeof(buf), "00:%02lu:%02lu", h, s);
} else {
std::snprintf(buf, sizeof(buf), "%02lu:%02lu:%02lu", d, h, s);
}
return std::string{buf};
}
} // namespace } // namespace
@@ -72,11 +40,10 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
if (m_index < (m_entries.size() - 1)) { if (m_index < (m_entries.size() - 1)) {
if (m_index < (m_entries.size() - 3)) { if (m_index < (m_entries.size() - 3)) {
SetIndex(m_index + 3); SetIndex(m_index + 3);
App::PlaySoundEffect(SoundEffect_Scroll);
} else { } else {
SetIndex(m_entries.size() - 1); SetIndex(m_entries.size() - 1);
App::PlaySoundEffect(SoundEffect_Scroll);
} }
App::PlaySoundEffect(SoundEffect_Scroll);
if (m_index - m_start >= 9) { if (m_index - m_start >= 9) {
log_write("moved down\n"); log_write("moved down\n");
m_start += 3; m_start += 3;
@@ -153,7 +120,7 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){ options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){
App::Push(std::make_shared<OptionBox>( App::Push(std::make_shared<OptionBox>(
"WARNING: Installing forwarders will lead to a ban!"_i18n, "WARNING: Installing forwarders will lead to a ban!"_i18n,
"Back"_i18n, "Install"_i18n, 1, [this](auto op_index){ "Back"_i18n, "Install"_i18n, 0, [this](auto op_index){
if (op_index && *op_index) { if (op_index && *op_index) {
InstallHomebrew(); InstallHomebrew();
} }
@@ -263,11 +230,7 @@ void Menu::SetIndex(std::size_t index) {
void Menu::InstallHomebrew() { void Menu::InstallHomebrew() {
const auto& nro = m_entries[m_index]; const auto& nro = m_entries[m_index];
OwoConfig config{}; InstallHomebrew(nro.path, nro.nacp, nro.icon);
config.nro_path = nro.path.toString();
config.nacp = nro.nacp;
config.icon = nro.icon;
App::Install(config);
} }
void Menu::ScanHomebrew() { void Menu::ScanHomebrew() {
@@ -275,22 +238,11 @@ void Menu::ScanHomebrew() {
nro_scan("/switch", m_entries, m_hide_sphaira.Get()); nro_scan("/switch", m_entries, m_hide_sphaira.Get());
log_write("nros found: %zu time_taken: %.2f\n", m_entries.size(), ts.GetSeconds()); log_write("nros found: %zu time_taken: %.2f\n", m_entries.size(), ts.GetSeconds());
// todo: optimise this. maybe create a file per entry
// which would speed up parsing
for (auto& e : m_entries) {
if (ini_hassection(e.path, App::PLAYLOG_PATH)) {
// log_write("has section for: %s\n", e.path);
e.hbini.timestamp = ini_getl(e.path, "timestamp", 0, App::PLAYLOG_PATH);
}
e.image = 0; // images are lazy loaded
}
#if 0
struct IniUser { struct IniUser {
std::vector<NroEntry>& entires; std::vector<NroEntry>& entires;
Hbini* ini; Hbini* ini;
std::string last_section; std::string last_section;
} ini_user { m_entries }; } ini_user{ m_entries };
ini_browse([](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) -> int { ini_browse([](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) -> int {
auto user = static_cast<IniUser*>(UserData); auto user = static_cast<IniUser*>(UserData);
@@ -308,14 +260,16 @@ void Menu::ScanHomebrew() {
} }
if (user->ini) { if (user->ini) {
if (!strcmp(Key, "timestamp")) {
user->ini->timestamp = atoi(Value);
} else if (!strcmp(Key, "launch_count")) {
user->ini->launch_count = atoi(Value);
}
} }
// app-> // log_write("found: %s %s %s\n", Section, Key, Value);
log_write("found: %s %s %s\n", Section, Key, Value);
return 1; return 1;
}, &ini_user, App::PLAYLOG_PATH); }, &ini_user, App::PLAYLOG_PATH);
#endif
this->Sort(); this->Sort();
SetIndex(0); SetIndex(0);
@@ -374,4 +328,19 @@ void Menu::SortAndFindLastFile() {
Sort(); Sort();
} }
Result Menu::InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector<u8>& icon) {
OwoConfig config{};
config.nro_path = path.toString();
config.nacp = nacp;
config.icon = icon;
return App::Install(config);
}
Result Menu::InstallHomebrewFromPath(const fs::FsPath& path) {
NacpStruct nacp;
R_TRY(nro_get_nacp(path, nacp))
const auto icon = nro_get_icon(path);
return InstallHomebrew(path, nacp, icon);
}
} // namespace sphaira::ui::menu::homebrew } // namespace sphaira::ui::menu::homebrew

View File

@@ -1,22 +1,26 @@
#include "ui/menus/main_menu.hpp" #include "ui/menus/main_menu.hpp"
#include "ui/menus/irs_menu.hpp"
#include "ui/menus/themezer.hpp"
#include "ui/sidebar.hpp" #include "ui/sidebar.hpp"
#include "ui/popup_list.hpp" #include "ui/popup_list.hpp"
#include "ui/option_box.hpp" #include "ui/option_box.hpp"
#include "ui/progress_box.hpp"
#include "ui/error_box.hpp"
#include "app.hpp" #include "app.hpp"
#include "log.hpp" #include "log.hpp"
#include "download.hpp" #include "download.hpp"
#include "defines.hpp" #include "defines.hpp"
#include "ui/menus/irs_menu.hpp"
#include "ui/menus/themezer.hpp"
#include "web.hpp" #include "web.hpp"
#include "i18n.hpp" #include "i18n.hpp"
#include <cstring> #include <cstring>
#include <minizip/unzip.h>
namespace sphaira::ui::menu::main { namespace sphaira::ui::menu::main {
namespace { namespace {
#if 0
bool parseSearch(const char *parse_string, const char *filter, char* new_string) { bool parseSearch(const char *parse_string, const char *filter, char* new_string) {
char c; char c;
u32 offset = 0; u32 offset = 0;
@@ -40,37 +44,159 @@ bool parseSearch(const char *parse_string, const char *filter, char* new_string)
return false; return false;
} }
#endif
auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string version) -> bool {
static fs::FsPath zip_out{"/switch/sphaira/cache/update.zip"};
constexpr auto chunk_size = 1024 * 512; // 512KiB
fs::FsNativeSd fs;
R_TRY_RESULT(fs.GetFsOpenResult(), false);
// 1. download the zip
if (!pbox->ShouldExit()) {
pbox->NewTransfer("Downloading "_i18n + version);
log_write("starting download: %s\n", url.c_str());
DownloadClearCache(url);
if (!DownloadFile(url, zip_out, "", [pbox](u32 dltotal, u32 dlnow, u32 ultotal, u32 ulnow){
if (pbox->ShouldExit()) {
return false;
}
pbox->UpdateTransfer(dlnow, dltotal);
return true;
})) {
log_write("error with download\n");
// push popup error box
return false;
}
}
ON_SCOPE_EXIT(fs.DeleteFile(zip_out));
// 2. extract the zip
if (!pbox->ShouldExit()) {
auto zfile = unzOpen64(zip_out);
if (!zfile) {
log_write("failed to open zip: %s\n", zip_out);
return false;
}
ON_SCOPE_EXIT(unzClose(zfile));
unz_global_info64 pglobal_info;
if (UNZ_OK != unzGetGlobalInfo64(zfile, &pglobal_info)) {
return false;
}
for (int i = 0; i < pglobal_info.number_entry; i++) {
if (i > 0) {
if (UNZ_OK != unzGoToNextFile(zfile)) {
log_write("failed to unzGoToNextFile\n");
return false;
}
}
if (UNZ_OK != unzOpenCurrentFile(zfile)) {
log_write("failed to open current file\n");
return false;
}
ON_SCOPE_EXIT(unzCloseCurrentFile(zfile));
unz_file_info64 info;
fs::FsPath file_path;
if (UNZ_OK != unzGetCurrentFileInfo64(zfile, &info, file_path, sizeof(file_path), 0, 0, 0, 0)) {
log_write("failed to get current info\n");
return false;
}
if (file_path[0] != '/') {
file_path = fs::AppendPath("/", file_path);
}
Result rc;
if (file_path[strlen(file_path) -1] == '/') {
if (R_FAILED(rc = fs.CreateDirectoryRecursively(file_path)) && rc != FsError_ResultPathAlreadyExists) {
log_write("failed to create folder: %s 0x%04X\n", file_path, rc);
return false;
}
} else {
Result rc;
if (R_FAILED(rc = fs.CreateFile(file_path, info.uncompressed_size, 0)) && rc != FsError_ResultPathAlreadyExists) {
log_write("failed to create file: %s 0x%04X\n", file_path, rc);
return false;
}
FsFile f;
if (R_FAILED(rc = fs.OpenFile(file_path, FsOpenMode_Write, &f))) {
log_write("failed to open file: %s 0x%04X\n", file_path, rc);
return false;
}
ON_SCOPE_EXIT(fsFileClose(&f));
if (R_FAILED(rc = fsFileSetSize(&f, info.uncompressed_size))) {
log_write("failed to set file size: %s 0x%04X\n", file_path, rc);
return false;
}
std::vector<char> buf(chunk_size);
u64 offset{};
while (offset < info.uncompressed_size) {
if (pbox->ShouldExit()) {
return false;
}
const auto bytes_read = unzReadCurrentFile(zfile, buf.data(), buf.size());
if (bytes_read <= 0) {
// log_write("failed to read zip file: %s\n", inzip.c_str());
return false;
}
if (R_FAILED(rc = fsFileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None))) {
log_write("failed to write file: %s 0x%04X\n", file_path, rc);
return false;
}
pbox->UpdateTransfer(offset, info.uncompressed_size);
offset += bytes_read;
}
}
}
}
log_write("finished update :)\n");
return true;
}
} // namespace } // namespace
MainMenu::MainMenu() { MainMenu::MainMenu() {
#if 0 // todo: replace below with yyjson, this is old code from ams updater, lol.
DownloadMemoryAsync("https://api.github.com/repos/ITotalJustice/sys-patch/releases/latest", [this](std::vector<u8>& data, bool success){ DownloadMemoryAsync("https://api.github.com/repos/ITotalJustice/sphaira/releases/latest", "", [this](std::vector<u8>& data, bool success){
data.push_back('\0'); data.push_back('\0');
auto raw_str = (const char*)data.data(); auto raw_str = (const char*)data.data();
char out_str[0x301]; char out_str[0x301];
if (parseSearch(raw_str, "tag_name\":\"", out_str)) { if (parseSearch(raw_str, "tag_name\":\"", out_str)) {
m_update_version = out_str; m_update_version = out_str;
if (strcasecmp("v1.5.0", m_update_version.c_str())) { if (std::strcmp(APP_VERSION, m_update_version.c_str()) < 0) {
m_update_avaliable = true; m_update_state = UpdateState::Update;
App::Notify("Update avaliable: "_i18n + m_update_version);
} else {
m_update_state = UpdateState::None;
} }
log_write("FOUND IT : %s\n", out_str); log_write("found update tag : %s vs %s\n", APP_VERSION, out_str);
} }
if (parseSearch(raw_str, "browser_download_url\":\"", out_str)) { if (parseSearch(raw_str, "browser_download_url\":\"", out_str)) {
m_update_url = out_str; m_update_url = out_str;
log_write("FOUND IT : %s\n", out_str); log_write("found download url : %s\n", out_str);
} }
if (parseSearch(raw_str, "body\":\"", out_str)) { if (parseSearch(raw_str, "body\":\"", out_str)) {
m_update_description = out_str; m_update_description = out_str;
// m_update_description.replace("\r\n\r\n", "\n"); // m_update_description.replace("\r\n\r\n", "\n");
log_write("FOUND IT : %s\n", out_str); log_write("found description : %s\n", out_str);
} }
}); });
#endif
AddOnLPress(); AddOnLPress();
AddOnRPress(); AddOnRPress();
@@ -128,22 +254,26 @@ MainMenu::MainMenu() {
options->Add(std::make_shared<SidebarEntryBool>("Nxlink"_i18n, App::GetNxlinkEnable(), [this](bool& enable){ options->Add(std::make_shared<SidebarEntryBool>("Nxlink"_i18n, App::GetNxlinkEnable(), [this](bool& enable){
App::SetNxlinkEnable(enable); App::SetNxlinkEnable(enable);
}, "Enabled"_i18n, "Disabled"_i18n)); }, "Enabled"_i18n, "Disabled"_i18n));
options->Add(std::make_shared<SidebarEntryCallback>("Check for update"_i18n, [this](){
App::Notify("Not Implemented"_i18n); if (m_update_state == UpdateState::Update) {
})); options->Add(std::make_shared<SidebarEntryCallback>("Download update: "_i18n + m_update_version, [this](){
App::Push(std::make_shared<ProgressBox>("Downloading "_i18n + m_update_version, [this](auto pbox){
return InstallUpdate(pbox, m_update_url, m_update_version);
}, [this](bool success){
if (success) {
m_update_state = UpdateState::None;
} else {
App::Push(std::make_shared<ui::ErrorBox>(MAKERESULT(351, 1), "Failed to download update"));
}
}, 2));
}));
}
})); }));
options->Add(std::make_shared<SidebarEntryArray>("Language"_i18n, language_items, [this, language_items](std::size_t& index_out){ options->Add(std::make_shared<SidebarEntryArray>("Language"_i18n, language_items, [this, language_items](std::size_t& index_out){
App::SetLanguage(index_out); App::SetLanguage(index_out);
}, (std::size_t)App::GetLanguage())); }, (std::size_t)App::GetLanguage()));
if (m_update_avaliable) {
std::string str = "Update avaliable: "_i18n + m_update_version;
options->Add(std::make_shared<SidebarEntryCallback>(str, [this](){
App::Notify("Not Implemented"_i18n);
}));
}
options->Add(std::make_shared<SidebarEntryBool>("Logging"_i18n, App::GetLogEnable(), [this](bool& enable){ options->Add(std::make_shared<SidebarEntryBool>("Logging"_i18n, App::GetLogEnable(), [this](bool& enable){
App::SetLogEnable(enable); App::SetLogEnable(enable);
}, "Enabled"_i18n, "Disabled"_i18n)); }, "Enabled"_i18n, "Disabled"_i18n));

View File

@@ -32,6 +32,13 @@ void MenuBase::Draw(NVGcontext* vg, Theme* theme) {
u32 strength{}; u32 strength{};
u32 ip{}; u32 ip{};
const auto _time = time(NULL);
struct tm tm{};
const auto gmt = gmtime(&_time);
if (gmt) {
tm = *gmt;
}
// todo: app thread poll every 1s and this query the result // todo: app thread poll every 1s and this query the result
psmGetBatteryChargePercentage(&battery_percetange); psmGetBatteryChargePercentage(&battery_percetange);
psmGetChargerType(&charger_type); psmGetChargerType(&charger_type);
@@ -54,7 +61,8 @@ void MenuBase::Draw(NVGcontext* vg, Theme* theme) {
start_x -= spacing; start_x -= spacing;
// draw("version %s", APP_VERSION); // draw("version %s", APP_VERSION);
draw("%u%%", battery_percetange); draw("%u\uFE6A", battery_percetange);
draw("%02u:%02u:%02u", tm.tm_hour, tm.tm_min, tm.tm_sec);
if (ip) { if (ip) {
draw("%u.%u.%u.%u", ip&0xFF, (ip>>8)&0xFF, (ip>>16)&0xFF, (ip>>24)&0xFF); draw("%u.%u.%u.%u", ip&0xFF, (ip>>8)&0xFF, (ip>>16)&0xFF, (ip>>24)&0xFF);
} else { } else {

View File

@@ -508,7 +508,7 @@ Menu::Menu() : MenuBase{"Themezer"_i18n} {
options->Add(std::make_shared<SidebarEntryCallback>("Page"_i18n, [this](){ options->Add(std::make_shared<SidebarEntryCallback>("Page"_i18n, [this](){
s64 out; s64 out;
if (R_SUCCEEDED(swkbd::ShowNumPad(out, "Enter Page Number", -1, 3))) { if (R_SUCCEEDED(swkbd::ShowNumPad(out, "Enter Page Number", nullptr, -1, 3))) {
if (out < m_page_index_max) { if (out < m_page_index_max) {
m_page_index = out; m_page_index = out;
PackListDownload(); PackListDownload();

View File

@@ -457,7 +457,7 @@ void drawButton(NVGcontext* vg, float x, float y, float size, Button button) {
drawText(vg, x, y, size, getButton(button), nullptr, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, getColour(Colour::WHITE)); drawText(vg, x, y, size, getButton(button), nullptr, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, getColour(Colour::WHITE));
} }
void drawButtons(NVGcontext* vg, const Widget::Actions& actions, const NVGcolor& c, float start_x) { void drawButtons(NVGcontext* vg, const Widget::Actions& _actions, const NVGcolor& c, float start_x) {
nvgBeginPath(vg); nvgBeginPath(vg);
nvgFontSize(vg, 24.f); nvgFontSize(vg, 24.f);
nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP); nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP);
@@ -467,6 +467,22 @@ void drawButtons(NVGcontext* vg, const Widget::Actions& actions, const NVGcolor&
const float y = 675.f; const float y = 675.f;
float bounds[4]{}; float bounds[4]{};
// swaps L/R position, idc how shit this is, it's called once per frame.
std::vector<std::pair<Button, Action>> actions;
actions.reserve(_actions.size());
for (const auto a: _actions) {
// swap
if (a.first == Button::R && actions.size() && actions.back().first == Button::L) {
const auto s = actions.back();
actions.back() = a;
actions.emplace_back(s);
} else {
actions.emplace_back(a);
}
}
for (const auto& [button, action] : actions) { for (const auto& [button, action] : actions) {
if (action.IsHidden() || action.m_hint.empty()) { if (action.IsHidden() || action.m_hint.empty()) {
continue; continue;

View File

@@ -123,6 +123,7 @@ auto OptionBox::Setup(std::size_t index) -> void {
m_entries[m_index].Selected(false); m_entries[m_index].Selected(false);
m_index--; m_index--;
m_entries[m_index].Selected(true); m_entries[m_index].Selected(true);
App::PlaySoundEffect(SoundEffect_Focus);
} }
}}), }}),
std::make_pair(Button::RIGHT, Action{[this](){ std::make_pair(Button::RIGHT, Action{[this](){
@@ -130,6 +131,7 @@ auto OptionBox::Setup(std::size_t index) -> void {
m_entries[m_index].Selected(false); m_entries[m_index].Selected(false);
m_index++; m_index++;
m_entries[m_index].Selected(true); m_entries[m_index].Selected(true);
App::PlaySoundEffect(SoundEffect_Focus);
} }
}}), }}),
std::make_pair(Button::A, Action{[this](){ std::make_pair(Button::A, Action{[this](){

View File

@@ -90,6 +90,8 @@ auto PopupList::Update(Controller* controller, TouchInfo* touch) -> void {
return; return;
} }
const auto old_index = m_index;
if (controller->GotDown(Button::DOWN) && m_index < (m_items.size() - 1)) { if (controller->GotDown(Button::DOWN) && m_index < (m_items.size() - 1)) {
m_index++; m_index++;
m_selected_y += m_block.h; m_selected_y += m_block.h;
@@ -98,7 +100,10 @@ auto PopupList::Update(Controller* controller, TouchInfo* touch) -> void {
m_selected_y -= m_block.h; m_selected_y -= m_block.h;
} }
OnLayoutChange(); if (old_index != m_index) {
App::PlaySoundEffect(SoundEffect_Scroll);
OnLayoutChange();
}
} }
auto PopupList::OnLayoutChange() -> void { auto PopupList::OnLayoutChange() -> void {

View File

@@ -27,6 +27,7 @@ ScrollableText::ScrollableText(const std::string& text, float x, float y, float
} }
m_y_off -= m_step; m_y_off -= m_step;
m_index++; m_index++;
App::PlaySoundEffect(SoundEffect_Scroll);
}}), }}),
std::make_pair(Button::LS_UP, Action{[this](){ std::make_pair(Button::LS_UP, Action{[this](){
if (m_y_off == m_y_off_base) { if (m_y_off == m_y_off_base) {
@@ -34,6 +35,7 @@ ScrollableText::ScrollableText(const std::string& text, float x, float y, float
} }
m_y_off += m_step; m_y_off += m_step;
m_index--; m_index--;
App::PlaySoundEffect(SoundEffect_Scroll);
}}) }})
); );

View File

@@ -234,6 +234,7 @@ auto Sidebar::Update(Controller* controller, TouchInfo* touch) -> void {
// if we moved // if we moved
if (m_index != old_index) { if (m_index != old_index) {
App::PlaySoundEffect(SoundEffect_Scroll);
m_items[old_index]->OnFocusLost(); m_items[old_index]->OnFocusLost();
m_items[m_index]->OnFocusGained(); m_items[m_index]->OnFocusGained();

View File

@@ -7,8 +7,10 @@ namespace sphaira::ui {
void Widget::Update(Controller* controller, TouchInfo* touch) { void Widget::Update(Controller* controller, TouchInfo* touch) {
for (const auto& [button, action] : m_actions) { for (const auto& [button, action] : m_actions) {
if ((action.m_type & ActionType::DOWN) && controller->GotDown(button)) { if ((action.m_type & ActionType::DOWN) && controller->GotDown(button)) {
if (static_cast<u64>(button) & static_cast<u64>(Button::ANY_BUTTON)) {
App::PlaySoundEffect(SoundEffect_Focus);
}
action.Invoke(true); action.Invoke(true);
App::PlaySoundEffect(SoundEffect_Focus);
} }
else if ((action.m_type & ActionType::UP) && controller->GotUp(button)) { else if ((action.m_type & ActionType::UP) && controller->GotUp(button)) {
action.Invoke(false); action.Invoke(false);